@sanity/embeddings-index-ui 1.0.2 → 1.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.
package/dist/index.esm.js CHANGED
@@ -1,10 +1,57 @@
1
- import { useSchema, DefaultPreview, useDocumentPreviewStore, SanityDefaultPreview, getPreviewValueWithFallback, getPreviewStateObservable, useClient, typed, unset, setIfMissing, set, definePlugin, isObjectInputProps } from 'sanity';
1
+ import { useClient, useSchema, DefaultPreview, useDocumentPreviewStore, SanityDefaultPreview, getPreviewValueWithFallback, getPreviewStateObservable, typed, unset, setIfMissing, set, definePlugin, isObjectInputProps } from 'sanity';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
- import { Card, Button, Box, Flex, Autocomplete, Text, Stack, Label, TextInput, TextArea, Dialog, Spinner, Heading, MenuButton, Menu, MenuItem } from '@sanity/ui';
4
- import { ErrorOutlineIcon, EarthGlobeIcon, LinkIcon, AddIcon, SearchIcon, EllipsisVerticalIcon, TrashIcon, UndoIcon } from '@sanity/icons';
5
- import require$$0, { useMemo, useRef, useEffect, useState, useCallback, useId } from 'react';
6
- import { useIntentLink } from 'sanity/router';
3
+ import { Card, Text, Button, Box, Autocomplete, Flex, Spinner, Stack, Label, TextInput, TextArea, Dialog, Heading, MenuButton, Menu, MenuItem } from '@sanity/ui';
4
+ import { ErrorOutlineIcon, EarthGlobeIcon, LinkIcon, AddIcon, EllipsisVerticalIcon, TrashIcon, UndoIcon } from '@sanity/icons';
5
+ import require$$0, { createContext, useState, useEffect, useContext, useMemo, useRef, forwardRef, useId, useCallback } from 'react';
7
6
  import { useDocumentPane } from 'sanity/desk';
7
+ import { useIntentLink } from 'sanity/router';
8
+ function publicId(id) {
9
+ return id.replace("drafts.", "");
10
+ }
11
+ const featureName = "embeddingsIndexApi";
12
+ const FeatureEnabledContext = createContext("loading");
13
+ function useIsFeatureEnabled() {
14
+ const client = useClient({
15
+ apiVersion: "2023-09-01"
16
+ });
17
+ const [status, setStatus] = useState("loading");
18
+ useEffect(() => {
19
+ client.request({
20
+ method: "GET",
21
+ url: "/projects/".concat(client.config().projectId, "/features/").concat(featureName)
22
+ }).then(isEnabled => {
23
+ setStatus(isEnabled === "true" || isEnabled === true ? "enabled" : "disabled");
24
+ }).catch(err => {
25
+ console.error(err);
26
+ setStatus("disabled");
27
+ });
28
+ }, [client]);
29
+ return status;
30
+ }
31
+ function FeatureEnabledProvider(props) {
32
+ const status = useIsFeatureEnabled();
33
+ return /* @__PURE__ */jsx(FeatureEnabledContext.Provider, {
34
+ value: status,
35
+ children: props.children
36
+ });
37
+ }
38
+ function useIsFeatureEnabledContext() {
39
+ return useContext(FeatureEnabledContext);
40
+ }
41
+ function FeatureDisabledNotice() {
42
+ return /* @__PURE__ */jsx(Card, {
43
+ tone: "primary",
44
+ border: true,
45
+ padding: 2,
46
+ children: /* @__PURE__ */jsxs(Text, {
47
+ size: 1,
48
+ children: ["Embeddings index APIs are only available on the", " ", /* @__PURE__ */jsx("a", {
49
+ href: "https://sanity.io/pricing",
50
+ children: "Team tier and above"
51
+ }), ". Please upgrade to enable access."]
52
+ })
53
+ });
54
+ }
8
55
 
