@echothink-ui/search 0.1.0

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.
Files changed (40) hide show
  1. package/README.md +5 -0
  2. package/dist/components/AgentSearchSuggestion.d.ts +2 -0
  3. package/dist/components/AppCommandSearch.d.ts +2 -0
  4. package/dist/components/AppDomainSearch.d.ts +2 -0
  5. package/dist/components/EntitySearchInput.d.ts +4 -0
  6. package/dist/components/GlobalCommandPalette.d.ts +4 -0
  7. package/dist/components/ProjectCommandPalette.d.ts +2 -0
  8. package/dist/components/RecentItems.d.ts +2 -0
  9. package/dist/components/ResourceSearch.d.ts +2 -0
  10. package/dist/components/SavedSearches.d.ts +2 -0
  11. package/dist/components/SearchFacetPanel.d.ts +2 -0
  12. package/dist/components/SearchResultsPanel.d.ts +2 -0
  13. package/dist/components/SemanticSearchResult.d.ts +2 -0
  14. package/dist/components/searchUtils.d.ts +3 -0
  15. package/dist/components/types.d.ts +175 -0
  16. package/dist/index.cjs +1224 -0
  17. package/dist/index.cjs.map +1 -0
  18. package/dist/index.css +1460 -0
  19. package/dist/index.css.map +1 -0
  20. package/dist/index.d.ts +16 -0
  21. package/dist/index.js +1185 -0
  22. package/dist/index.js.map +1 -0
  23. package/package.json +43 -0
  24. package/src/components/AgentSearchSuggestion.tsx +44 -0
  25. package/src/components/AppCommandSearch.tsx +163 -0
  26. package/src/components/AppDomainSearch.tsx +90 -0
  27. package/src/components/EntitySearchInput.tsx +165 -0
  28. package/src/components/GlobalCommandPalette.tsx +182 -0
  29. package/src/components/ProjectCommandPalette.tsx +24 -0
  30. package/src/components/RecentItems.tsx +136 -0
  31. package/src/components/ResourceSearch.tsx +27 -0
  32. package/src/components/SavedSearches.tsx +27 -0
  33. package/src/components/SearchFacetPanel.tsx +100 -0
  34. package/src/components/SearchResultsPanel.tsx +327 -0
  35. package/src/components/SemanticSearchResult.tsx +52 -0
  36. package/src/components/searchUtils.ts +20 -0
  37. package/src/components/types.ts +208 -0
  38. package/src/index.test.tsx +254 -0
  39. package/src/index.tsx +55 -0
  40. package/src/styles.css +1716 -0
