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