9
56
  /******************************************************************************
10
57
  Copyright (c) Microsoft Corporation.
@@ -1876,9 +1923,6 @@ function deleteIndex(indexName, client) {
1876
1923
  url: "/embeddings-index/".concat(dataset, "/").concat(indexName, "?projectId=").concat(projectId)
1877
1924
  });
1878
1925
  }
1879
- function publicId(id) {
1880
- return id.replace("drafts.", "");
1881
- }
1882
1926
  function useApiClient() {
1883
1927
  const client = useClient({
1884
1928
  apiVersion: "vX"
@@ -1895,59 +1939,20 @@ function useApiClient() {
1895
1939
  return client;
1896
1940
  }, [client]);
1897
1941
  }
1942
+ const NO_RESULTS_VALUE = "";
1898
1943
  const NO_OPTIONS = [];
1899
1944
  const NO_FILTER = () => true;
1900
- function SemanticSearchReferenceInput(props) {
1901
- var _a, _b, _c;
1902
- const defaultEnabled = ((_c = (_b = (_a = props.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.embeddingsIndex) == null ? void 0 : _c.searchMode) === "embeddings";
1903
- const [semantic, setSemantic] = useState(defaultEnabled);
1904
- const toggleSemantic = useCallback(() => setSemantic(current => !current), []);
1905
- return /* @__PURE__ */jsxs(Flex, {
1906
- gap: 2,
1907
- flex: 1,
1908
- style: {
1909
- width: "100%"
1910
- },
1911
- children: [/* @__PURE__ */jsx(Box, {
1912
- flex: 1,
1913
- style: {
1914
- maxHeight: 36,
1915
- overflow: "hidden"
1916
- },
1917
- children: semantic ? /* @__PURE__ */jsx(SemanticSearchInput, {
1918
- ...props
1919
- }) : props.renderDefault(props)
1920
- }), /* @__PURE__ */jsx(Button, {
1921
- icon: semantic ? EarthGlobeIcon : LinkIcon,
1922
- onClick: toggleSemantic,
1923
- mode: "bleed",
1924
- title: semantic ? "Switch to standard reference search" : "Switch to semantic reference search"
1925
- })]
1926
- });
1927
- }
1928
- function useDebouncedValue(value, ms) {
1929
- const [debouncedValue, setDebouncedValue] = useState(value);
1930
- useEffect(() => {
1931
- const timeoutId = setTimeout(() => {
1932
- setDebouncedValue(value);
1933
- }, ms);
1934
- return () => clearTimeout(timeoutId);
1935
- }, [value, ms]);
1936
- return debouncedValue;
1937
- }
1938
- function SemanticSearchInput(props) {
1945
+ const SemanticSearchAutocomplete = forwardRef(function SemanticSearchAutocomplete2(props, ref) {
1939
1946
  const {
1940
- onPathFocus,
1941
- onChange,
1947
+ indexConfig,
1948
+ filterResult,
1949
+ getEmptySearchValue,
1942
1950
  readOnly,
1943
- schemaType,
1944
- value
1951
+ onFocus,
1952
+ onBlur,
1953
+ onChange,
1954
+ typeFilter
1945
1955
  } = props;
1946
- const {
1947
- value: currentDocument
1948
- } = useDocumentPane();
1949
- const docRef = useRef(currentDocument);
1950
- const autocompleteRef = useRef(null);
1951
1956
  const id = useId();
1952
1957
  const [query, setQuery] = useState("");
1953
1958
  const queryRef = useRef(query);
@@ -1956,29 +1961,16 @@ function SemanticSearchInput(props) {
1956
1961
  const [searching, setSearching] = useState(false);
1957
1962
  const [options, setOptions] = useState(NO_OPTIONS);
1958
1963
  const client = useApiClient();
1959
- useEffect(() => {
1960
- docRef.current = currentDocument;
1961
- }, [currentDocument]);
1962
- useEffect(() => {
1963
- var _a;
1964
- if (value == null ? void 0 : value._ref) {
1965
- (_a = autocompleteRef.current) == null ? void 0 : _a.focus();
1966
- }
1967
- }, []);
1968
- const handleFocus = useCallback(() => onPathFocus(["_ref"]), [onPathFocus]);
1969
- const handleBlur = useCallback(() => onPathFocus([]), [onPathFocus]);
1970
1964
  const runIndexQuery = useCallback(queryString => {
1971
- var _a, _b, _c, _d, _e;
1965
+ var _a;
1972
1966
  setSearching(true);
1973
- const refSchema = schemaType;
1974
- const indexName = (_b = (_a = refSchema.options) == null ? void 0 : _a.embeddingsIndex) == null ? void 0 : _b.indexName;
1975
- const maxResults = (_d = (_c = refSchema.options) == null ? void 0 : _c.embeddingsIndex) == null ? void 0 : _d.maxResults;
1976
- const typeFilter = refSchema.to.map(ref => ref.name);
1967
+ const indexName = indexConfig == null ? void 0 : indexConfig.indexName;
1968
+ const maxResults = indexConfig == null ? void 0 : indexConfig.maxResults;
1977
1969
  if (!indexName) {
1978
- throw new Error("Reference option embeddingsIndex.indexName is required, but was missing in type ".concat(refSchema.name));
1970
+ throw new Error("Reference option embeddingsIndex.indexName is required, but was missing");
1979
1971
  }
1980
1972
  queryIndex({
1981
- query: queryString.trim().length ? queryString : (_e = JSON.stringify(docRef.current)) != null ? _e : "",
1973
+ query: queryString.trim().length ? queryString : (_a = getEmptySearchValue()) != null ? _a : "",
1982
1974
  indexName,
1983
1975
  maxResults,
1984
1976
  filter: {
@@ -1987,10 +1979,18 @@ function SemanticSearchInput(props) {
1987
1979
  }, client).then(result => {
1988
1980
  if (queryRef.current === queryString) {
1989
1981
  setSearching(false);
1990
- setOptions(result.filter(r => r.value.documentId !== publicId(docRef.current._id)).map(r => typed({
1982
+ setOptions([]);
1983
+ const resultOptions = result.filter(hit => filterResult ? filterResult(hit) : true).map(r => typed({
1991
1984
  result: r,
1992
1985
  value: r.value.documentId
1993
- })));
1986
+ }));
1987
+ if (resultOptions.length) {
1988
+ setOptions(resultOptions);
1989
+ } else {
1990
+ setOptions([{
1991
+ value: NO_RESULTS_VALUE
1992
+ }]);
1993
+ }
1994
1994
  }
1995
1995
  }).catch(e => {
1996
1996
  if (queryRef.current === queryString) {
@@ -1998,23 +1998,13 @@ function SemanticSearchInput(props) {
1998
1998
  }
1999
1999
  throw e;
2000
2000
  });
2001
- }, [client, schemaType]);
2001
+ }, [client, indexConfig, getEmptySearchValue, filterResult, typeFilter]);
2002
2002
  useEffect(() => {
2003
2003
  if (prevDebouncedQuery.current !== debouncedQuery) {
2004
2004
  runIndexQuery(debouncedQuery);
2005
2005
  }
2006
2006
  prevDebouncedQuery.current = debouncedQuery;
2007
2007
  }, [debouncedQuery, runIndexQuery]);
2008
- const handleChange = useCallback(nextId => {
2009
- if (!nextId) {
2010
- onChange(unset());
2011
- onPathFocus([]);
2012
- return;
2013
- }
2014
- const patches = [setIfMissing({}), set(schemaType.name, ["_type"]), set(publicId(nextId), ["_ref"]), unset(["_weak"]), unset(["_strengthenOnPublish"])];
2015
- onChange(patches);
2016
- onPathFocus([]);
2017
- }, [onChange, onPathFocus, schemaType.name]);
2018
2008
  const openButtonConfig = useMemo(() => ({
2019
2009
  onClick: () => runIndexQuery(queryRef.current)
2020
2010
  }), [runIndexQuery, queryRef]);
@@ -2023,52 +2013,187 @@ function SemanticSearchInput(props) {
2023
2013
  queryRef.current = newQuery;
2024
2014
  setQuery(newQuery);
2025
2015
  }, [setQuery]);
2016
+ const handleChange = useCallback(value => {
2017
+ if (value === NO_RESULTS_VALUE) {
2018
+ setOptions(NO_OPTIONS);
2019
+ return;
2020
+ }
2021
+ onChange == null ? void 0 : onChange(value);
2022
+ }, [onChange]);
2026
2023
  return /* @__PURE__ */jsx(Autocomplete, {
2027
2024
  id,
2028
- ref: autocompleteRef,
2025
+ ref,
2029
2026
  "data-testid": "semantic-autocomplete",
2030
2027
  placeholder: "Type to search...",
2031
2028
  openButton: openButtonConfig,
2032
- onFocus: handleFocus,
2029
+ onFocus,
2033
2030
  onChange: handleChange,
2034
2031
  loading: searching,
2035
- onBlur: handleBlur,
2032
+ onBlur,
2036
2033
  readOnly,
2037
2034
  filterOption: NO_FILTER,
2038
2035
  onQueryChange: handleQueryChange,
2039
2036
  options,
2040
2037
  renderOption: AutocompleteOption
2041
2038
  });
2042
- }
2039
+ });
2043
2040
  function AutocompleteOption(props) {
2044
- const value = props.result.value;
2041
+ if ("result" in props) {
2042
+ const value = props.result.value;
2043
+ return /* @__PURE__ */jsx(Button, {
2044
+ mode: "bleed",
2045
+ padding: 1,
2046
+ style: {
2047
+ width: "100%"
2048
+ },
2049
+ children: /* @__PURE__ */jsxs(Flex, {
2050
+ gap: 2,
2051
+ align: "center",
2052
+ children: [/* @__PURE__ */jsx(Box, {
2053
+ flex: 1,
2054
+ children: /* @__PURE__ */jsx(DocumentPreview, {
2055
+ documentId: value.documentId,
2056
+ schemaTypeName: value.type
2057
+ })
2058
+ }), /* @__PURE__ */jsx(Box, {
2059
+ padding: 2,
2060
+ children: /* @__PURE__ */jsxs(Text, {
2061
+ size: 1,
2062
+ muted: true,
2063
+ title: "Relevance",
2064
+ children: [Math.floor(props.result.score * 100), "%"]
2065
+ })
2066
+ })]
2067
+ })
2068
+ });
2069
+ }
2045
2070
  return /* @__PURE__ */jsx(Button, {
2046
2071
  mode: "bleed",
2047
2072
  padding: 1,
2048
2073
  style: {
2049
2074
  width: "100%"
2050
2075
  },
2051
- children: /* @__PURE__ */jsxs(Flex, {
2076
+ disabled: true,
2077
+ children: /* @__PURE__ */jsx(Flex, {
2052
2078
  gap: 2,
2053
2079
  align: "center",
2054
- children: [/* @__PURE__ */jsx(Box, {
2055
- flex: 1,
2056
- children: /* @__PURE__ */jsx(DocumentPreview, {
2057
- documentId: value.documentId,
2058
- schemaTypeName: value.type
2059
- })
2060
- }), /* @__PURE__ */jsx(Box, {
2061
- padding: 2,
2062
- children: /* @__PURE__ */jsxs(Text, {
2063
- size: 1,
2064
- muted: true,
2065
- title: "Relevance",
2066
- children: [Math.floor(props.result.score * 100), "%"]
2067
- })
2068
- })]
2080
+ children: "No results."
2069
2081
  })
2070
2082
  });
2071
2083
  }
2084
+ function useDebouncedValue(value, ms) {
2085
+ const [debouncedValue, setDebouncedValue] = useState(value);
2086
+ useEffect(() => {
2087
+ const timeoutId = setTimeout(() => {
2088
+ setDebouncedValue(value);
2089
+ }, ms);
2090
+ return () => clearTimeout(timeoutId);
2091
+ }, [value, ms]);
2092
+ return debouncedValue;
2093
+ }
2094
+ function useEmeddingsConfig(embeddingsIndexConfig, defaultConfig) {
2095
+ return useMemo(() => {
2096
+ if (embeddingsIndexConfig === true || !embeddingsIndexConfig) {
2097
+ if (!(defaultConfig == null ? void 0 : defaultConfig.indexName)) {
2098
+ throw new Error("Default embeddingsIndex config is missing. When options.embeddingsIndex: true, embeddingsIndexReferenceInput plugin config is required.");
2099
+ }
2100
+ return defaultConfig;
2101
+ }
2102
+ const finalConfig = {
2103
+ ...defaultConfig,
2104
+ ...embeddingsIndexConfig
2105
+ };
2106
+ if (!(finalConfig == null ? void 0 : finalConfig.indexName)) {
2107
+ throw new Error("indexName is missing. Either set it in options.embeddingsIndex or configure defaults using plugin config.");
2108
+ }
2109
+ return finalConfig;
2110
+ }, [defaultConfig, embeddingsIndexConfig]);
2111
+ }
2112
+ function SemanticSearchReferenceInput(props) {
2113
+ var _a, _b;
2114
+ const embeddingsIndexConfig = (_b = (_a = props.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.embeddingsIndex;
2115
+ const config = useEmeddingsConfig(embeddingsIndexConfig, props.defaultConfig);
2116
+ const defaultEnabled = config.searchMode === "embeddings";
2117
+ const featureState = useIsFeatureEnabledContext();
2118
+ const [semantic, setSemantic] = useState(defaultEnabled);
2119
+ const toggleSemantic = useCallback(() => setSemantic(current => !current), []);
2120
+ return /* @__PURE__ */jsxs(Flex, {
2121
+ gap: 2,
2122
+ flex: 1,
2123
+ style: {
2124
+ width: "100%"
2125
+ },
2126
+ children: [semantic && featureState == "loading" ? /* @__PURE__ */jsx(Box, {
2127
+ padding: 2,
2128
+ children: /* @__PURE__ */jsx(Spinner, {})
2129
+ }) : null, semantic && featureState == "disabled" ? /* @__PURE__ */jsx(FeatureDisabledNotice, {}) : null, /* @__PURE__ */jsx(Box, {
2130
+ flex: 1,
2131
+ style: {
2132
+ maxHeight: 36,
2133
+ overflow: "hidden"
2134
+ },
2135
+ children: semantic && featureState == "enabled" ? /* @__PURE__ */jsx(SemanticSearchInput, {
2136
+ ...props,
2137
+ indexConfig: config
2138
+ }) : props.renderDefault(props)
2139
+ }), /* @__PURE__ */jsx(Button, {
2140
+ icon: semantic ? EarthGlobeIcon : LinkIcon,
2141
+ onClick: toggleSemantic,
2142
+ mode: "bleed",
2143
+ title: semantic ? "Switch to standard reference search" : "Switch to semantic reference search"
2144
+ })]
2145
+ });
2146
+ }
2147
+ function SemanticSearchInput(props) {
2148
+ const {
2149
+ indexConfig,
2150
+ onPathFocus,
2151
+ onChange,
2152
+ readOnly,
2153
+ schemaType,
2154
+ value
2155
+ } = props;
2156
+ const {
2157
+ value: currentDocument
2158
+ } = useDocumentPane();
2159
+ const docRef = useRef(currentDocument);
2160
+ const autocompleteRef = useRef(null);
2161
+ useEffect(() => {
2162
+ docRef.current = currentDocument;
2163
+ }, [currentDocument]);
2164
+ useEffect(() => {
2165
+ var _a;
2166
+ if (value == null ? void 0 : value._ref) {
2167
+ (_a = autocompleteRef.current) == null ? void 0 : _a.focus();
2168
+ }
2169
+ }, []);
2170
+ const handleFocus = useCallback(() => onPathFocus(["_ref"]), [onPathFocus]);
2171
+ const handleBlur = useCallback(() => onPathFocus([]), [onPathFocus]);
2172
+ const handleChange = useCallback(nextId => {
2173
+ if (!nextId) {
2174
+ onChange(unset());
2175
+ onPathFocus([]);
2176
+ return;
2177
+ }
2178
+ const patches = [setIfMissing({}), set(schemaType.name, ["_type"]), set(publicId(nextId), ["_ref"]), unset(["_weak"]), unset(["_strengthenOnPublish"])];
2179
+ onChange(patches);
2180
+ onPathFocus([]);
2181
+ }, [onChange, onPathFocus, schemaType.name]);
2182
+ const filterResult = useCallback(r => r.value.documentId !== publicId(docRef.current._id), [docRef]);
2183
+ const getEmptySearchValue = useCallback(() => JSON.stringify(docRef.current), [docRef]);
2184
+ const typeFilter = useMemo(() => schemaType.to.map(refType => refType.name), [schemaType]);
2185
+ return /* @__PURE__ */jsx(SemanticSearchAutocomplete, {
2186
+ ref: autocompleteRef,
2187
+ typeFilter,
2188
+ indexConfig,
2189
+ onChange: handleChange,
2190
+ onFocus: handleFocus,
2191
+ onBlur: handleBlur,
2192
+ getEmptySearchValue,
2193
+ filterResult,
2194
+ readOnly
2195
+ });
2196
+ }
2072
2197
  function isType(schemaType, typeName) {
2073
2198
  if (schemaType.name === typeName) {
2074
2199
  return true;
@@ -2078,21 +2203,35 @@ function isType(schemaType, typeName) {
2078
2203
  }
2079
2204
  return isType(schemaType.type, typeName);
2080
2205
  }
2081
- const embeddingsIndexReferenceInput = definePlugin({
2082
- name: "@sanity/embeddings-index-reference-input",
2083
- form: {
2084
- components: {
2085
- input: props => {
2086
- var _a, _b;
2087
- if (isObjectInputProps(props) && isType(props.schemaType, "reference") && ((_b = (_a = props.schemaType.options) == null ? void 0 : _a.embeddingsIndex) == null ? void 0 : _b.indexName)) {
2088
- return /* @__PURE__ */jsx(SemanticSearchReferenceInput, {
2089
- ...props
2206
+ const embeddingsIndexReferenceInput = definePlugin(defaultConfig => {
2207
+ const config = typeof defaultConfig === "object" ? defaultConfig : void 0;
2208
+ return {
2209
+ name: "@sanity/embeddings-index-reference-input",
2210
+ studio: {
2211
+ components: {
2212
+ layout: props => {
2213
+ return /* @__PURE__ */jsx(FeatureEnabledProvider, {
2214
+ children: props.renderDefault(props)
2090
2215
  });
2091
2216
  }
2092
- return props.renderDefault(props);
2217
+ }
2218
+ },
2219
+ form: {
2220
+ components: {
2221
+ input: props => {
2222
+ var _a, _b;
2223
+ const embeddingsIndexConfig = (_b = (_a = props.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.embeddingsIndex;
2224
+ if (isObjectInputProps(props) && isType(props.schemaType, "reference") && (embeddingsIndexConfig === true || (embeddingsIndexConfig == null ? void 0 : embeddingsIndexConfig.indexName))) {
2225
+ return /* @__PURE__ */jsx(SemanticSearchReferenceInput, {
2226
+ ...props,
2227
+ defaultConfig: config
2228
+ });
2229
+ }
2230
+ return props.renderDefault(props);
2231
+ }
2093
2232
  }
2094
2233
  }
2095
- }
2234
+ };
2096
2235
  });
2097
2236
  const defaultProjection = "{...}";
2098
2237
  function useDefaultIndex(schema, dataset) {
@@ -2107,6 +2246,7 @@ function IndexFormInput(props) {
2107
2246
  var _a;
2108
2247
  const {
2109
2248
  label,
2249
+ description,
2110
2250
  index,
2111
2251
  prop,
2112
2252
  onChange,
@@ -2120,6 +2260,7 @@ function IndexFormInput(props) {
2120
2260
  })), [onChange, prop]);
2121
2261
  return /* @__PURE__ */jsx(FormInput, {
2122
2262
  label,
2263
+ description,
2123
2264
  onChange: handleChange,
2124
2265
  value: (_a = index[prop]) != null ? _a : "",
2125
2266
  readOnly,
@@ -2130,6 +2271,7 @@ function IndexFormInput(props) {
2130
2271
  function FormInput(props) {
2131
2272
  const {
2132
2273
  label,
2274
+ description,
2133
2275
  onChange,
2134
2276
  value,
2135
2277
  readOnly,
@@ -2147,6 +2289,12 @@ function FormInput(props) {
2147
2289
  htmlFor: id,
2148
2290
  children: label
2149
2291
  })
2292
+ }), description && /* @__PURE__ */jsx(Box, {
2293
+ children: /* @__PURE__ */jsx(Text, {
2294
+ size: 1,
2295
+ muted: true,
2296
+ children: description
2297
+ })
2150
2298
  }), type === "text" ? /* @__PURE__ */jsx(TextInput, {
2151
2299
  id,
2152
2300
  value,
@@ -2301,18 +2449,20 @@ function IndexEditor(props) {
2301
2449
  onChange: setIndex,
2302
2450
  readOnly: true
2303
2451
  }), /* @__PURE__ */jsx(IndexFormInput, {
2304
- label: "Projection",
2305
- placeholder: defaultIndex.projection,
2452
+ label: "Filter",
2453
+ description: "Must be a valid GROQ filter",
2454
+ placeholder: defaultIndex.filter,
2306
2455
  index,
2307
- prop: "projection",
2456
+ prop: "filter",
2308
2457
  onChange: setIndex,
2309
2458
  readOnly,
2310
2459
  type: "textarea"
2311
2460
  }), /* @__PURE__ */jsx(IndexFormInput, {
2312
- label: "Filter",
2313
- placeholder: defaultIndex.filter,
2461
+ label: "Projection",
2462
+ description: "Must be a valid GROQ projection, starting { and ending with }",
2463
+ placeholder: defaultIndex.projection,
2314
2464
  index,
2315
- prop: "filter",
2465
+ prop: "projection",
2316
2466
  onChange: setIndex,
2317
2467
  readOnly,
2318
2468
  type: "textarea"
@@ -2349,6 +2499,7 @@ function IndexList(props) {
2349
2499
  borderBottom: true,
2350
2500
  flex: 1,
2351
2501
  paddingBottom: 2,
2502
+ padding: 3,
2352
2503
  children: /* @__PURE__ */jsxs(Flex, {
2353
2504
  children: [/* @__PURE__ */jsx(Box, {
2354
2505
  flex: 1,
@@ -2403,12 +2554,15 @@ function IndexRow(props) {
2403
2554
  const onSelect = useCallback(() => onIndexSelected(index), [onIndexSelected, index]);
2404
2555
  return /* @__PURE__ */jsx(Button, {
2405
2556
  tone: (selectedIndex == null ? void 0 : selectedIndex.indexName) === index.indexName ? "primary" : "default",
2406
- mode: (selectedIndex == null ? void 0 : selectedIndex.indexName) === index.indexName ? "default" : "bleed",
2557
+ mode: (selectedIndex == null ? void 0 : selectedIndex.indexName) === index.indexName ? "default" : "ghost",
2407
2558
  onClick: onSelect,
2559
+ padding: 3,
2408
2560
  children: /* @__PURE__ */jsxs(Flex, {
2409
2561
  children: [/* @__PURE__ */jsx(Box, {
2410
2562
  flex: 1,
2411
- children: index.indexName
2563
+ children: /* @__PURE__ */jsx("strong", {
2564
+ children: index.indexName
2565
+ })
2412
2566
  }), /* @__PURE__ */jsx(Box, {
2413
2567
  flex: 1,
2414
2568
  children: index.dataset
@@ -2422,102 +2576,18 @@ function IndexRow(props) {
2422
2576
  })
2423
2577
  }, index.indexName);
2424
2578
  }
2425
- const NO_RESULTS = [];
2426
2579
  function QueryIndex(props) {
2427
2580
  const {
2428
2581
  indexName
2429
2582
  } = props;
2430
- const [query, setQuery] = useState("");
2431
- const [searching, setSearching] = useState(false);
2432
- const [results, setResults] = useState(NO_RESULTS);
2433
- const client = useApiClient();
2434
- const search = useCallback(queryString => {
2435
- setSearching(true);
2436
- return queryIndex({
2437
- query: queryString,
2438
- indexName,
2439
- maxResults: 5
2440
- }, client).then(setResults).finally(() => setSearching(false));
2441
- }, [client, indexName]);
2442
- const onInputChange = useCallback(e => {
2443
- setQuery(e.currentTarget.value);
2444
- }, []);
2445
- const onKeyDown = useCallback(e => {
2446
- if (e.key === "Enter") {
2447
- search(query).catch(console.error);
2448
- }
2449
- }, [search, query]);
2450
- return /* @__PURE__ */jsxs(Stack, {
2451
- space: 3,
2452
- flex: 1,
2453
- children: [/* @__PURE__ */jsx(Flex, {
2454
- flex: 1,
2455
- children: /* @__PURE__ */jsx(Card, {
2456
- flex: 1,
2457
- children: /* @__PURE__ */jsx(TextInput, {
2458
- iconRight: searching ? /* @__PURE__ */jsx(Box, {
2459
- style: {
2460
- marginTop: 5
2461
- },
2462
- children: /* @__PURE__ */jsx(Spinner, {})
2463
- }) : SearchIcon,
2464
- placeholder: "Find documents",
2465
- value: query,
2466
- disabled: searching,
2467
- onChange: onInputChange,
2468
- onKeyDown
2469
- })
2470
- })
2471
- }), /* @__PURE__ */jsx(Flex, {
2472
- gap: 4,
2473
- style: {
2474
- opacity: searching ? 0.5 : 1
2475
- },
2476
- children: /* @__PURE__ */jsx(Box, {
2477
- flex: 1,
2478
- children: /* @__PURE__ */jsx(ResultList, {
2479
- results,
2480
- query
2481
- })
2482
- })
2483
- })]
2484
- });
2485
- }
2486
- function ResultList(props) {
2487
- const {
2488
- results,
2489
- query
2490
- } = props;
2491
- return /* @__PURE__ */jsx(Stack, {
2492
- space: 4,
2493
- height: "fill",
2494
- children: /* @__PURE__ */jsxs(Stack, {
2495
- space: 2,
2496
- children: [results.map(r => /* @__PURE__ */jsx(ResultEntry, {
2497
- result: r
2498
- }, r.value.documentId)), !results.length && query ? "No results." : null]
2499
- })
2500
- });
2501
- }
2502
- function ResultEntry(props) {
2503
- const value = props.result.value;
2504
- return /* @__PURE__ */jsxs(Flex, {
2505
- gap: 4,
2506
- align: "center",
2507
- children: [/* @__PURE__ */jsx(Box, {
2508
- flex: 1,
2509
- children: /* @__PURE__ */jsx(DocumentPreview, {
2510
- documentId: value.documentId,
2511
- schemaTypeName: value.type,
2512
- button: true
2513
- })
2514
- }), /* @__PURE__ */jsx(Box, {
2515
- children: /* @__PURE__ */jsxs(Text, {
2516
- muted: true,
2517
- size: 1,
2518
- children: [Math.floor(props.result.score * 100), " %"]
2519
- })
2520
- })]
2583
+ const getEmpty = useCallback(() => "anything", []);
2584
+ const indexConfig = useMemo(() => ({
2585
+ indexName,
2586
+ maxResults: 8
2587
+ }), [indexName]);
2588
+ return /* @__PURE__ */jsx(SemanticSearchAutocomplete, {
2589
+ getEmptySearchValue: getEmpty,
2590
+ indexConfig
2521
2591
  });
2522
2592
  }
2523
2593
  function IndexInfo(_ref3) {
@@ -2649,17 +2719,21 @@ function IndexStatus(_ref4) {
2649
2719
  });
2650
2720
  }
2651
2721
  function EmbeddingsIndexTool() {
2722
+ const featureState = useIsFeatureEnabled();
2652
2723
  return /* @__PURE__ */jsx(Card, {
2653
2724
  children: /* @__PURE__ */jsx(Flex, {
2654
2725
  justify: "center",
2655
2726
  flex: 1,
2656
- children: /* @__PURE__ */jsx(Card, {
2727
+ children: /* @__PURE__ */jsxs(Card, {
2657
2728
  flex: 1,
2658
2729
  style: {
2659
2730
  maxWidth: 1200
2660
2731
  },
2661
2732
  padding: 5,
2662
- children: /* @__PURE__ */jsx(Indexes, {})
2733
+ children: [featureState == "loading" ? /* @__PURE__ */jsx(Box, {
2734
+ padding: 2,
2735
+ children: /* @__PURE__ */jsx(Spinner, {})
2736
+ }) : null, featureState == "disabled" ? /* @__PURE__ */jsx(FeatureDisabledNotice, {}) : null, featureState == "enabled" ? /* @__PURE__ */jsx(Indexes, {}) : null]
2663
2737
  })
2664
2738
  })
2665
2739
  });