@canopy-iiif/app 0.7.16 → 0.7.18
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/lib/AGENTS.md +2 -0
- package/lib/build/dev.js +205 -5
- package/lib/build/iiif.js +1 -8
- package/lib/build/mdx.js +5 -2
- package/lib/build/pages.js +1 -5
- package/lib/build/search.js +2 -5
- package/lib/common.js +16 -8
- package/lib/head.js +21 -0
- package/lib/index.js +5 -1
- package/lib/orchestrator.js +203 -0
- package/lib/search/command-runtime.js +5 -4
- package/lib/search/search-app.jsx +548 -111
- package/lib/search/search.js +8 -2
- package/package.json +10 -3
- package/types/orchestrator.d.ts +18 -0
- package/ui/dist/index.mjs +280 -47
- package/ui/dist/index.mjs.map +4 -4
- package/ui/dist/server.mjs +139 -43
- package/ui/dist/server.mjs.map +4 -4
- package/ui/styles/base/_common.scss +8 -0
- package/ui/styles/base/index.scss +1 -0
- package/ui/styles/components/_command.scss +85 -1
- package/ui/styles/components/_header.scss +0 -0
- package/ui/styles/components/_hero.scss +21 -0
- package/ui/styles/components/index.scss +2 -3
- package/ui/styles/index.css +105 -1
- package/ui/styles/index.scss +3 -2
package/lib/search/search.js
CHANGED
|
@@ -150,7 +150,6 @@ async function buildSearchPage() {
|
|
|
150
150
|
head = rendered && rendered.head ? rendered.head : '';
|
|
151
151
|
if (!body) throw new Error('Search: content/search/_layout.mdx produced empty output');
|
|
152
152
|
const importMap = '';
|
|
153
|
-
const cssRel = path.relative(path.dirname(outPath), path.join(OUT_DIR, 'styles', 'styles.css')).split(path.sep).join('/');
|
|
154
153
|
const jsAbs = path.join(OUT_DIR, 'scripts', 'search.js');
|
|
155
154
|
let jsRel = path.relative(path.dirname(outPath), jsAbs).split(path.sep).join('/');
|
|
156
155
|
let v = '';
|
|
@@ -166,7 +165,14 @@ async function buildSearchPage() {
|
|
|
166
165
|
return rel;
|
|
167
166
|
}
|
|
168
167
|
const vendorTags = `<script src="${verRel(vendorReactAbs)}"></script><script src="${verRel(vendorFlexAbs)}"></script><script src="${verRel(vendorCommandAbs)}"></script>`;
|
|
169
|
-
let
|
|
168
|
+
let headExtra = vendorTags + head + importMap;
|
|
169
|
+
try {
|
|
170
|
+
const { BASE_PATH } = require('../common');
|
|
171
|
+
if (BASE_PATH) {
|
|
172
|
+
headExtra = `<script>window.CANOPY_BASE_PATH=${JSON.stringify(BASE_PATH)}</script>` + headExtra;
|
|
173
|
+
}
|
|
174
|
+
} catch (_) {}
|
|
175
|
+
let html = htmlShell({ title: 'Search', body, cssHref: null, scriptHref: jsRel, headExtra });
|
|
170
176
|
try { html = require('./common').applyBaseToHtml(html); } catch (_) {}
|
|
171
177
|
await fsp.writeFile(outPath, html, 'utf8');
|
|
172
178
|
console.log('Search: Built', path.relative(process.cwd(), outPath));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canopy-iiif/app",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.18",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Mat Jordan <mat@northwestern.edu>",
|
|
@@ -14,14 +14,21 @@
|
|
|
14
14
|
"./ui/styles/index.css": "./ui/styles/index.css",
|
|
15
15
|
"./ui/canopy-iiif-plugin": "./ui/tailwind-canopy-iiif-plugin.js",
|
|
16
16
|
"./ui/canopy-iiif-preset": "./ui/tailwind-canopy-iiif-preset.js",
|
|
17
|
-
"./lib/components/*": "./lib/components/*"
|
|
17
|
+
"./lib/components/*": "./lib/components/*",
|
|
18
|
+
"./head": "./lib/head.js",
|
|
19
|
+
"./orchestrator": {
|
|
20
|
+
"types": "./types/orchestrator.d.ts",
|
|
21
|
+
"require": "./lib/orchestrator.js",
|
|
22
|
+
"default": "./lib/orchestrator.js"
|
|
23
|
+
}
|
|
18
24
|
},
|
|
19
25
|
"files": [
|
|
20
26
|
"lib/**",
|
|
21
27
|
"ui/dist/**",
|
|
22
28
|
"ui/styles/**",
|
|
23
29
|
"ui/tailwind-canopy-iiif-plugin.js",
|
|
24
|
-
"ui/tailwind-canopy-iiif-preset.js"
|
|
30
|
+
"ui/tailwind-canopy-iiif-preset.js",
|
|
31
|
+
"types/**"
|
|
25
32
|
],
|
|
26
33
|
"publishConfig": {
|
|
27
34
|
"access": "public"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ChildProcess } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
export type Mode = 'build' | 'dev';
|
|
4
|
+
|
|
5
|
+
export interface LibraryApi {
|
|
6
|
+
build?: () => Promise<void> | void;
|
|
7
|
+
dev?: () => Promise<void> | void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type NullableChildProcess = ChildProcess | null;
|
|
11
|
+
|
|
12
|
+
export interface OrchestratorOptions {
|
|
13
|
+
argv?: string[];
|
|
14
|
+
env?: NodeJS.ProcessEnv;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export declare function orchestrate(options?: OrchestratorOptions): Promise<void>;
|
|
18
|
+
export declare function verifyBuildOutput(outDir?: string): void;
|
package/ui/dist/index.mjs
CHANGED
|
@@ -377,40 +377,233 @@ function SearchResults({
|
|
|
377
377
|
|
|
378
378
|
// ui/src/search/SearchTabs.jsx
|
|
379
379
|
import React11 from "react";
|
|
380
|
-
function SearchTabs({
|
|
380
|
+
function SearchTabs({
|
|
381
|
+
type = "all",
|
|
382
|
+
onTypeChange,
|
|
383
|
+
types = [],
|
|
384
|
+
counts = {},
|
|
385
|
+
onOpenFilters,
|
|
386
|
+
activeFilterCount = 0,
|
|
387
|
+
filtersLabel = "Filters",
|
|
388
|
+
filtersOpen = false
|
|
389
|
+
}) {
|
|
381
390
|
const orderedTypes = Array.isArray(types) ? types : [];
|
|
382
391
|
const toLabel = (t) => t && t.length ? t.charAt(0).toUpperCase() + t.slice(1) : "";
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
392
|
+
const hasFilters = typeof onOpenFilters === "function";
|
|
393
|
+
const filterBadge = activeFilterCount > 0 ? ` (${activeFilterCount})` : "";
|
|
394
|
+
return /* @__PURE__ */ React11.createElement("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b border-slate-200 pb-1" }, /* @__PURE__ */ React11.createElement(
|
|
395
|
+
"div",
|
|
396
|
+
{
|
|
397
|
+
role: "tablist",
|
|
398
|
+
"aria-label": "Search types",
|
|
399
|
+
className: "flex items-center gap-2"
|
|
400
|
+
},
|
|
401
|
+
orderedTypes.map((t) => {
|
|
402
|
+
const active = String(type).toLowerCase() === String(t).toLowerCase();
|
|
403
|
+
const cRaw = counts && Object.prototype.hasOwnProperty.call(counts, t) ? counts[t] : void 0;
|
|
404
|
+
const c = Number.isFinite(Number(cRaw)) ? Number(cRaw) : 0;
|
|
405
|
+
return /* @__PURE__ */ React11.createElement(
|
|
406
|
+
"button",
|
|
407
|
+
{
|
|
408
|
+
key: t,
|
|
409
|
+
role: "tab",
|
|
410
|
+
"aria-selected": active,
|
|
411
|
+
type: "button",
|
|
412
|
+
onClick: () => onTypeChange && onTypeChange(t),
|
|
413
|
+
className: "px-3 py-2 text-sm rounded-t-md border-b-2 -mb-px transition-colors " + (active ? "border-brand-600 text-brand-700" : "border-transparent text-slate-600 hover:text-slate-900 hover:border-slate-300")
|
|
414
|
+
},
|
|
415
|
+
toLabel(t),
|
|
416
|
+
" (",
|
|
417
|
+
c,
|
|
418
|
+
")"
|
|
419
|
+
);
|
|
420
|
+
})
|
|
421
|
+
), hasFilters ? /* @__PURE__ */ React11.createElement(
|
|
422
|
+
"button",
|
|
423
|
+
{
|
|
424
|
+
type: "button",
|
|
425
|
+
onClick: () => onOpenFilters && onOpenFilters(),
|
|
426
|
+
"aria-expanded": filtersOpen ? "true" : "false",
|
|
427
|
+
className: "inline-flex items-center gap-2 rounded-md border border-slate-200 bg-white px-3 py-1.5 text-sm font-medium text-slate-700 shadow-sm transition hover:border-brand-200 hover:bg-brand-50 hover:text-brand-700"
|
|
428
|
+
},
|
|
429
|
+
/* @__PURE__ */ React11.createElement("span", null, filtersLabel, filterBadge)
|
|
430
|
+
) : null);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ui/src/search/SearchFiltersDialog.jsx
|
|
434
|
+
import React12 from "react";
|
|
435
|
+
function toArray(input) {
|
|
436
|
+
if (!input) return [];
|
|
437
|
+
if (Array.isArray(input)) return input;
|
|
438
|
+
return [input];
|
|
439
|
+
}
|
|
440
|
+
function normalizeSelected(selected = {}) {
|
|
441
|
+
const map = /* @__PURE__ */ new Map();
|
|
442
|
+
if (selected && typeof selected === "object") {
|
|
443
|
+
Object.keys(selected).forEach((key) => {
|
|
444
|
+
const vals = new Set(toArray(selected[key]).map((v) => String(v)));
|
|
445
|
+
if (vals.size) map.set(String(key), vals);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
return map;
|
|
449
|
+
}
|
|
450
|
+
function facetMatches(values = [], query) {
|
|
451
|
+
const q = String(query || "").trim().toLowerCase();
|
|
452
|
+
if (!q) return values;
|
|
453
|
+
const starts = [];
|
|
454
|
+
const contains = [];
|
|
455
|
+
values.forEach((entry) => {
|
|
456
|
+
if (!entry || !entry.value) return;
|
|
457
|
+
const value = String(entry.value);
|
|
458
|
+
const slug = String(entry.slug || entry.value || "");
|
|
459
|
+
const match = value.toLowerCase();
|
|
460
|
+
if (match.startsWith(q))
|
|
461
|
+
starts.push({ value, slug, doc_count: entry.doc_count });
|
|
462
|
+
else if (match.includes(q))
|
|
463
|
+
contains.push({ value, slug, doc_count: entry.doc_count });
|
|
464
|
+
});
|
|
465
|
+
return [...starts, ...contains].slice(0, 10);
|
|
466
|
+
}
|
|
467
|
+
function FacetSection({ facet, selected, onToggle }) {
|
|
468
|
+
if (!facet || !facet.label || !Array.isArray(facet.values)) return null;
|
|
469
|
+
const { label, slug, values } = facet;
|
|
470
|
+
const selectedValues = selected.get(String(slug)) || /* @__PURE__ */ new Set();
|
|
471
|
+
const checkboxId = (valueSlug) => `filter-${slug}-${valueSlug}`;
|
|
472
|
+
const hasSelection = selectedValues.size > 0;
|
|
473
|
+
const [quickQuery, setQuickQuery] = React12.useState("");
|
|
474
|
+
const hasQuery = quickQuery.trim().length > 0;
|
|
475
|
+
const filteredValues = React12.useMemo(
|
|
476
|
+
() => facetMatches(values, quickQuery),
|
|
477
|
+
[values, quickQuery]
|
|
478
|
+
);
|
|
479
|
+
return /* @__PURE__ */ React12.createElement(
|
|
480
|
+
"details",
|
|
481
|
+
{
|
|
482
|
+
className: "rounded-lg border border-slate-200 bg-slate-50",
|
|
483
|
+
open: hasSelection
|
|
484
|
+
},
|
|
485
|
+
/* @__PURE__ */ React12.createElement("summary", { className: "flex cursor-pointer items-center justify-between gap-3 px-4 py-3 text-sm font-medium text-slate-900" }, /* @__PURE__ */ React12.createElement("span", null, label), /* @__PURE__ */ React12.createElement("span", { className: "text-xs font-normal text-slate-500" }, values.length)),
|
|
486
|
+
/* @__PURE__ */ React12.createElement("div", { className: "max-h-60 overflow-y-auto border-t border-slate-200 bg-white px-4 py-3 text-sm text-slate-700" }, /* @__PURE__ */ React12.createElement("div", { className: "mb-3 flex items-center gap-2" }, /* @__PURE__ */ React12.createElement(
|
|
487
|
+
"input",
|
|
488
|
+
{
|
|
489
|
+
type: "search",
|
|
490
|
+
value: quickQuery,
|
|
491
|
+
onChange: (event) => setQuickQuery(event.target.value),
|
|
492
|
+
placeholder: "Search values",
|
|
493
|
+
className: "flex-1 rounded-md border border-slate-300 px-2 py-1 text-sm text-slate-700 focus:border-brand focus:outline-none focus:ring-1 focus:ring-brand",
|
|
494
|
+
"aria-label": `Filter ${label} values`
|
|
495
|
+
}
|
|
496
|
+
), quickQuery ? /* @__PURE__ */ React12.createElement(
|
|
388
497
|
"button",
|
|
389
498
|
{
|
|
390
|
-
key: t,
|
|
391
|
-
role: "tab",
|
|
392
|
-
"aria-selected": active,
|
|
393
499
|
type: "button",
|
|
394
|
-
onClick: () =>
|
|
395
|
-
className: "
|
|
500
|
+
onClick: () => setQuickQuery(""),
|
|
501
|
+
className: "rounded-md border border-slate-200 px-2 py-1 text-xs text-slate-600 transition hover:bg-slate-100"
|
|
396
502
|
},
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
503
|
+
"Clear"
|
|
504
|
+
) : null), hasQuery && !filteredValues.length ? /* @__PURE__ */ React12.createElement("p", { className: "mb-3 text-xs text-slate-400" }, "No matches found.") : null, /* @__PURE__ */ React12.createElement("ul", { className: "space-y-2", style: { padding: 0 } }, filteredValues.map((entry) => {
|
|
505
|
+
const valueSlug = String(entry.slug || entry.value || "");
|
|
506
|
+
const isChecked = selectedValues.has(valueSlug);
|
|
507
|
+
const inputId = checkboxId(valueSlug);
|
|
508
|
+
return /* @__PURE__ */ React12.createElement("li", { key: valueSlug, className: "flex items-start gap-2" }, /* @__PURE__ */ React12.createElement(
|
|
509
|
+
"input",
|
|
510
|
+
{
|
|
511
|
+
id: inputId,
|
|
512
|
+
type: "checkbox",
|
|
513
|
+
className: "mt-1 h-4 w-4 rounded border-slate-300 text-brand focus:ring-brand",
|
|
514
|
+
checked: isChecked,
|
|
515
|
+
onChange: (event) => {
|
|
516
|
+
const nextChecked = !!event.target.checked;
|
|
517
|
+
if (onToggle) onToggle(slug, valueSlug, nextChecked);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
), /* @__PURE__ */ React12.createElement(
|
|
521
|
+
"label",
|
|
522
|
+
{
|
|
523
|
+
htmlFor: inputId,
|
|
524
|
+
className: "flex flex-1 flex-col gap-0.5"
|
|
525
|
+
},
|
|
526
|
+
/* @__PURE__ */ React12.createElement("span", null, entry.value, " ", Number.isFinite(entry.doc_count) ? /* @__PURE__ */ React12.createElement("span", { className: "text-xs text-slate-500" }, "(", entry.doc_count, ")") : null)
|
|
527
|
+
));
|
|
528
|
+
}), !filteredValues.length && !hasQuery ? /* @__PURE__ */ React12.createElement("li", { className: "text-xs text-slate-400" }, "No values available.") : null))
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
function SearchFiltersDialog(props = {}) {
|
|
532
|
+
const {
|
|
533
|
+
open = false,
|
|
534
|
+
onOpenChange,
|
|
535
|
+
facets = [],
|
|
536
|
+
selected = {},
|
|
537
|
+
onToggle,
|
|
538
|
+
onClear,
|
|
539
|
+
title = "Filters",
|
|
540
|
+
subtitle = "Refine results by metadata"
|
|
541
|
+
} = props;
|
|
542
|
+
const selectedMap = normalizeSelected(selected);
|
|
543
|
+
const activeCount = Array.from(selectedMap.values()).reduce(
|
|
544
|
+
(total, set) => total + set.size,
|
|
545
|
+
0
|
|
546
|
+
);
|
|
547
|
+
if (!open) return null;
|
|
548
|
+
return /* @__PURE__ */ React12.createElement(
|
|
549
|
+
"div",
|
|
550
|
+
{
|
|
551
|
+
role: "dialog",
|
|
552
|
+
"aria-modal": "true",
|
|
553
|
+
className: "fixed inset-0 z-50 flex items-start justify-center bg-slate-900/50 px-4 py-8",
|
|
554
|
+
onClick: (event) => {
|
|
555
|
+
if (event.target === event.currentTarget && onOpenChange)
|
|
556
|
+
onOpenChange(false);
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
/* @__PURE__ */ React12.createElement("div", { className: "w-full max-w-3xl overflow-hidden rounded-xl bg-white shadow-2xl" }, /* @__PURE__ */ React12.createElement("header", { className: "flex items-start justify-between gap-4 border-b border-slate-200 px-6 py-4" }, /* @__PURE__ */ React12.createElement("div", null, /* @__PURE__ */ React12.createElement("h2", { className: "text-lg font-semibold text-slate-900" }, title), /* @__PURE__ */ React12.createElement("p", { className: "text-sm text-slate-500" }, subtitle)), /* @__PURE__ */ React12.createElement(
|
|
560
|
+
"button",
|
|
561
|
+
{
|
|
562
|
+
type: "button",
|
|
563
|
+
onClick: () => onOpenChange && onOpenChange(false),
|
|
564
|
+
className: "rounded-md border border-transparent px-2 py-1 text-sm text-slate-600 transition hover:bg-slate-100 hover:text-slate-900"
|
|
565
|
+
},
|
|
566
|
+
"Close"
|
|
567
|
+
)), /* @__PURE__ */ React12.createElement("div", { className: "grid gap-4 px-6 py-6" }, Array.isArray(facets) && facets.length ? /* @__PURE__ */ React12.createElement("div", { className: "space-y-3" }, facets.map((facet) => /* @__PURE__ */ React12.createElement(
|
|
568
|
+
FacetSection,
|
|
569
|
+
{
|
|
570
|
+
key: facet.slug || facet.label,
|
|
571
|
+
facet,
|
|
572
|
+
selected: selectedMap,
|
|
573
|
+
onToggle
|
|
574
|
+
}
|
|
575
|
+
))) : /* @__PURE__ */ React12.createElement("p", { className: "text-sm text-slate-500" }, "No filters are available for this collection.")), /* @__PURE__ */ React12.createElement("footer", { className: "flex items-center justify-between gap-4 border-t border-slate-200 px-6 py-4" }, /* @__PURE__ */ React12.createElement("div", { className: "text-sm text-slate-500" }, activeCount ? `${activeCount} filter${activeCount === 1 ? "" : "s"} applied` : "No filters applied"), /* @__PURE__ */ React12.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React12.createElement(
|
|
576
|
+
"button",
|
|
577
|
+
{
|
|
578
|
+
type: "button",
|
|
579
|
+
onClick: () => {
|
|
580
|
+
if (onClear) onClear();
|
|
581
|
+
},
|
|
582
|
+
disabled: !activeCount,
|
|
583
|
+
className: "rounded-md border border-slate-200 px-3 py-1.5 text-sm text-slate-600 transition disabled:cursor-not-allowed disabled:text-slate-400 hover:bg-slate-100"
|
|
584
|
+
},
|
|
585
|
+
"Clear all"
|
|
586
|
+
), /* @__PURE__ */ React12.createElement(
|
|
587
|
+
"button",
|
|
588
|
+
{
|
|
589
|
+
type: "button",
|
|
590
|
+
onClick: () => onOpenChange && onOpenChange(false),
|
|
591
|
+
className: "rounded-md bg-brand px-4 py-1.5 text-sm font-semibold text-white shadow-sm transition hover:bg-brand-700"
|
|
592
|
+
},
|
|
593
|
+
"Done"
|
|
594
|
+
))))
|
|
595
|
+
);
|
|
403
596
|
}
|
|
404
597
|
|
|
405
598
|
// ui/src/command/MdxCommandPalette.jsx
|
|
406
|
-
import
|
|
599
|
+
import React16 from "react";
|
|
407
600
|
|
|
408
601
|
// ui/src/Icons.jsx
|
|
409
|
-
import
|
|
410
|
-
var MagnifyingGlassIcon = (props) => /* @__PURE__ */
|
|
602
|
+
import React13 from "react";
|
|
603
|
+
var MagnifyingGlassIcon = (props) => /* @__PURE__ */ React13.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props }, /* @__PURE__ */ React13.createElement("path", { d: "M456.69 421.39L362.6 327.3a173.81 173.81 0 0034.84-104.58C397.44 126.38 319.06 48 222.72 48S48 126.38 48 222.72s78.38 174.72 174.72 174.72A173.81 173.81 0 00327.3 362.6l94.09 94.09a25 25 0 0035.3-35.3zM97.92 222.72a124.8 124.8 0 11124.8 124.8 124.95 124.95 0 01-124.8-124.8z" }));
|
|
411
604
|
|
|
412
605
|
// ui/src/search/SearchPanelForm.jsx
|
|
413
|
-
import
|
|
606
|
+
import React14 from "react";
|
|
414
607
|
function readBasePath() {
|
|
415
608
|
const normalize = (val) => {
|
|
416
609
|
const raw = typeof val === "string" ? val.trim() : "";
|
|
@@ -468,14 +661,42 @@ function SearchPanelForm(props = {}) {
|
|
|
468
661
|
placeholder = "Search\u2026",
|
|
469
662
|
buttonLabel = "Search",
|
|
470
663
|
label,
|
|
471
|
-
searchPath = "/search"
|
|
664
|
+
searchPath = "/search",
|
|
665
|
+
inputId: inputIdProp
|
|
472
666
|
} = props || {};
|
|
473
667
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
474
|
-
const action =
|
|
668
|
+
const action = React14.useMemo(
|
|
475
669
|
() => resolveSearchPath(searchPath),
|
|
476
670
|
[searchPath]
|
|
477
671
|
);
|
|
478
|
-
|
|
672
|
+
const autoId = typeof React14.useId === "function" ? React14.useId() : void 0;
|
|
673
|
+
const [fallbackId] = React14.useState(
|
|
674
|
+
() => `canopy-cmdk-${Math.random().toString(36).slice(2, 10)}`
|
|
675
|
+
);
|
|
676
|
+
const inputId = inputIdProp || autoId || fallbackId;
|
|
677
|
+
const inputRef = React14.useRef(null);
|
|
678
|
+
const focusInput = React14.useCallback(() => {
|
|
679
|
+
const el = inputRef.current;
|
|
680
|
+
if (!el) return;
|
|
681
|
+
if (document.activeElement === el) return;
|
|
682
|
+
try {
|
|
683
|
+
el.focus({ preventScroll: true });
|
|
684
|
+
} catch (_) {
|
|
685
|
+
try {
|
|
686
|
+
el.focus();
|
|
687
|
+
} catch (_2) {
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}, []);
|
|
691
|
+
const handlePointerDown = React14.useCallback((event) => {
|
|
692
|
+
const target = event.target;
|
|
693
|
+
if (target && typeof target.closest === "function") {
|
|
694
|
+
if (target.closest("[data-canopy-command-trigger]")) return;
|
|
695
|
+
}
|
|
696
|
+
event.preventDefault();
|
|
697
|
+
focusInput();
|
|
698
|
+
}, [focusInput]);
|
|
699
|
+
return /* @__PURE__ */ React14.createElement(
|
|
479
700
|
"form",
|
|
480
701
|
{
|
|
481
702
|
action,
|
|
@@ -483,36 +704,47 @@ function SearchPanelForm(props = {}) {
|
|
|
483
704
|
role: "search",
|
|
484
705
|
autoComplete: "off",
|
|
485
706
|
spellCheck: "false",
|
|
486
|
-
className: "group flex items-center gap-2
|
|
707
|
+
className: "group flex items-center gap-2 rounded-lg border border-slate-300 text-slate-700 shadow-sm transition focus-within:ring-2 focus-within:ring-brand-500 canopy-cmdk-form",
|
|
708
|
+
onPointerDown: handlePointerDown,
|
|
709
|
+
"data-placeholder": placeholder || ""
|
|
487
710
|
},
|
|
488
|
-
/* @__PURE__ */
|
|
489
|
-
|
|
490
|
-
"input",
|
|
711
|
+
/* @__PURE__ */ React14.createElement(
|
|
712
|
+
"label",
|
|
491
713
|
{
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
714
|
+
htmlFor: inputId,
|
|
715
|
+
className: "flex items-center gap-2 flex-1 min-w-0 cursor-text canopy-cmdk-label"
|
|
716
|
+
},
|
|
717
|
+
/* @__PURE__ */ React14.createElement(MagnifyingGlassIcon, { className: "w-5 h-5 text-slate-400 group-focus-within:text-brand-500 pointer-events-none" }),
|
|
718
|
+
/* @__PURE__ */ React14.createElement(
|
|
719
|
+
"input",
|
|
720
|
+
{
|
|
721
|
+
id: inputId,
|
|
722
|
+
type: "search",
|
|
723
|
+
name: "q",
|
|
724
|
+
inputMode: "search",
|
|
725
|
+
"data-canopy-command-input": true,
|
|
726
|
+
placeholder,
|
|
727
|
+
className: "flex-1 bg-transparent outline-none placeholder:text-slate-400 py-1 min-w-0",
|
|
728
|
+
"aria-label": "Search",
|
|
729
|
+
ref: inputRef
|
|
730
|
+
}
|
|
731
|
+
)
|
|
500
732
|
),
|
|
501
|
-
/* @__PURE__ */
|
|
733
|
+
/* @__PURE__ */ React14.createElement(
|
|
502
734
|
"button",
|
|
503
735
|
{
|
|
504
|
-
type: "
|
|
505
|
-
"data-canopy-command-trigger":
|
|
736
|
+
type: "submit",
|
|
737
|
+
"data-canopy-command-trigger": "submit",
|
|
506
738
|
className: "inline-flex items-center gap-2 rounded-md border border-transparent bg-brand px-2 py-1 text-sm font-medium text-white shadow-sm transition hover:bg-brand-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand focus-visible:ring-offset-2"
|
|
507
739
|
},
|
|
508
|
-
/* @__PURE__ */
|
|
509
|
-
/* @__PURE__ */
|
|
740
|
+
/* @__PURE__ */ React14.createElement("span", null, text),
|
|
741
|
+
/* @__PURE__ */ React14.createElement("span", { "aria-hidden": true, className: "hidden sm:inline-flex items-center gap-1 text-xs font-semibold canopy-cmdk-shortcut" }, /* @__PURE__ */ React14.createElement("span", null, "\u2318"), /* @__PURE__ */ React14.createElement("span", null, "K"))
|
|
510
742
|
)
|
|
511
743
|
);
|
|
512
744
|
}
|
|
513
745
|
|
|
514
746
|
// ui/src/search/SearchPanelTeaserResults.jsx
|
|
515
|
-
import
|
|
747
|
+
import React15 from "react";
|
|
516
748
|
function SearchPanelTeaserResults(props = {}) {
|
|
517
749
|
const { style } = props || {};
|
|
518
750
|
const baseStyle = {
|
|
@@ -529,7 +761,7 @@ function SearchPanelTeaserResults(props = {}) {
|
|
|
529
761
|
overflow: "auto",
|
|
530
762
|
maxHeight: "60vh"
|
|
531
763
|
};
|
|
532
|
-
return /* @__PURE__ */
|
|
764
|
+
return /* @__PURE__ */ React15.createElement("div", { "data-canopy-command-panel": true, style: { ...baseStyle, ...style || {} } }, /* @__PURE__ */ React15.createElement("div", { id: "cplist" }));
|
|
533
765
|
}
|
|
534
766
|
|
|
535
767
|
// ui/src/command/MdxCommandPalette.jsx
|
|
@@ -548,17 +780,17 @@ function MdxCommandPalette(props = {}) {
|
|
|
548
780
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
549
781
|
const resolvedSearchPath = resolveSearchPath(searchPath);
|
|
550
782
|
const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
|
|
551
|
-
return /* @__PURE__ */
|
|
783
|
+
return /* @__PURE__ */ React16.createElement("div", { "data-canopy-command": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React16.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React16.createElement("style", null, `.relative[data-canopy-panel-auto='1']:focus-within [data-canopy-command-panel]{display:block}`), /* @__PURE__ */ React16.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React16.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React16.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
|
|
552
784
|
}
|
|
553
785
|
|
|
554
786
|
// ui/src/search/SearchPanel.jsx
|
|
555
|
-
import
|
|
787
|
+
import React17 from "react";
|
|
556
788
|
function SearchPanel(props = {}) {
|
|
557
789
|
const {
|
|
558
790
|
placeholder = "Search\u2026",
|
|
559
791
|
hotkey = "mod+k",
|
|
560
792
|
maxResults = 8,
|
|
561
|
-
groupOrder = ["work", "page"],
|
|
793
|
+
groupOrder = ["work", "docs", "page"],
|
|
562
794
|
// Kept for backward compat; form always renders submit
|
|
563
795
|
button = true,
|
|
564
796
|
// eslint-disable-line no-unused-vars
|
|
@@ -569,7 +801,7 @@ function SearchPanel(props = {}) {
|
|
|
569
801
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
570
802
|
const resolvedSearchPath = resolveSearchPath(searchPath);
|
|
571
803
|
const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
|
|
572
|
-
return /* @__PURE__ */
|
|
804
|
+
return /* @__PURE__ */ React17.createElement("div", { "data-canopy-command": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React17.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React17.createElement("style", null, `.relative[data-canopy-panel-auto='1']:focus-within [data-canopy-command-panel]{display:block}`), /* @__PURE__ */ React17.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React17.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React17.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
|
|
573
805
|
}
|
|
574
806
|
export {
|
|
575
807
|
Card,
|
|
@@ -578,6 +810,7 @@ export {
|
|
|
578
810
|
GridItem,
|
|
579
811
|
HelloWorld,
|
|
580
812
|
MdxRelatedItems as RelatedItems,
|
|
813
|
+
SearchFiltersDialog,
|
|
581
814
|
SearchPanel,
|
|
582
815
|
SearchPanelForm,
|
|
583
816
|
SearchPanelTeaserResults,
|