package/dist/index.js ADDED
@@ -0,0 +1,1185 @@
1
+ // src/components/AgentSearchSuggestion.tsx
2
+ import * as React from "react";
3
+ import { AgentRunningIcon, ChevronRightIcon } from "@echothink-ui/icons";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ function AgentSearchSuggestion({
6
+ suggestion,
7
+ className,
8
+ onClick,
9
+ ...props
10
+ }) {
11
+ const handleClick = React.useCallback(
12
+ (event) => {
13
+ onClick?.(event);
14
+ if (!event.defaultPrevented) {
15
+ suggestion.onSelect?.();
16
+ }
17
+ },
18
+ [onClick, suggestion]
19
+ );
20
+ return /* @__PURE__ */ jsxs(
21
+ "button",
22
+ {
23
+ ...props,
24
+ type: "button",
25
+ className: ["eth-search-agent-suggestion", className].filter(Boolean).join(" "),
26
+ "data-eth-component": "AgentSearchSuggestion",
27
+ onClick: handleClick,
28
+ children: [
29
+ /* @__PURE__ */ jsx("span", { className: "eth-search-agent-suggestion__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AgentRunningIcon, { size: 16 }) }),
30
+ /* @__PURE__ */ jsxs("span", { className: "eth-search-agent-suggestion__body", children: [
31
+ /* @__PURE__ */ jsx("span", { className: "eth-search-agent-suggestion__label", children: suggestion.label }),
32
+ suggestion.rationale ? /* @__PURE__ */ jsx("span", { className: "eth-search-agent-suggestion__rationale", children: suggestion.rationale }) : null
33
+ ] }),
34
+ /* @__PURE__ */ jsxs("span", { className: "eth-search-agent-suggestion__action", "aria-hidden": "true", children: [
35
+ "Run",
36
+ /* @__PURE__ */ jsx(ChevronRightIcon, { size: 16 })
37
+ ] })
38
+ ]
39
+ }
40
+ );
41
+ }
42
+
43
+ // src/components/AppCommandSearch.tsx
44
+ import * as React2 from "react";
45
+ import { Button, SearchInput } from "@echothink-ui/core";
46
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
47
+ function commandMatches(command, query) {
48
+ const normalized = query.trim().toLowerCase();
49
+ if (!normalized) return true;
50
+ return [
51
+ command.label,
52
+ command.description,
53
+ command.domain,
54
+ command.kind,
55
+ command.shortcut,
56
+ command.status
57
+ ].filter(Boolean).some((value) => String(value).toLowerCase().includes(normalized));
58
+ }
59
+ function kindLabel(kind) {
60
+ if (kind === "app-domain") return "Domain";
61
+ if (kind === "resource") return "Resource";
62
+ return "Action";
63
+ }
64
+ function AppCommandSearch({
65
+ value,
66
+ defaultValue = "",
67
+ placeholder = "Search app domains, resources, or commands",
68
+ commands = [],
69
+ kbdHint = "\u2318K",
70
+ scopeLabel = "App domains",
71
+ emptyText = "No commands found",
72
+ onChange,
73
+ onSelect,
74
+ onSubmit,
75
+ onKeyDown,
76
+ className,
77
+ ...props
78
+ }) {
79
+ const [internalValue, setInternalValue] = React2.useState(defaultValue);
80
+ const [activeIndex, setActiveIndex] = React2.useState(0);
81
+ const currentValue = value ?? internalValue;
82
+ const filteredCommands = React2.useMemo(
83
+ () => commands.filter((command) => commandMatches(command, currentValue)),
84
+ [commands, currentValue]
85
+ );
86
+ const activeCommand = filteredCommands[activeIndex];
87
+ React2.useEffect(() => {
88
+ setActiveIndex((index) => Math.min(index, Math.max(0, filteredCommands.length - 1)));
89
+ }, [filteredCommands.length]);
90
+ const updateValue = (nextValue) => {
91
+ if (value === void 0) setInternalValue(nextValue);
92
+ onChange?.(nextValue);
93
+ };
94
+ const selectCommand = (command) => {
95
+ command.onSelect?.();
96
+ onSelect?.(command.id);
97
+ };
98
+ const handleKeyDown = (event) => {
99
+ onKeyDown?.(event);
100
+ if (event.defaultPrevented) return;
101
+ if (!filteredCommands.length) return;
102
+ if (event.key === "ArrowDown") {
103
+ event.preventDefault();
104
+ setActiveIndex((index) => Math.min(filteredCommands.length - 1, index + 1));
105
+ return;
106
+ }
107
+ if (event.key === "ArrowUp") {
108
+ event.preventDefault();
109
+ setActiveIndex((index) => Math.max(0, index - 1));
110
+ return;
111
+ }
112
+ if (event.key === "Enter") {
113
+ event.preventDefault();
114
+ selectCommand(filteredCommands[activeIndex]);
115
+ }
116
+ };
117
+ return /* @__PURE__ */ jsxs2(
118
+ "section",
119
+ {
120
+ ...props,
121
+ className: ["eth-search-app-command", className].filter(Boolean).join(" "),
122
+ "data-eth-component": "AppCommandSearch",
123
+ onKeyDown: handleKeyDown,
124
+ children: [
125
+ /* @__PURE__ */ jsxs2(
126
+ "form",
127
+ {
128
+ className: "eth-search-app-command__bar",
129
+ role: "search",
130
+ onSubmit: (event) => {
131
+ event.preventDefault();
132
+ if (activeCommand) {
133
+ selectCommand(activeCommand);
134
+ return;
135
+ }
136
+ onSubmit?.(currentValue);
137
+ },
138
+ children: [
139
+ /* @__PURE__ */ jsx2(
140
+ SearchInput,
141
+ {
142
+ className: "eth-search-app-command__input",
143
+ density: "comfortable",
144
+ value: currentValue,
145
+ placeholder,
146
+ onChange: (event) => updateValue(event.currentTarget.value),
147
+ onClear: () => updateValue("")
148
+ }
149
+ ),
150
+ /* @__PURE__ */ jsxs2("div", { className: "eth-search-app-command__controls", children: [
151
+ kbdHint ? /* @__PURE__ */ jsx2("kbd", { className: "eth-search-app-command__kbd", children: kbdHint }) : null,
152
+ /* @__PURE__ */ jsx2(Button, { density: "compact", intent: "primary", type: "submit", children: "Run" })
153
+ ] })
154
+ ]
155
+ }
156
+ ),
157
+ /* @__PURE__ */ jsxs2("div", { className: "eth-search-app-command__summary", children: [
158
+ /* @__PURE__ */ jsx2("span", { children: scopeLabel }),
159
+ /* @__PURE__ */ jsxs2("strong", { children: [
160
+ filteredCommands.length,
161
+ " available"
162
+ ] })
163
+ ] }),
164
+ /* @__PURE__ */ jsxs2("div", { className: "eth-search-app-command__list", role: "listbox", "aria-label": "App commands", children: [
165
+ filteredCommands.map((command, index) => {
166
+ const active = index === activeIndex;
167
+ return /* @__PURE__ */ jsxs2(
168
+ "button",
169
+ {
170
+ type: "button",
171
+ role: "option",
172
+ "aria-selected": active,
173
+ className: `eth-search-app-command__item ${active ? "eth-search-app-command__item--active" : ""}`,
174
+ onMouseEnter: () => setActiveIndex(index),
175
+ onClick: () => selectCommand(command),
176
+ children: [
177
+ /* @__PURE__ */ jsx2("span", { className: "eth-search-app-command__kind", children: kindLabel(command.kind) }),
178
+ /* @__PURE__ */ jsxs2("span", { className: "eth-search-app-command__copy", children: [
179
+ /* @__PURE__ */ jsx2("strong", { children: command.label }),
180
+ command.description ? /* @__PURE__ */ jsx2("small", { children: command.description }) : null
181
+ ] }),
182
+ /* @__PURE__ */ jsxs2("span", { className: "eth-search-app-command__meta", children: [
183
+ command.domain ? /* @__PURE__ */ jsx2("span", { children: command.domain }) : null,
184
+ command.shortcut ? /* @__PURE__ */ jsx2("kbd", { children: command.shortcut }) : null,
185
+ command.status ? /* @__PURE__ */ jsx2("small", { children: command.status }) : null
186
+ ] })
187
+ ]
188
+ },
189
+ command.id
190
+ );
191
+ }),
192
+ !filteredCommands.length ? /* @__PURE__ */ jsx2("p", { className: "eth-search-app-command__empty", children: emptyText }) : null
193
+ ] })
194
+ ]
195
+ }
196
+ );
197
+ }
198
+
199
+ // src/components/AppDomainSearch.tsx
200
+ import * as React3 from "react";
201
+ import { SearchInput as SearchInput2 } from "@echothink-ui/core";
202
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
203
+ function AppDomainSearch({
204
+ appDomainRef,
205
+ placeholder = "Search app-domain catalog or instances",
206
+ className,
207
+ suggestions = [],
208
+ value,
209
+ defaultValue,
210
+ onChange,
211
+ onSearch,
212
+ onSelect,
213
+ open: _open,
214
+ defaultOpen: _defaultOpen,
215
+ loading: _loading,
216
+ emptyText: _emptyText,
217
+ onOpenChange: _onOpenChange,
218
+ ...props
219
+ }) {
220
+ const [internalValue, setInternalValue] = React3.useState(
221
+ () => Array.isArray(defaultValue) ? defaultValue.join(",") : String(defaultValue ?? "")
222
+ );
223
+ const currentValue = value ?? internalValue;
224
+ const scopeLabel = appDomainRef ? appDomainRef.replace(/[-_]/g, " ") : "All app domains";
225
+ const update = (nextValue) => {
226
+ if (value === void 0) {
227
+ setInternalValue(nextValue);
228
+ }
229
+ onChange?.(nextValue);
230
+ onSearch?.(nextValue);
231
+ };
232
+ const choose = (suggestion) => {
233
+ if (value === void 0) {
234
+ setInternalValue(suggestion.label);
235
+ }
236
+ onChange?.(suggestion.id);
237
+ onSearch?.(suggestion.label);
238
+ onSelect?.(suggestion);
239
+ };
240
+ return /* @__PURE__ */ jsxs3(
241
+ "section",
242
+ {
243
+ ...props,
244
+ className: `eth-search-app-domain ${className ?? ""}`,
245
+ "data-app-domain-ref": appDomainRef,
246
+ "data-eth-component": "AppDomainSearch",
247
+ children: [
248
+ /* @__PURE__ */ jsxs3("div", { className: "eth-search-app-domain__header", children: [
249
+ /* @__PURE__ */ jsx3("span", { children: "App-domain search" }),
250
+ /* @__PURE__ */ jsx3("strong", { children: scopeLabel })
251
+ ] }),
252
+ /* @__PURE__ */ jsx3(
253
+ SearchInput2,
254
+ {
255
+ value: currentValue,
256
+ placeholder,
257
+ onChange: (event) => update(event.currentTarget.value),
258
+ onClear: () => update(""),
259
+ className: "eth-search-app-domain__input"
260
+ }
261
+ ),
262
+ /* @__PURE__ */ jsxs3("div", { className: "eth-search-app-domain__summary", "aria-live": "polite", children: [
263
+ /* @__PURE__ */ jsx3("span", { children: suggestions.length ? `${suggestions.length} matching domains` : "Catalog and running instances" }),
264
+ /* @__PURE__ */ jsx3("span", { children: appDomainRef ? "Scoped instance" : "Global catalog" })
265
+ ] }),
266
+ suggestions.length ? /* @__PURE__ */ jsx3("ul", { className: "eth-search-app-domain__results", "aria-label": "App-domain search results", children: suggestions.map((suggestion) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsxs3("button", { type: "button", onClick: () => choose(suggestion), children: [
267
+ /* @__PURE__ */ jsxs3("span", { className: "eth-search-app-domain__result-copy", children: [
268
+ /* @__PURE__ */ jsx3("strong", { children: suggestion.label }),
269
+ suggestion.kind ? /* @__PURE__ */ jsx3("small", { children: suggestion.kind }) : null
270
+ ] }),
271
+ suggestion.meta ? /* @__PURE__ */ jsx3("span", { className: "eth-search-app-domain__result-meta", children: suggestion.meta }) : null
272
+ ] }) }, suggestion.id)) }) : null
273
+ ]
274
+ }
275
+ );
276
+ }
277
+
278
+ // src/components/EntitySearchInput.tsx
279
+ import * as React4 from "react";
280
+ import { SearchInput as SearchInput3 } from "@echothink-ui/core";
281
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
282
+ function EntitySearchInput({
283
+ placeholder = "Search",
284
+ onSearch,
285
+ suggestions = [],
286
+ value,
287
+ defaultValue = "",
288
+ onChange,
289
+ onSelect,
290
+ suggestionsLabel = "Entity suggestions",
291
+ resultsLabel = "Entity matches",
292
+ loadingLabel = "Searching entities",
293
+ open,
294
+ defaultOpen = false,
295
+ loading = false,
296
+ emptyText = "No matching entities",
297
+ onOpenChange,
298
+ className,
299
+ onBlur,
300
+ onKeyDown,
301
+ "data-eth-component": dataEthComponent = "EntitySearchInput",
302
+ ...props
303
+ }) {
304
+ const listboxId = React4.useId().replace(/:/g, "");
305
+ const [internalValue, setInternalValue] = React4.useState(defaultValue);
306
+ const [internalOpen, setInternalOpen] = React4.useState(defaultOpen);
307
+ const [selectedId, setSelectedId] = React4.useState(() => {
308
+ const initialValue = value ?? defaultValue;
309
+ return suggestions.find((suggestion) => suggestion.id === initialValue)?.id;
310
+ });
311
+ const currentValue = value ?? internalValue;
312
+ const selectedSuggestion = suggestions.find((suggestion) => suggestion.id === currentValue) ?? suggestions.find((suggestion) => suggestion.id === selectedId);
313
+ const displayValue = selectedSuggestion?.id === currentValue ? selectedSuggestion.label : currentValue;
314
+ const isOpen = open ?? internalOpen;
315
+ const setOpen = (nextOpen) => {
316
+ if (open === void 0) {
317
+ setInternalOpen(nextOpen);
318
+ }
319
+ onOpenChange?.(nextOpen);
320
+ };
321
+ const update = (nextValue) => {
322
+ if (value === void 0) {
323
+ setInternalValue(nextValue);
324
+ }
325
+ setSelectedId(void 0);
326
+ onChange?.(nextValue);
327
+ onSearch?.(nextValue);
328
+ setOpen(Boolean(nextValue || suggestions.length || loading));
329
+ };
330
+ const choose = (suggestion) => {
331
+ if (value === void 0) {
332
+ setInternalValue(suggestion.label);
333
+ }
334
+ setSelectedId(suggestion.id);
335
+ onChange?.(suggestion.id);
336
+ onSearch?.(suggestion.label);
337
+ onSelect?.(suggestion);
338
+ setOpen(false);
339
+ };
340
+ const clear = () => {
341
+ if (value === void 0) {
342
+ setInternalValue("");
343
+ }
344
+ setSelectedId(void 0);
345
+ onChange?.("");
346
+ onSearch?.("");
347
+ setOpen(false);
348
+ };
349
+ const handleBlur = (event) => {
350
+ onBlur?.(event);
351
+ if (!event.currentTarget.contains(event.relatedTarget)) {
352
+ setOpen(false);
353
+ }
354
+ };
355
+ const handleKeyDown = (event) => {
356
+ onKeyDown?.(event);
357
+ if (event.defaultPrevented) return;
358
+ if (event.key === "Escape") {
359
+ setOpen(false);
360
+ event.stopPropagation();
361
+ }
362
+ if (event.key === "ArrowDown" && suggestions.length) {
363
+ setOpen(true);
364
+ }
365
+ };
366
+ return /* @__PURE__ */ jsxs4(
367
+ "div",
368
+ {
369
+ ...props,
370
+ className: `eth-search-entity-input ${className ?? ""}`,
371
+ "data-eth-component": dataEthComponent,
372
+ onBlur: handleBlur,
373
+ onKeyDown: handleKeyDown,
374
+ children: [
375
+ /* @__PURE__ */ jsx4(
376
+ SearchInput3,
377
+ {
378
+ "aria-controls": isOpen ? listboxId : void 0,
379
+ "aria-expanded": isOpen,
380
+ "aria-haspopup": "listbox",
381
+ autoComplete: "off",
382
+ className: "eth-search-entity-input__field",
383
+ value: displayValue,
384
+ placeholder,
385
+ onFocus: () => setOpen(Boolean(suggestions.length || loading)),
386
+ onChange: (event) => update(event.currentTarget.value),
387
+ onClear: clear
388
+ }
389
+ ),
390
+ isOpen ? /* @__PURE__ */ jsxs4(
391
+ "div",
392
+ {
393
+ className: "eth-search-entity-input__popover",
394
+ id: listboxId,
395
+ role: "listbox",
396
+ "aria-label": suggestionsLabel,
397
+ children: [
398
+ /* @__PURE__ */ jsxs4("div", { className: "eth-search-entity-input__summary", "aria-live": "polite", children: [
399
+ /* @__PURE__ */ jsx4("span", { children: loading ? loadingLabel : resultsLabel }),
400
+ /* @__PURE__ */ jsx4("strong", { children: loading ? "Loading" : `${suggestions.length} result${suggestions.length === 1 ? "" : "s"}` })
401
+ ] }),
402
+ suggestions.length ? /* @__PURE__ */ jsx4("ul", { className: "eth-search-entity-input__list", children: suggestions.map((suggestion) => {
403
+ const isSelected = suggestion.id === selectedSuggestion?.id;
404
+ return /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs4(
405
+ "button",
406
+ {
407
+ type: "button",
408
+ role: "option",
409
+ "aria-selected": isSelected,
410
+ onClick: () => choose(suggestion),
411
+ children: [
412
+ /* @__PURE__ */ jsxs4("span", { className: "eth-search-entity-input__copy", children: [
413
+ /* @__PURE__ */ jsx4("strong", { children: suggestion.label }),
414
+ suggestion.kind ? /* @__PURE__ */ jsx4("small", { children: suggestion.kind }) : null
415
+ ] }),
416
+ /* @__PURE__ */ jsx4("span", { className: "eth-search-entity-input__meta", children: isSelected ? "Selected" : suggestion.meta })
417
+ ]
418
+ }
419
+ ) }, suggestion.id);
420
+ }) }) : /* @__PURE__ */ jsx4("p", { className: "eth-search-entity-input__empty", children: loading ? "Loading..." : emptyText })
421
+ ]
422
+ }
423
+ ) : null
424
+ ]
425
+ }
426
+ );
427
+ }
428
+
429
+ // src/components/GlobalCommandPalette.tsx
430
+ import * as React5 from "react";
431
+ import { SearchInput as SearchInput4 } from "@echothink-ui/core";
432
+ import { CloseIcon } from "@echothink-ui/icons";
433
+
434
+ // src/components/searchUtils.ts
435
+ function filterCommands(commands, query) {
436
+ const normalized = query.trim().toLowerCase();
437
+ if (!normalized) return commands;
438
+ return commands.filter(
439
+ (command) => [command.label, command.hint, command.group, command.shortcut].filter(Boolean).some((value) => String(value).toLowerCase().includes(normalized))
440
+ );
441
+ }
442
+ function groupBy(items, getKey) {
443
+ const groups = /* @__PURE__ */ new Map();
444
+ items.forEach((item) => {
445
+ const key = getKey(item);
446
+ groups.set(key, [...groups.get(key) ?? [], item]);
447
+ });
448
+ return Array.from(groups.entries());
449
+ }
450
+
451
+ // src/components/GlobalCommandPalette.tsx
452
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
453
+ function GlobalCommandPalette({
454
+ open,
455
+ onClose,
456
+ commands,
457
+ heading = "Global command palette",
458
+ contextLabel,
459
+ query,
460
+ searchPlaceholder = "Search commands",
461
+ onQueryChange,
462
+ className,
463
+ "data-eth-component": dataEthComponent = "GlobalCommandPalette",
464
+ ...props
465
+ }) {
466
+ const [internalQuery, setInternalQuery] = React5.useState("");
467
+ const [activeIndex, setActiveIndex] = React5.useState(0);
468
+ const generatedId = React5.useId().replace(/:/g, "");
469
+ const searchRef = React5.useRef(null);
470
+ const currentQuery = query ?? internalQuery;
471
+ const filtered = React5.useMemo(
472
+ () => filterCommands(commands, currentQuery),
473
+ [commands, currentQuery]
474
+ );
475
+ const grouped = groupBy(filtered, (command) => command.group ?? "Commands");
476
+ const titleId = `eth-search-command-palette-title-${generatedId}`;
477
+ const listboxId = `eth-search-command-palette-listbox-${generatedId}`;
478
+ const activeOptionId = filtered.length ? `${listboxId}-option-${activeIndex}` : void 0;
479
+ const resultCountLabel = `${filtered.length} ${filtered.length === 1 ? "command" : "commands"} ${currentQuery.trim() ? "matched" : "available"}`;
480
+ React5.useEffect(() => {
481
+ if (!open) return;
482
+ setActiveIndex(0);
483
+ const input = searchRef.current?.querySelector("input");
484
+ input?.focus();
485
+ }, [open]);
486
+ React5.useEffect(() => {
487
+ setActiveIndex((index) => Math.min(index, Math.max(0, filtered.length - 1)));
488
+ }, [filtered.length]);
489
+ if (!open) return null;
490
+ const setQuery = (nextQuery) => {
491
+ setInternalQuery(nextQuery);
492
+ onQueryChange?.(nextQuery);
493
+ };
494
+ const selectCommand = (command) => {
495
+ command.onSelect();
496
+ onClose();
497
+ };
498
+ const onKeyDown = (event) => {
499
+ if (event.key === "Escape") {
500
+ event.preventDefault();
501
+ onClose();
502
+ return;
503
+ }
504
+ if (event.key === "ArrowDown") {
505
+ event.preventDefault();
506
+ if (!filtered.length) return;
507
+ setActiveIndex((index) => Math.min(filtered.length - 1, index + 1));
508
+ return;
509
+ }
510
+ if (event.key === "ArrowUp") {
511
+ event.preventDefault();
512
+ if (!filtered.length) return;
513
+ setActiveIndex((index) => Math.max(0, index - 1));
514
+ return;
515
+ }
516
+ if (event.key === "Enter") {
517
+ event.preventDefault();
518
+ const command = filtered[activeIndex];
519
+ if (command) selectCommand(command);
520
+ }
521
+ };
522
+ let flatIndex = 0;
523
+ return /* @__PURE__ */ jsxs5(
524
+ "div",
525
+ {
526
+ ...props,
527
+ className: `eth-search-command-palette ${className ?? ""}`,
528
+ "data-eth-component": dataEthComponent,
529
+ role: "dialog",
530
+ "aria-modal": "true",
531
+ "aria-labelledby": titleId,
532
+ onKeyDown,
533
+ children: [
534
+ /* @__PURE__ */ jsx5(
535
+ "button",
536
+ {
537
+ type: "button",
538
+ className: "eth-search-command-palette__scrim",
539
+ "aria-label": "Close command palette",
540
+ onClick: onClose
541
+ }
542
+ ),
543
+ /* @__PURE__ */ jsxs5("section", { className: "eth-search-command-palette__panel", "aria-labelledby": titleId, children: [
544
+ /* @__PURE__ */ jsxs5("header", { className: "eth-search-command-palette__header", children: [
545
+ /* @__PURE__ */ jsxs5("div", { className: "eth-search-command-palette__heading", children: [
546
+ /* @__PURE__ */ jsx5("h2", { id: titleId, children: heading }),
547
+ /* @__PURE__ */ jsxs5("div", { className: "eth-search-command-palette__meta", children: [
548
+ contextLabel ? /* @__PURE__ */ jsx5("span", { children: contextLabel }) : null,
549
+ /* @__PURE__ */ jsx5("span", { "aria-live": "polite", children: resultCountLabel })
550
+ ] })
551
+ ] }),
552
+ /* @__PURE__ */ jsx5(
553
+ "button",
554
+ {
555
+ type: "button",
556
+ className: "eth-search-command-palette__close",
557
+ "aria-label": "Close command palette",
558
+ onClick: onClose,
559
+ children: /* @__PURE__ */ jsx5(CloseIcon, { size: 16 })
560
+ }
561
+ )
562
+ ] }),
563
+ /* @__PURE__ */ jsx5("div", { ref: searchRef, className: "eth-search-command-palette__search", children: /* @__PURE__ */ jsx5(
564
+ SearchInput4,
565
+ {
566
+ value: currentQuery,
567
+ placeholder: searchPlaceholder,
568
+ "aria-controls": listboxId,
569
+ "aria-activedescendant": activeOptionId,
570
+ onChange: (event) => setQuery(event.currentTarget.value),
571
+ onClear: () => setQuery("")
572
+ }
573
+ ) }),
574
+ /* @__PURE__ */ jsxs5(
575
+ "div",
576
+ {
577
+ id: listboxId,
578
+ className: "eth-search-command-palette__results",
579
+ role: "listbox",
580
+ "aria-label": "Commands",
581
+ children: [
582
+ grouped.map(([group, items], groupIndex) => {
583
+ const groupHeadingId = `${listboxId}-group-${groupIndex}`;
584
+ return /* @__PURE__ */ jsxs5(
585
+ "section",
586
+ {
587
+ className: "eth-search-command-palette__group",
588
+ role: "group",
589
+ "aria-labelledby": groupHeadingId,
590
+ children: [
591
+ /* @__PURE__ */ jsx5("h3", { id: groupHeadingId, children: group }),
592
+ items.map((command) => {
593
+ const index = flatIndex;
594
+ flatIndex += 1;
595
+ const active = index === activeIndex;
596
+ return /* @__PURE__ */ jsxs5(
597
+ "button",
598
+ {
599
+ id: `${listboxId}-option-${index}`,
600
+ type: "button",
601
+ role: "option",
602
+ "aria-selected": active,
603
+ className: `eth-search-command-palette__item ${active ? "eth-search-command-palette__item--active" : ""}`,
604
+ onMouseEnter: () => setActiveIndex(index),
605
+ onClick: () => selectCommand(command),
606
+ children: [
607
+ /* @__PURE__ */ jsxs5("span", { className: "eth-search-command-palette__item-copy", children: [
608
+ /* @__PURE__ */ jsx5("strong", { children: command.label }),
609
+ command.hint ? /* @__PURE__ */ jsx5("small", { children: command.hint }) : null
610
+ ] }),
611
+ command.shortcut ? /* @__PURE__ */ jsx5("kbd", { children: command.shortcut }) : null
612
+ ]
613
+ },
614
+ command.id
615
+ );
616
+ })
617
+ ]
618
+ },
619
+ group
620
+ );
621
+ }),
622
+ !filtered.length ? /* @__PURE__ */ jsxs5("div", { className: "eth-search-command-palette__empty", role: "status", children: [
623
+ /* @__PURE__ */ jsx5("strong", { children: "No commands found" }),
624
+ /* @__PURE__ */ jsx5("span", { children: "Try a different command, resource, or setting." })
625
+ ] }) : null
626
+ ]
627
+ }
628
+ )
629
+ ] })
630
+ ]
631
+ }
632
+ );
633
+ }
634
+
635
+ // src/components/ProjectCommandPalette.tsx
636
+ import { jsx as jsx6 } from "react/jsx-runtime";
637
+ function ProjectCommandPalette({
638
+ projectRef,
639
+ className,
640
+ contextLabel = `Project ${projectRef}`,
641
+ heading = "Project command palette",
642
+ searchPlaceholder = "Search project commands",
643
+ ...props
644
+ }) {
645
+ return /* @__PURE__ */ jsx6(
646
+ GlobalCommandPalette,
647
+ {
648
+ ...props,
649
+ contextLabel,
650
+ heading,
651
+ className: `eth-search-project-command-palette ${className ?? ""}`,
652
+ "data-project-ref": projectRef,
653
+ "data-eth-component": "ProjectCommandPalette",
654
+ searchPlaceholder
655
+ }
656
+ );
657
+ }
658
+
659
+ // src/components/RecentItems.tsx
660
+ import {
661
+ AgentRunningIcon as AgentRunningIcon2,
662
+ ChevronRightIcon as ChevronRightIcon2,
663
+ DocumentIcon,
664
+ FolderIcon,
665
+ MessageIcon,
666
+ PlayIcon,
667
+ SearchIcon,
668
+ StatusIcon,
669
+ TableIcon
670
+ } from "@echothink-ui/icons";
671
+ import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
672
+ function formatRecentKind(kind) {
673
+ return kind.split(/[-_\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
674
+ }
675
+ function RecentItemIcon({ kind }) {
676
+ const normalizedKind = kind.toLowerCase();
677
+ if (normalizedKind.includes("agent")) return /* @__PURE__ */ jsx7(AgentRunningIcon2, { size: 16 });
678
+ if (normalizedKind.includes("document") || normalizedKind.includes("file")) {
679
+ return /* @__PURE__ */ jsx7(DocumentIcon, { size: 16 });
680
+ }
681
+ if (normalizedKind.includes("project") || normalizedKind.includes("folder")) {
682
+ return /* @__PURE__ */ jsx7(FolderIcon, { size: 16 });
683
+ }
684
+ if (normalizedKind.includes("mail") || normalizedKind.includes("message")) {
685
+ return /* @__PURE__ */ jsx7(MessageIcon, { size: 16 });
686
+ }
687
+ if (normalizedKind.includes("dataset") || normalizedKind.includes("table")) {
688
+ return /* @__PURE__ */ jsx7(TableIcon, { size: 16 });
689
+ }
690
+ if (normalizedKind.includes("action") || normalizedKind.includes("command")) {
691
+ return /* @__PURE__ */ jsx7(PlayIcon, { size: 16 });
692
+ }
693
+ if (normalizedKind.includes("task") || normalizedKind.includes("wave")) {
694
+ return /* @__PURE__ */ jsx7(StatusIcon, { size: 16 });
695
+ }
696
+ return /* @__PURE__ */ jsx7(SearchIcon, { size: 16 });
697
+ }
698
+ function RecentItems({
699
+ items,
700
+ emptyText = "No recent items",
701
+ onSelect,
702
+ className,
703
+ "aria-label": ariaLabel = "Recent items",
704
+ ...props
705
+ }) {
706
+ const classNames = ["eth-search-recent-items", className].filter(Boolean).join(" ");
707
+ if (!items.length) {
708
+ return /* @__PURE__ */ jsx7(
709
+ "section",
710
+ {
711
+ ...props,
712
+ "aria-label": ariaLabel,
713
+ className: classNames,
714
+ "data-eth-component": "RecentItems",
715
+ children: /* @__PURE__ */ jsx7("div", { className: "eth-search-recent-items__empty", role: "status", children: emptyText })
716
+ }
717
+ );
718
+ }
719
+ return /* @__PURE__ */ jsx7(
720
+ "section",
721
+ {
722
+ ...props,
723
+ "aria-label": ariaLabel,
724
+ className: classNames,
725
+ "data-eth-component": "RecentItems",
726
+ children: /* @__PURE__ */ jsx7("ul", { children: items.map((item) => {
727
+ const kindLabel2 = formatRecentKind(item.kind);
728
+ const rowLabel = `Open ${item.label}, ${kindLabel2}, last visited ${item.visitedAt}`;
729
+ const content = /* @__PURE__ */ jsxs6(Fragment, { children: [
730
+ /* @__PURE__ */ jsx7("span", { className: "eth-search-recent-items__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx7(RecentItemIcon, { kind: item.kind }) }),
731
+ /* @__PURE__ */ jsxs6("span", { className: "eth-search-recent-items__copy", children: [
732
+ /* @__PURE__ */ jsx7("span", { className: "eth-search-recent-items__label", children: item.label }),
733
+ item.description ? /* @__PURE__ */ jsx7("span", { className: "eth-search-recent-items__description", children: item.description }) : null
734
+ ] }),
735
+ /* @__PURE__ */ jsx7("span", { className: "eth-search-recent-items__kind", children: kindLabel2 }),
736
+ /* @__PURE__ */ jsxs6("span", { className: "eth-search-recent-items__visited", children: [
737
+ /* @__PURE__ */ jsx7("span", { className: "eth-search-recent-items__visited-label", children: "Last visited" }),
738
+ /* @__PURE__ */ jsx7("time", { dateTime: item.visitedAt, children: item.visitedAt })
739
+ ] }),
740
+ /* @__PURE__ */ jsx7(
741
+ ChevronRightIcon2,
742
+ {
743
+ className: "eth-search-recent-items__arrow",
744
+ size: 16,
745
+ "aria-hidden": "true"
746
+ }
747
+ )
748
+ ] });
749
+ return /* @__PURE__ */ jsx7("li", { children: item.href ? /* @__PURE__ */ jsx7(
750
+ "a",
751
+ {
752
+ className: "eth-search-recent-items__row",
753
+ href: item.href,
754
+ "aria-label": rowLabel,
755
+ onClick: () => onSelect?.(item.id),
756
+ children: content
757
+ }
758
+ ) : /* @__PURE__ */ jsx7(
759
+ "button",
760
+ {
761
+ className: "eth-search-recent-items__row",
762
+ type: "button",
763
+ "aria-label": rowLabel,
764
+ onClick: () => onSelect?.(item.id),
765
+ children: content
766
+ }
767
+ ) }, item.id);
768
+ }) })
769
+ }
770
+ );
771
+ }
772
+
773
+ // src/components/ResourceSearch.tsx
774
+ import { jsx as jsx8 } from "react/jsx-runtime";
775
+ function ResourceSearch({
776
+ resourceScope,
777
+ placeholder = "Search resources",
778
+ emptyText = "No matching resources",
779
+ suggestionsLabel = "Resource suggestions",
780
+ resultsLabel = "Resource matches",
781
+ loadingLabel = "Searching resources",
782
+ className,
783
+ ...props
784
+ }) {
785
+ return /* @__PURE__ */ jsx8(
786
+ EntitySearchInput,
787
+ {
788
+ ...props,
789
+ placeholder,
790
+ emptyText,
791
+ suggestionsLabel,
792
+ resultsLabel,
793
+ loadingLabel,
794
+ className: ["eth-search-resource", className].filter(Boolean).join(" "),
795
+ "data-resource-scope": resourceScope,
796
+ "data-eth-component": "ResourceSearch"
797
+ }
798
+ );
799
+ }
800
+
801
+ // src/components/SavedSearches.tsx
802
+ import { Button as Button2, IconButton } from "@echothink-ui/core";
803
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
804
+ function SavedSearches({ searches, onRun, onDelete, className, ...props }) {
805
+ return /* @__PURE__ */ jsx9(
806
+ "section",
807
+ {
808
+ ...props,
809
+ className: `eth-search-saved-searches ${className ?? ""}`,
810
+ "data-eth-component": "SavedSearches",
811
+ children: /* @__PURE__ */ jsx9("ul", { children: searches.map((search) => /* @__PURE__ */ jsxs7("li", { children: [
812
+ /* @__PURE__ */ jsxs7("div", { children: [
813
+ /* @__PURE__ */ jsx9("strong", { children: search.label }),
814
+ /* @__PURE__ */ jsx9("code", { children: search.query }),
815
+ /* @__PURE__ */ jsx9("small", { children: search.updatedAt })
816
+ ] }),
817
+ onRun ? /* @__PURE__ */ jsx9(Button2, { intent: "secondary", density: "compact", onClick: () => onRun(search.id), children: "Run" }) : null,
818
+ onDelete ? /* @__PURE__ */ jsx9(IconButton, { label: "Delete saved search", intent: "ghost", icon: /* @__PURE__ */ jsx9("span", { "aria-hidden": true, children: "X" }), onClick: () => onDelete(search.id) }) : null
819
+ ] }, search.id)) })
820
+ }
821
+ );
822
+ }
823
+
824
+ // src/components/SearchFacetPanel.tsx
825
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
826
+ function SearchFacetPanel({
827
+ facets,
828
+ selected,
829
+ onChange,
830
+ className,
831
+ "aria-label": ariaLabel,
832
+ ...props
833
+ }) {
834
+ const selectedTotal = facets.reduce(
835
+ (total, facet) => total + (selected[facet.id]?.length ?? 0),
836
+ 0
837
+ );
838
+ const toggle = (facetId, value, checked) => {
839
+ const current = selected[facetId] ?? [];
840
+ onChange({
841
+ ...selected,
842
+ [facetId]: checked ? [...current, value] : current.filter((item) => item !== value)
843
+ });
844
+ };
845
+ const clearAll = () => {
846
+ onChange(Object.fromEntries(facets.map((facet) => [facet.id, []])));
847
+ };
848
+ return /* @__PURE__ */ jsxs8(
849
+ "aside",
850
+ {
851
+ className: ["eth-search-facet-panel", className].filter(Boolean).join(" "),
852
+ "data-eth-component": "SearchFacetPanel",
853
+ "aria-label": ariaLabel ?? "Search filters",
854
+ ...props,
855
+ children: [
856
+ /* @__PURE__ */ jsxs8("div", { className: "eth-search-facet-panel__header", children: [
857
+ /* @__PURE__ */ jsxs8("div", { children: [
858
+ /* @__PURE__ */ jsx10("h2", { className: "eth-search-facet-panel__title", children: "Filters" }),
859
+ /* @__PURE__ */ jsx10("p", { className: "eth-search-facet-panel__summary", children: selectedTotal > 0 ? `${selectedTotal} selected` : "Refine results" })
860
+ ] }),
861
+ selectedTotal > 0 ? /* @__PURE__ */ jsx10("button", { type: "button", className: "eth-search-facet-panel__clear", onClick: clearAll, children: "Clear" }) : null
862
+ ] }),
863
+ facets.length > 0 ? /* @__PURE__ */ jsx10("div", { className: "eth-search-facet-panel__groups", children: facets.map((facet) => /* @__PURE__ */ jsxs8("fieldset", { className: "eth-search-facet-panel__group", children: [
864
+ /* @__PURE__ */ jsx10("legend", { className: "eth-search-facet-panel__legend", children: /* @__PURE__ */ jsx10("span", { children: facet.label }) }),
865
+ /* @__PURE__ */ jsx10("div", { className: "eth-search-facet-panel__options", children: facet.options.map((option) => {
866
+ const checked = (selected[facet.id] ?? []).includes(option.value);
867
+ return /* @__PURE__ */ jsxs8(
868
+ "label",
869
+ {
870
+ className: [
871
+ "eth-search-facet-panel__option",
872
+ checked ? "eth-search-facet-panel__option--checked" : "",
873
+ option.disabled ? "eth-search-facet-panel__option--disabled" : ""
874
+ ].filter(Boolean).join(" "),
875
+ children: [
876
+ /* @__PURE__ */ jsx10(
877
+ "input",
878
+ {
879
+ type: "checkbox",
880
+ checked,
881
+ disabled: option.disabled,
882
+ value: option.value,
883
+ onChange: (event) => toggle(facet.id, option.value, event.currentTarget.checked)
884
+ }
885
+ ),
886
+ /* @__PURE__ */ jsx10("span", { className: "eth-search-facet-panel__control", "aria-hidden": "true" }),
887
+ /* @__PURE__ */ jsxs8("span", { className: "eth-search-facet-panel__option-main", children: [
888
+ /* @__PURE__ */ jsx10("span", { className: "eth-search-facet-panel__option-label", children: option.label }),
889
+ typeof option.count === "number" ? /* @__PURE__ */ jsx10("span", { className: "eth-search-facet-panel__option-count", children: option.count }) : null
890
+ ] })
891
+ ]
892
+ },
893
+ option.value
894
+ );
895
+ }) })
896
+ ] }, facet.id)) }) : /* @__PURE__ */ jsx10("p", { className: "eth-search-facet-panel__empty", children: "No filters available" })
897
+ ]
898
+ }
899
+ );
900
+ }
901
+
902
+ // src/components/SearchResultsPanel.tsx
903
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
904
+ function getGroupItems(group) {
905
+ return group.results ?? group.items ?? [];
906
+ }
907
+ function getGroupLabel(group) {
908
+ return group.label ?? group.title ?? group.entityType ?? group.type ?? "Results";
909
+ }
910
+ function getGroupDomId(group, index) {
911
+ const seed = String(group.id ?? getGroupLabel(group)).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
912
+ return `search-results-${seed || "group"}-${index}`;
913
+ }
914
+ function getResultTitle(result) {
915
+ return result.title ?? result.name ?? result.label ?? "Untitled result";
916
+ }
917
+ function getResultDescription(result) {
918
+ return result.description ?? result.excerpt;
919
+ }
920
+ function getResultHref(result) {
921
+ return result.href ?? result.url;
922
+ }
923
+ function getResultId(result, group, index) {
924
+ return result.id ?? `${group.id ?? getGroupLabel(group)}-${getResultTitle(result)}-${index}`;
925
+ }
926
+ function getTotalCount(groups, totalCount) {
927
+ if (typeof totalCount === "number") {
928
+ return totalCount;
929
+ }
930
+ return groups.reduce((sum, group) => sum + getGroupItems(group).length, 0);
931
+ }
932
+ function getEntityInitial(label) {
933
+ return label.trim().slice(0, 1).toUpperCase();
934
+ }
935
+ function isLabeledMetadata(metadata) {
936
+ return typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) && "value" in metadata;
937
+ }
938
+ function normalizeMetadataValue(metadata) {
939
+ if (isLabeledMetadata(metadata)) {
940
+ if (metadata.label && metadata.value !== void 0) {
941
+ return /* @__PURE__ */ jsxs9(Fragment2, { children: [
942
+ metadata.label,
943
+ ": ",
944
+ metadata.value
945
+ ] });
946
+ }
947
+ return metadata.value;
948
+ }
949
+ return metadata;
950
+ }
951
+ function hasMetadataSourceValue(metadata) {
952
+ return metadata !== null && metadata !== void 0 && metadata !== false && metadata !== "";
953
+ }
954
+ function hasRenderableMetadata(metadata) {
955
+ return metadata !== null && metadata !== void 0 && metadata !== false && metadata !== "";
956
+ }
957
+ function getMetadata(result) {
958
+ const source = result.meta ?? result.metadata;
959
+ const values = Array.isArray(source) ? [...source] : [];
960
+ if (!Array.isArray(source) && hasMetadataSourceValue(source)) {
961
+ values.push(source);
962
+ }
963
+ if (result.owner) {
964
+ values.push({ label: "Owner", value: result.owner });
965
+ }
966
+ if (result.status) {
967
+ values.push(result.status);
968
+ }
969
+ if (result.updatedAt) {
970
+ values.push({ label: "Updated", value: result.updatedAt });
971
+ }
972
+ if (result.version) {
973
+ values.push(`v${result.version}`);
974
+ }
975
+ return values.map(normalizeMetadataValue).filter(hasRenderableMetadata);
976
+ }
977
+ function SearchResultsPanel({
978
+ groups = [],
979
+ query,
980
+ totalCount,
981
+ isLoading = false,
982
+ emptyLabel = "No results found",
983
+ className,
984
+ onResultSelect,
985
+ onSelect,
986
+ ...props
987
+ }) {
988
+ const count = getTotalCount(groups, totalCount);
989
+ const hasResults = groups.some((group) => getGroupItems(group).length > 0);
990
+ const classes = ["eth-search-results-panel", className].filter(Boolean).join(" ");
991
+ const handleSelect = (result, group, resultId) => {
992
+ onResultSelect?.(result, group);
993
+ onSelect?.(result.id ?? resultId);
994
+ };
995
+ return /* @__PURE__ */ jsxs9(
996
+ "section",
997
+ {
998
+ ...props,
999
+ className: classes,
1000
+ "aria-label": props["aria-label"] ?? "Search results",
1001
+ "data-eth-component": "SearchResultsPanel",
1002
+ children: [
1003
+ /* @__PURE__ */ jsxs9("header", { className: "eth-search-results-panel__summary", children: [
1004
+ /* @__PURE__ */ jsxs9("div", { children: [
1005
+ /* @__PURE__ */ jsx11("p", { className: "eth-search-results-panel__eyebrow", children: "Search results" }),
1006
+ /* @__PURE__ */ jsx11("h3", { className: "eth-search-results-panel__title", children: isLoading ? "Searching..." : `${count} result${count === 1 ? "" : "s"}` })
1007
+ ] }),
1008
+ query ? /* @__PURE__ */ jsxs9("span", { className: "eth-search-results-panel__query", children: [
1009
+ '"',
1010
+ query,
1011
+ '"'
1012
+ ] }) : null
1013
+ ] }),
1014
+ isLoading ? /* @__PURE__ */ jsxs9("div", { className: "eth-search-results-panel__state", role: "status", children: [
1015
+ /* @__PURE__ */ jsx11("p", { className: "eth-search-results-panel__loading-copy", children: "Loading results" }),
1016
+ /* @__PURE__ */ jsx11("span", { className: "eth-search-results-panel__skeleton" }),
1017
+ /* @__PURE__ */ jsx11("span", { className: "eth-search-results-panel__skeleton" }),
1018
+ /* @__PURE__ */ jsx11("span", { className: "eth-search-results-panel__skeleton" })
1019
+ ] }) : hasResults ? /* @__PURE__ */ jsx11("div", { className: "eth-search-results-panel__groups", children: groups.map((group, groupIndex) => {
1020
+ const items = getGroupItems(group);
1021
+ const groupLabel = getGroupLabel(group);
1022
+ const groupId = getGroupDomId(group, groupIndex);
1023
+ if (items.length === 0) {
1024
+ return null;
1025
+ }
1026
+ return /* @__PURE__ */ jsxs9(
1027
+ "section",
1028
+ {
1029
+ className: "eth-search-results-panel__group",
1030
+ "aria-labelledby": groupId,
1031
+ children: [
1032
+ /* @__PURE__ */ jsxs9("div", { className: "eth-search-results-panel__group-header", children: [
1033
+ /* @__PURE__ */ jsx11(
1034
+ "h4",
1035
+ {
1036
+ className: "eth-search-results-panel__group-title",
1037
+ id: groupId,
1038
+ children: groupLabel
1039
+ }
1040
+ ),
1041
+ /* @__PURE__ */ jsx11("span", { className: "eth-search-results-panel__count", children: group.count ?? items.length })
1042
+ ] }),
1043
+ /* @__PURE__ */ jsx11("div", { className: "eth-search-results-panel__list", children: items.map((result, index) => {
1044
+ const metadata = getMetadata(result);
1045
+ const resultId = getResultId(result, group, index);
1046
+ const description = getResultDescription(result);
1047
+ const href = getResultHref(result);
1048
+ const title = getResultTitle(result);
1049
+ const resultContent = /* @__PURE__ */ jsxs9(Fragment2, { children: [
1050
+ /* @__PURE__ */ jsx11(
1051
+ "span",
1052
+ {
1053
+ className: "eth-search-results-panel__entity",
1054
+ "aria-hidden": "true",
1055
+ children: getEntityInitial(
1056
+ result.entityType ?? result.type ?? group.entityType ?? groupLabel
1057
+ )
1058
+ }
1059
+ ),
1060
+ /* @__PURE__ */ jsxs9("span", { className: "eth-search-results-panel__body", children: [
1061
+ /* @__PURE__ */ jsx11("span", { className: "eth-search-results-panel__name", children: title }),
1062
+ description ? /* @__PURE__ */ jsx11("span", { className: "eth-search-results-panel__description", children: description }) : null
1063
+ ] }),
1064
+ metadata.length > 0 ? /* @__PURE__ */ jsx11("span", { className: "eth-search-results-panel__metadata", children: metadata.map((item, metaIndex) => /* @__PURE__ */ jsx11(
1065
+ "span",
1066
+ {
1067
+ className: "eth-search-results-panel__meta",
1068
+ children: item
1069
+ },
1070
+ metaIndex
1071
+ )) }) : null
1072
+ ] });
1073
+ if (href) {
1074
+ return /* @__PURE__ */ jsx11(
1075
+ "a",
1076
+ {
1077
+ className: "eth-search-results-panel__item",
1078
+ href,
1079
+ onClick: onResultSelect || onSelect ? () => handleSelect(result, group, resultId) : void 0,
1080
+ children: resultContent
1081
+ },
1082
+ resultId
1083
+ );
1084
+ }
1085
+ if (onResultSelect || onSelect) {
1086
+ return /* @__PURE__ */ jsx11(
1087
+ "button",
1088
+ {
1089
+ className: "eth-search-results-panel__item",
1090
+ type: "button",
1091
+ onClick: () => handleSelect(result, group, resultId),
1092
+ children: resultContent
1093
+ },
1094
+ resultId
1095
+ );
1096
+ }
1097
+ return /* @__PURE__ */ jsx11(
1098
+ "article",
1099
+ {
1100
+ className: "eth-search-results-panel__item",
1101
+ children: resultContent
1102
+ },
1103
+ resultId
1104
+ );
1105
+ }) })
1106
+ ]
1107
+ },
1108
+ group.id ?? `${groupLabel}-${groupIndex}`
1109
+ );
1110
+ }) }) : /* @__PURE__ */ jsxs9("div", { className: "eth-search-results-panel__state", children: [
1111
+ /* @__PURE__ */ jsx11("p", { className: "eth-search-results-panel__empty-title", children: emptyLabel }),
1112
+ query ? /* @__PURE__ */ jsx11("p", { className: "eth-search-results-panel__empty-copy", children: "Try another keyword or remove a filter." }) : null
1113
+ ] })
1114
+ ]
1115
+ }
1116
+ );
1117
+ }
1118
+
1119
+ // src/components/SemanticSearchResult.tsx
1120
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1121
+ function SemanticSearchResult({ result, className, ...props }) {
1122
+ const evidence = result.evidence ?? [];
1123
+ return /* @__PURE__ */ jsxs10(
1124
+ "article",
1125
+ {
1126
+ ...props,
1127
+ className: `eth-search-semantic-result ${className ?? ""}`,
1128
+ "data-eth-component": "SemanticSearchResult",
1129
+ children: [
1130
+ /* @__PURE__ */ jsx12("p", { className: "eth-search-semantic-result__snippet", children: renderHighlightedSnippet(result.snippet, result.highlights ?? []) }),
1131
+ evidence.length > 0 ? /* @__PURE__ */ jsxs10("div", { className: "eth-search-semantic-result__evidence", children: [
1132
+ /* @__PURE__ */ jsx12("span", { className: "eth-search-semantic-result__evidence-label", children: "Evidence" }),
1133
+ /* @__PURE__ */ jsx12("ul", { className: "eth-search-semantic-result__evidence-list", children: evidence.map((item) => /* @__PURE__ */ jsx12("li", { children: item.href ? /* @__PURE__ */ jsx12("a", { className: "eth-search-semantic-result__evidence-item", href: item.href, children: item.label }) : /* @__PURE__ */ jsx12("span", { className: "eth-search-semantic-result__evidence-item", children: item.label }) }, item.id)) })
1134
+ ] }) : null
1135
+ ]
1136
+ }
1137
+ );
1138
+ }
1139
+ function renderHighlightedSnippet(snippet, highlights) {
1140
+ if (!highlights.length) return snippet;
1141
+ const sorted = [...highlights].sort((a, b) => a[0] - b[0]);
1142
+ const parts = [];
1143
+ let cursor = 0;
1144
+ sorted.forEach(([start, end], index) => {
1145
+ const safeStart = Math.max(cursor, Math.min(snippet.length, start));
1146
+ const safeEnd = Math.max(safeStart, Math.min(snippet.length, end));
1147
+ if (safeStart > cursor) parts.push(snippet.slice(cursor, safeStart));
1148
+ parts.push(/* @__PURE__ */ jsx12("mark", { children: snippet.slice(safeStart, safeEnd) }, index));
1149
+ cursor = safeEnd;
1150
+ });
1151
+ if (cursor < snippet.length) parts.push(snippet.slice(cursor));
1152
+ return parts;
1153
+ }
1154
+
1155
+ // src/index.tsx
1156
+ var SearchComponentNames = [
1157
+ "GlobalCommandPalette",
1158
+ "ProjectCommandPalette",
1159
+ "AppCommandSearch",
1160
+ "SearchResultsPanel",
1161
+ "SearchFacetPanel",
1162
+ "SavedSearches",
1163
+ "RecentItems",
1164
+ "EntitySearchInput",
1165
+ "AppDomainSearch",
1166
+ "ResourceSearch",
1167
+ "SemanticSearchResult",
1168
+ "AgentSearchSuggestion"
1169
+ ];
1170
+ export {
1171
+ AgentSearchSuggestion,
1172
+ AppCommandSearch,
1173
+ AppDomainSearch,
1174
+ EntitySearchInput,
1175
+ GlobalCommandPalette,
1176
+ ProjectCommandPalette,
1177
+ RecentItems,
1178
+ ResourceSearch,
1179
+ SavedSearches,
1180
+ SearchComponentNames,
1181
+ SearchFacetPanel,
1182
+ SearchResultsPanel,
1183
+ SemanticSearchResult
1184
+ };
1185
+ //# sourceMappingURL=index.js.map