@dbx-tools/ui-search 0.6.108 → 0.6.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -9
- package/package.json +5 -4
- package/src/react/index.ts +3 -3
- package/src/react/search-box.tsx +3 -4
- package/src/react/use-search.ts +69 -47
package/README.md
CHANGED
|
@@ -3,19 +3,21 @@
|
|
|
3
3
|
React search box and results for Databricks AI Search.
|
|
4
4
|
|
|
5
5
|
Import this package when an AppKit UI wants a drop-in, search-as-you-type box
|
|
6
|
-
over
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
over AppKit's beta `aiSearch` plugin. The components delegate single-index
|
|
7
|
+
queries to `useAiSearchQuery`, so native AppKit owns aliases, routes,
|
|
8
|
+
cancellation, and query state. The same UI works with
|
|
9
|
+
`@dbx-tools/search`'s `lakebaseAiSearch` provider because it implements the
|
|
10
|
+
native client-config and response contract.
|
|
10
11
|
|
|
11
12
|
**Key features:**
|
|
12
13
|
|
|
13
14
|
- `SearchBox` - a debounced, cancellable search-as-you-type input with a results
|
|
14
15
|
dropdown, styled with AppKit tokens. Meilisearch-style instant search.
|
|
15
16
|
- `SearchResults` - a presentational hit list for a full-page results layout.
|
|
16
|
-
- `useSearch` -
|
|
17
|
+
- `useSearch` - a debounced presentation adapter over AppKit
|
|
18
|
+
`useAiSearchQuery`: `{ query, setQuery, hits,
|
|
17
19
|
loading, error, config, submit, clear }`, debounced and abortable, targeting
|
|
18
|
-
|
|
20
|
+
native single-index queries or the dbx-tools universal route.
|
|
19
21
|
- Universal search with one flag (`universal`) to search every configured index.
|
|
20
22
|
- `renderHit` overrides on both components for full control of a row; a sensible
|
|
21
23
|
default shows a title, id, and score.
|
|
@@ -61,6 +63,7 @@ function Results() {
|
|
|
61
63
|
- `@dbx-tools/ui-search/styles.css` - AppKit-token styling for the box and
|
|
62
64
|
results; import once after Tailwind and your AppKit-UI theme.
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
[`@dbx-tools/
|
|
66
|
+
Native Vector Search runtime and routes come from `@databricks/appkit`.
|
|
67
|
+
Federated search, agent tools, lifecycle operations, and the Lakebase provider
|
|
68
|
+
live in [`@dbx-tools/search`](../../node/search); its extension wire contract
|
|
69
|
+
lives in [`@dbx-tools/shared-search`](../../shared/search).
|
package/package.json
CHANGED
|
@@ -24,9 +24,10 @@
|
|
|
24
24
|
"typescript": "^5.9.3"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@
|
|
28
|
-
"@dbx-tools/shared-
|
|
29
|
-
"@dbx-tools/
|
|
27
|
+
"@databricks/appkit-ui": "^0.60.0",
|
|
28
|
+
"@dbx-tools/shared-core": "0.6.112",
|
|
29
|
+
"@dbx-tools/shared-search": "0.6.112",
|
|
30
|
+
"@dbx-tools/ui-appkit": "0.6.112",
|
|
30
31
|
"lucide-react": "^0.554.0",
|
|
31
32
|
"react": "^19.2.4",
|
|
32
33
|
"react-dom": "^19.2.4"
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
"publishConfig": {
|
|
36
37
|
"access": "public"
|
|
37
38
|
},
|
|
38
|
-
"version": "0.6.
|
|
39
|
+
"version": "0.6.112",
|
|
39
40
|
"type": "module",
|
|
40
41
|
"exports": {
|
|
41
42
|
"./react": "./src/react/index.ts",
|
package/src/react/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// React surface for `@dbx-tools/ui-search`: a drop-in `SearchBox`
|
|
2
2
|
// (search-as-you-type over Databricks AI Search), a `SearchResults` list for a
|
|
3
|
-
// full-page layout, and the `useSearch` hook they share.
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// full-page layout, and the `useSearch` hook they share. Single-index queries
|
|
4
|
+
// delegate to AppKit's native `useAiSearchQuery`; universal search uses the
|
|
5
|
+
// dbx-tools extension route.
|
|
6
6
|
// Styled with AppKit tokens (import `@dbx-tools/ui-search/styles.css`).
|
|
7
7
|
|
|
8
8
|
export type {
|
package/src/react/search-box.tsx
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
// `SearchBox`
|
|
1
|
+
// `SearchBox` is a drop-in search-as-you-type input for
|
|
2
2
|
// Databricks AI Search. It wires an AppKit `Input` to the {@link useSearch}
|
|
3
|
-
// hook and renders
|
|
4
|
-
//
|
|
5
|
-
// supplies the index, page size, and route.
|
|
3
|
+
// hook and renders hits in a dropdown as the user types. AppKit's native
|
|
4
|
+
// `aiSearch` client config supplies the index alias and query route.
|
|
6
5
|
//
|
|
7
6
|
// It is presentational and unopinionated about what a hit looks like: pass a
|
|
8
7
|
// `renderHit` to control each row, or rely on the default which shows the first
|
package/src/react/use-search.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
// `useSearch`
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// via AppKit's `usePluginClientConfig`, so a search box needs no props to know
|
|
5
|
-
// where to POST. Point it at `@dbx-tools/search`'s `POST /api/search`
|
|
6
|
-
// (single index) or `POST /api/search/universal` (federated) route.
|
|
1
|
+
// `useSearch` adapts AppKit's native `useAiSearchQuery` to a debounced
|
|
2
|
+
// search-as-you-type state machine. Single-index queries use AppKit's native
|
|
3
|
+
// route and client config; universal queries use the dbx-tools extension route.
|
|
7
4
|
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
import {
|
|
6
|
+
search as sharedSearch,
|
|
7
|
+
type SearchClientConfig,
|
|
8
|
+
type SearchHit,
|
|
9
|
+
type SearchMode,
|
|
10
|
+
type SearchResult,
|
|
13
11
|
} from "@dbx-tools/shared-search";
|
|
14
|
-
import {
|
|
12
|
+
import { useAiSearchQuery, type AiSearchRequest } from "@databricks/appkit-ui/react/beta";
|
|
15
13
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
16
14
|
|
|
17
15
|
/** Options for {@link useSearch}. */
|
|
@@ -28,8 +26,6 @@ export interface UseSearchOptions {
|
|
|
28
26
|
debounceMs?: number;
|
|
29
27
|
/** Minimum query length before searching. Defaults to 1. */
|
|
30
28
|
minLength?: number;
|
|
31
|
-
/** The plugin name to read config from / route under. Defaults to "search". */
|
|
32
|
-
pluginName?: string;
|
|
33
29
|
}
|
|
34
30
|
|
|
35
31
|
/** The state {@link useSearch} returns. */
|
|
@@ -52,25 +48,34 @@ export interface UseSearchState {
|
|
|
52
48
|
clear: () => void;
|
|
53
49
|
}
|
|
54
50
|
|
|
55
|
-
const
|
|
51
|
+
const UNIVERSAL_SEARCH_PATH = "/api/search/universal";
|
|
52
|
+
|
|
53
|
+
function toHits(
|
|
54
|
+
results: Array<{ score: number; data: Record<string, unknown> }>,
|
|
55
|
+
index: string | null,
|
|
56
|
+
): SearchHit[] {
|
|
57
|
+
return results.map((result, resultIndex) => ({
|
|
58
|
+
id: String(result.data.id ?? Object.values(result.data)[0] ?? resultIndex),
|
|
59
|
+
score: result.score,
|
|
60
|
+
fields: result.data,
|
|
61
|
+
...(index ? { index } : {}),
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
56
64
|
|
|
57
65
|
/**
|
|
58
|
-
* Search-as-you-type against
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* for the base path, default index, and page size.
|
|
66
|
+
* Search-as-you-type against AppKit AI Search. Debounces input, cancels stale
|
|
67
|
+
* native queries, and exposes `{ query, setQuery, hits, loading, error }` for a
|
|
68
|
+
* search box to render.
|
|
62
69
|
*/
|
|
63
70
|
export function useSearch(options: UseSearchOptions = {}): UseSearchState {
|
|
64
|
-
const
|
|
65
|
-
const config = usePluginClientConfig<SearchClientConfig>(pluginName);
|
|
66
|
-
const basePath = config?.basePath ?? DEFAULT_BASE_PATH;
|
|
71
|
+
const native = useAiSearchQuery({ ...(options.index ? { alias: options.index } : {}) });
|
|
67
72
|
const debounceMs = options.debounceMs ?? 200;
|
|
68
73
|
const minLength = options.minLength ?? 1;
|
|
69
74
|
|
|
70
75
|
const [query, setQueryState] = useState("");
|
|
71
76
|
const [hits, setHits] = useState<SearchHit[]>([]);
|
|
72
|
-
const [
|
|
73
|
-
const [
|
|
77
|
+
const [universalLoading, setUniversalLoading] = useState(false);
|
|
78
|
+
const [universalError, setUniversalError] = useState<string | null>(null);
|
|
74
79
|
|
|
75
80
|
const abortRef = useRef<AbortController | null>(null);
|
|
76
81
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
@@ -80,31 +85,33 @@ export function useSearch(options: UseSearchOptions = {}): UseSearchState {
|
|
|
80
85
|
abortRef.current?.abort();
|
|
81
86
|
if (text.trim().length < minLength) {
|
|
82
87
|
setHits([]);
|
|
83
|
-
|
|
88
|
+
setUniversalLoading(false);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!options.universal) {
|
|
92
|
+
const resolvedQueryType = sharedSearch.toAiSearchQueryType(options.mode);
|
|
93
|
+
const request: AiSearchRequest = {
|
|
94
|
+
queryText: text,
|
|
95
|
+
...(options.limit ? { numResults: options.limit } : {}),
|
|
96
|
+
...(resolvedQueryType ? { queryType: resolvedQueryType } : {}),
|
|
97
|
+
};
|
|
98
|
+
const result = await native.search(request);
|
|
99
|
+
setHits(result ? toHits(result.results, native.alias) : []);
|
|
84
100
|
return;
|
|
85
101
|
}
|
|
86
102
|
const controller = new AbortController();
|
|
87
103
|
abortRef.current = controller;
|
|
88
|
-
|
|
89
|
-
|
|
104
|
+
setUniversalLoading(true);
|
|
105
|
+
setUniversalError(null);
|
|
90
106
|
try {
|
|
91
|
-
const
|
|
92
|
-
const body = options.universal
|
|
93
|
-
? {
|
|
94
|
-
query: text,
|
|
95
|
-
...(options.limit ? { limit: options.limit } : {}),
|
|
96
|
-
...(options.mode ? { mode: options.mode } : {}),
|
|
97
|
-
}
|
|
98
|
-
: {
|
|
99
|
-
query: text,
|
|
100
|
-
...(options.index ? { index: options.index } : {}),
|
|
101
|
-
...(options.limit ? { limit: options.limit } : {}),
|
|
102
|
-
...(options.mode ? { mode: options.mode } : {}),
|
|
103
|
-
};
|
|
104
|
-
const response = await fetch(path, {
|
|
107
|
+
const response = await fetch(UNIVERSAL_SEARCH_PATH, {
|
|
105
108
|
method: "POST",
|
|
106
109
|
headers: { "content-type": "application/json" },
|
|
107
|
-
body: JSON.stringify(
|
|
110
|
+
body: JSON.stringify({
|
|
111
|
+
query: text,
|
|
112
|
+
...(options.limit ? { limit: options.limit } : {}),
|
|
113
|
+
...(options.mode ? { mode: options.mode } : {}),
|
|
114
|
+
}),
|
|
108
115
|
signal: controller.signal,
|
|
109
116
|
});
|
|
110
117
|
if (!response.ok) {
|
|
@@ -114,13 +121,13 @@ export function useSearch(options: UseSearchOptions = {}): UseSearchState {
|
|
|
114
121
|
setHits(result.hits ?? []);
|
|
115
122
|
} catch (err) {
|
|
116
123
|
if ((err as Error).name === "AbortError") return;
|
|
117
|
-
|
|
124
|
+
setUniversalError((err as Error).message);
|
|
118
125
|
setHits([]);
|
|
119
126
|
} finally {
|
|
120
|
-
if (abortRef.current === controller)
|
|
127
|
+
if (abortRef.current === controller) setUniversalLoading(false);
|
|
121
128
|
}
|
|
122
129
|
},
|
|
123
|
-
[
|
|
130
|
+
[minLength, native.alias, native.search, options.limit, options.mode, options.universal],
|
|
124
131
|
);
|
|
125
132
|
|
|
126
133
|
const setQuery = useCallback(
|
|
@@ -142,8 +149,8 @@ export function useSearch(options: UseSearchOptions = {}): UseSearchState {
|
|
|
142
149
|
abortRef.current?.abort();
|
|
143
150
|
setQueryState("");
|
|
144
151
|
setHits([]);
|
|
145
|
-
|
|
146
|
-
|
|
152
|
+
setUniversalError(null);
|
|
153
|
+
setUniversalLoading(false);
|
|
147
154
|
}, []);
|
|
148
155
|
|
|
149
156
|
useEffect(
|
|
@@ -154,6 +161,21 @@ export function useSearch(options: UseSearchOptions = {}): UseSearchState {
|
|
|
154
161
|
[],
|
|
155
162
|
);
|
|
156
163
|
|
|
164
|
+
const config = useMemo<SearchClientConfig>(
|
|
165
|
+
() => ({
|
|
166
|
+
indexes: native.indexes.map((index) => ({
|
|
167
|
+
name: index.alias,
|
|
168
|
+
alias: index.alias,
|
|
169
|
+
isDefault: index.alias === native.alias,
|
|
170
|
+
})),
|
|
171
|
+
...(native.alias ? { defaultIndex: native.alias } : {}),
|
|
172
|
+
pageSize: options.limit ?? 20,
|
|
173
|
+
basePath: "/api/ai-search",
|
|
174
|
+
}),
|
|
175
|
+
[native.alias, native.indexes, options.limit],
|
|
176
|
+
);
|
|
177
|
+
const loading = options.universal ? universalLoading : native.loading;
|
|
178
|
+
const error = options.universal ? universalError : native.error;
|
|
157
179
|
return useMemo(
|
|
158
180
|
() => ({ query, setQuery, hits, loading, error, config, submit, clear }),
|
|
159
181
|
[query, setQuery, hits, loading, error, config, submit, clear],
|