@sanity/embeddings-index-ui 1.0.3 → 1.1.1

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, Text, Flex, Spinner, Autocomplete, Stack, Label, TextInput, TextArea, Dialog, 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, createContext, useState, useContext, 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, useRouter } 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,107 +1939,20 @@ function useApiClient() {
1895
1939
  return client;
1896
1940
  }, [client]);
1897
1941
  }
1898
- const featureName = "embeddingsIndexApi";
1899
- const FeatureEnabledContext = createContext("loading");
1900
- function useIsFeatureEnabled() {
1901
- const client = useClient({
1902
- apiVersion: "2023-09-01"
1903
- });
1904
- const [status, setStatus] = useState("loading");
1905
- useEffect(() => {
1906
- client.request({
1907
- method: "GET",
1908
- url: "/projects/".concat(client.config().projectId, "/features/").concat(featureName)
1909
- }).then(isEnabled => {
1910
- setStatus(isEnabled === "true" || isEnabled === true ? "enabled" : "disabled");
1911
- }).catch(err => {
1912
- console.error(err);
1913
- setStatus("disabled");
1914
- });
1915
- }, [client]);
1916
- return status;
1917
- }
1918
- function FeatureEnabledProvider(props) {
1919
- const status = useIsFeatureEnabled();
1920
- return /* @__PURE__ */jsx(FeatureEnabledContext.Provider, {
1921
- value: status,
1922
- children: props.children
1923
- });
1924
- }
1925
- function useIsFeatureEnabledContext() {
1926
- return useContext(FeatureEnabledContext);
1927
- }
1928
- function FeatureDisabledNotice() {
1929
- return /* @__PURE__ */jsx(Card, {
1930
- tone: "primary",
1931
- border: true,
1932
- padding: 2,
1933
- children: /* @__PURE__ */jsxs(Text, {
1934
- size: 1,
1935
- children: ["Embeddings index APIs are only available on the", " ", /* @__PURE__ */jsx("a", {
1936
- href: "https://sanity.io/pricing",
1937
- children: "Team tier and above"
1938
- }), ". Please upgrade to enable access."]
1939
- })
1940
- });
1941
- }
1942
+ const NO_RESULTS_VALUE = "";
1942
1943
  const NO_OPTIONS = [];
1943
1944
  const NO_FILTER = () => true;
1944
- function SemanticSearchReferenceInput(props) {
1945
- var _a, _b, _c;
1946
- const defaultEnabled = ((_c = (_b = (_a = props.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.embeddingsIndex) == null ? void 0 : _c.searchMode) === "embeddings";
1947
- const featureState = useIsFeatureEnabledContext();
1948
- const [semantic, setSemantic] = useState(defaultEnabled);
1949
- const toggleSemantic = useCallback(() => setSemantic(current => !current), []);
1950
- return /* @__PURE__ */jsxs(Flex, {
1951
- gap: 2,
1952
- flex: 1,
1953
- style: {
1954
- width: "100%"
1955
- },
1956
- children: [semantic && featureState == "loading" ? /* @__PURE__ */jsx(Box, {
1957
- padding: 2,
1958
- children: /* @__PURE__ */jsx(Spinner, {})
1959
- }) : null, semantic && featureState == "disabled" ? /* @__PURE__ */jsx(FeatureDisabledNotice, {}) : null, /* @__PURE__ */jsx(Box, {
1960
- flex: 1,
1961
- style: {
1962
- maxHeight: 36,
1963
- overflow: "hidden"
1964
- },
1965
- children: semantic && featureState == "enabled" ? /* @__PURE__ */jsx(SemanticSearchInput, {
1966
- ...props
1967
- }) : props.renderDefault(props)
1968
- }), /* @__PURE__ */jsx(Button, {
1969
- icon: semantic ? EarthGlobeIcon : LinkIcon,
1970
- onClick: toggleSemantic,
1971
- mode: "bleed",
1972
- title: semantic ? "Switch to standard reference search" : "Switch to semantic reference search"
1973
- })]
1974
- });
1975
- }
1976
- function useDebouncedValue(value, ms) {
1977
- const [debouncedValue, setDebouncedValue] = useState(value);
1978
- useEffect(() => {
1979
- const timeoutId = setTimeout(() => {
1980
- setDebouncedValue(value);
1981
- }, ms);
1982
- return () => clearTimeout(timeoutId);
1983
- }, [value, ms]);
1984
- return debouncedValue;
1985
- }
1986
- function SemanticSearchInput(props) {
1945
+ const SemanticSearchAutocomplete = forwardRef(function SemanticSearchAutocomplete2(props, ref) {
1987
1946
  const {
1988
- onPathFocus,
1989
- onChange,
1947
+ indexConfig,
1948
+ filterResult,
1949
+ getEmptySearchValue,
1990
1950
  readOnly,
1991
- schemaType,
1992
- value
1951
+ onFocus,
1952
+ onBlur,
1953
+ onSelect,
1954
+ typeFilter
1993
1955
  } = props;
1994
- const {
1995
- value: currentDocument
1996
- } = useDocumentPane();
1997
- const docRef = useRef(currentDocument);
1998
- const autocompleteRef = useRef(null);
1999
1956
  const id = useId();
2000
1957
  const [query, setQuery] = useState("");
2001
1958
  const queryRef = useRef(query);
@@ -2004,29 +1961,16 @@ function SemanticSearchInput(props) {
2004
1961
  const [searching, setSearching] = useState(false);
2005
1962
  const [options, setOptions] = useState(NO_OPTIONS);
2006
1963
  const client = useApiClient();
2007
- useEffect(() => {
2008
- docRef.current = currentDocument;
2009
- }, [currentDocument]);
2010
- useEffect(() => {
2011
- var _a;
2012
- if (value == null ? void 0 : value._ref) {
2013
- (_a = autocompleteRef.current) == null ? void 0 : _a.focus();
2014
- }
2015
- }, []);
2016
- const handleFocus = useCallback(() => onPathFocus(["_ref"]), [onPathFocus]);
2017
- const handleBlur = useCallback(() => onPathFocus([]), [onPathFocus]);
2018
1964
  const runIndexQuery = useCallback(queryString => {
2019
- var _a, _b, _c, _d, _e;
1965
+ var _a;
2020
1966
  setSearching(true);
2021
- const refSchema = schemaType;
2022
- const indexName = (_b = (_a = refSchema.options) == null ? void 0 : _a.embeddingsIndex) == null ? void 0 : _b.indexName;
2023
- const maxResults = (_d = (_c = refSchema.options) == null ? void 0 : _c.embeddingsIndex) == null ? void 0 : _d.maxResults;
2024
- 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;
2025
1969
  if (!indexName) {
2026
- 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");
2027
1971
  }
2028
1972
  queryIndex({
2029
- query: queryString.trim().length ? queryString : (_e = JSON.stringify(docRef.current)) != null ? _e : "",
1973
+ query: queryString.trim().length ? queryString : (_a = getEmptySearchValue()) != null ? _a : "",
2030
1974
  indexName,
2031
1975
  maxResults,
2032
1976
  filter: {
@@ -2035,10 +1979,18 @@ function SemanticSearchInput(props) {
2035
1979
  }, client).then(result => {
2036
1980
  if (queryRef.current === queryString) {
2037
1981
  setSearching(false);
2038
- 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({
2039
1984
  result: r,
2040
1985
  value: r.value.documentId
2041
- })));
1986
+ }));
1987
+ if (resultOptions.length) {
1988
+ setOptions(resultOptions);
1989
+ } else {
1990
+ setOptions([{
1991
+ value: NO_RESULTS_VALUE
1992
+ }]);
1993
+ }
2042
1994
  }
2043
1995
  }).catch(e => {
2044
1996
  if (queryRef.current === queryString) {
@@ -2046,23 +1998,13 @@ function SemanticSearchInput(props) {
2046
1998
  }
2047
1999
  throw e;
2048
2000
  });
2049
- }, [client, schemaType]);
2001
+ }, [client, indexConfig, getEmptySearchValue, filterResult, typeFilter]);
2050
2002
  useEffect(() => {
2051
2003
  if (prevDebouncedQuery.current !== debouncedQuery) {
2052
2004
  runIndexQuery(debouncedQuery);
2053
2005
  }
2054
2006
  prevDebouncedQuery.current = debouncedQuery;
2055
2007
  }, [debouncedQuery, runIndexQuery]);
2056
- const handleChange = useCallback(nextId => {
2057
- if (!nextId) {
2058
- onChange(unset());
2059
- onPathFocus([]);
2060
- return;
2061
- }
2062
- const patches = [setIfMissing({}), set(schemaType.name, ["_type"]), set(publicId(nextId), ["_ref"]), unset(["_weak"]), unset(["_strengthenOnPublish"])];
2063
- onChange(patches);
2064
- onPathFocus([]);
2065
- }, [onChange, onPathFocus, schemaType.name]);
2066
2008
  const openButtonConfig = useMemo(() => ({
2067
2009
  onClick: () => runIndexQuery(queryRef.current)
2068
2010
  }), [runIndexQuery, queryRef]);
@@ -2071,52 +2013,190 @@ function SemanticSearchInput(props) {
2071
2013
  queryRef.current = newQuery;
2072
2014
  setQuery(newQuery);
2073
2015
  }, [setQuery]);
2016
+ const handleChange = useCallback(value => {
2017
+ if (value === NO_RESULTS_VALUE) {
2018
+ setOptions(NO_OPTIONS);
2019
+ return;
2020
+ }
2021
+ const option = options.filter(r => "result" in r).find(r => r.result.value.documentId === value);
2022
+ if (option && onSelect) {
2023
+ onSelect(option.result);
2024
+ }
2025
+ }, [onSelect, options]);
2074
2026
  return /* @__PURE__ */jsx(Autocomplete, {
2075
2027
  id,
2076
- ref: autocompleteRef,
2028
+ ref,
2077
2029
  "data-testid": "semantic-autocomplete",
2078
2030
  placeholder: "Type to search...",
2079
2031
  openButton: openButtonConfig,
2080
- onFocus: handleFocus,
2032
+ onFocus,
2081
2033
  onChange: handleChange,
2082
2034
  loading: searching,
2083
- onBlur: handleBlur,
2035
+ onBlur,
2084
2036
  readOnly,
2085
2037
  filterOption: NO_FILTER,
2086
2038
  onQueryChange: handleQueryChange,
2087
2039
  options,
2088
2040
  renderOption: AutocompleteOption
2089
2041
  });
2090
- }
2042
+ });
2091
2043
  function AutocompleteOption(props) {
2092
- const value = props.result.value;
2044
+ if ("result" in props) {
2045
+ const value = props.result.value;
2046
+ return /* @__PURE__ */jsx(Button, {
2047
+ mode: "bleed",
2048
+ padding: 1,
2049
+ style: {
2050
+ width: "100%"
2051
+ },
2052
+ children: /* @__PURE__ */jsxs(Flex, {
2053
+ gap: 2,
2054
+ align: "center",
2055
+ children: [/* @__PURE__ */jsx(Box, {
2056
+ flex: 1,
2057
+ children: /* @__PURE__ */jsx(DocumentPreview, {
2058
+ documentId: value.documentId,
2059
+ schemaTypeName: value.type
2060
+ })
2061
+ }), /* @__PURE__ */jsx(Box, {
2062
+ padding: 2,
2063
+ children: /* @__PURE__ */jsxs(Text, {
2064
+ size: 1,
2065
+ muted: true,
2066
+ title: "Relevance",
2067
+ children: [Math.floor(props.result.score * 100), "%"]
2068
+ })
2069
+ })]
2070
+ })
2071
+ });
2072
+ }
2093
2073
  return /* @__PURE__ */jsx(Button, {
2094
2074
  mode: "bleed",
2095
2075
  padding: 1,
2096
2076
  style: {
2097
2077
  width: "100%"
2098
2078
  },
2099
- children: /* @__PURE__ */jsxs(Flex, {
2079
+ disabled: true,
2080
+ children: /* @__PURE__ */jsx(Flex, {
2100
2081
  gap: 2,
2101
2082
  align: "center",
2102
- children: [/* @__PURE__ */jsx(Box, {
2103
- flex: 1,
2104
- children: /* @__PURE__ */jsx(DocumentPreview, {
2105
- documentId: value.documentId,
2106
- schemaTypeName: value.type
2107
- })
2108
- }), /* @__PURE__ */jsx(Box, {
2109
- padding: 2,
2110
- children: /* @__PURE__ */jsxs(Text, {
2111
- size: 1,
2112
- muted: true,
2113
- title: "Relevance",
2114
- children: [Math.floor(props.result.score * 100), "%"]
2115
- })
2116
- })]
2083
+ children: "No results."
2117
2084
  })
2118
2085
  });
2119
2086
  }
2087
+ function useDebouncedValue(value, ms) {
2088
+ const [debouncedValue, setDebouncedValue] = useState(value);
2089
+ useEffect(() => {
2090
+ const timeoutId = setTimeout(() => {
2091
+ setDebouncedValue(value);
2092
+ }, ms);
2093
+ return () => clearTimeout(timeoutId);
2094
+ }, [value, ms]);
2095
+ return debouncedValue;
2096
+ }
2097
+ function useEmeddingsConfig(embeddingsIndexConfig, defaultConfig) {
2098
+ return useMemo(() => {
2099
+ if (embeddingsIndexConfig === true || !embeddingsIndexConfig) {
2100
+ if (!(defaultConfig == null ? void 0 : defaultConfig.indexName)) {
2101
+ throw new Error("Default embeddingsIndex config is missing. When options.embeddingsIndex: true, embeddingsIndexReferenceInput plugin config is required.");
2102
+ }
2103
+ return defaultConfig;
2104
+ }
2105
+ const finalConfig = {
2106
+ ...defaultConfig,
2107
+ ...embeddingsIndexConfig
2108
+ };
2109
+ if (!(finalConfig == null ? void 0 : finalConfig.indexName)) {
2110
+ throw new Error("indexName is missing. Either set it in options.embeddingsIndex or configure defaults using plugin config.");
2111
+ }
2112
+ return finalConfig;
2113
+ }, [defaultConfig, embeddingsIndexConfig]);
2114
+ }
2115
+ function SemanticSearchReferenceInput(props) {
2116
+ var _a, _b;
2117
+ const embeddingsIndexConfig = (_b = (_a = props.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.embeddingsIndex;
2118
+ const config = useEmeddingsConfig(embeddingsIndexConfig, props.defaultConfig);
2119
+ const defaultEnabled = config.searchMode === "embeddings";
2120
+ const featureState = useIsFeatureEnabledContext();
2121
+ const [semantic, setSemantic] = useState(defaultEnabled);
2122
+ const toggleSemantic = useCallback(() => setSemantic(current => !current), []);
2123
+ return /* @__PURE__ */jsxs(Flex, {
2124
+ gap: 2,
2125
+ flex: 1,
2126
+ style: {
2127
+ width: "100%"
2128
+ },
2129
+ children: [semantic && featureState == "loading" ? /* @__PURE__ */jsx(Box, {
2130
+ padding: 2,
2131
+ children: /* @__PURE__ */jsx(Spinner, {})
2132
+ }) : null, semantic && featureState == "disabled" ? /* @__PURE__ */jsx(FeatureDisabledNotice, {}) : null, /* @__PURE__ */jsx(Box, {
2133
+ flex: 1,
2134
+ style: {
2135
+ maxHeight: 36,
2136
+ overflow: "hidden"
2137
+ },
2138
+ children: semantic && featureState == "enabled" ? /* @__PURE__ */jsx(SemanticSearchInput, {
2139
+ ...props,
2140
+ indexConfig: config
2141
+ }) : props.renderDefault(props)
2142
+ }), /* @__PURE__ */jsx(Button, {
2143
+ icon: semantic ? EarthGlobeIcon : LinkIcon,
2144
+ onClick: toggleSemantic,
2145
+ mode: "bleed",
2146
+ title: semantic ? "Switch to standard reference search" : "Switch to semantic reference search"
2147
+ })]
2148
+ });
2149
+ }
2150
+ function SemanticSearchInput(props) {
2151
+ const {
2152
+ indexConfig,
2153
+ onPathFocus,
2154
+ onChange,
2155
+ readOnly,
2156
+ schemaType,
2157
+ value
2158
+ } = props;
2159
+ const {
2160
+ value: currentDocument
2161
+ } = useDocumentPane();
2162
+ const docRef = useRef(currentDocument);
2163
+ const autocompleteRef = useRef(null);
2164
+ useEffect(() => {
2165
+ docRef.current = currentDocument;
2166
+ }, [currentDocument]);
2167
+ useEffect(() => {
2168
+ var _a;
2169
+ if (value == null ? void 0 : value._ref) {
2170
+ (_a = autocompleteRef.current) == null ? void 0 : _a.focus();
2171
+ }
2172
+ }, []);
2173
+ const handleFocus = useCallback(() => onPathFocus(["_ref"]), [onPathFocus]);
2174
+ const handleBlur = useCallback(() => onPathFocus([]), [onPathFocus]);
2175
+ const handleChange = useCallback(result => {
2176
+ if (!result) {
2177
+ onChange(unset());
2178
+ onPathFocus([]);
2179
+ return;
2180
+ }
2181
+ const patches = [setIfMissing({}), set(schemaType.name, ["_type"]), set(publicId(result.value.documentId), ["_ref"]), unset(["_weak"]), unset(["_strengthenOnPublish"])];
2182
+ onChange(patches);
2183
+ onPathFocus([]);
2184
+ }, [onChange, onPathFocus, schemaType.name]);
2185
+ const filterResult = useCallback(r => r.value.documentId !== publicId(docRef.current._id), [docRef]);
2186
+ const getEmptySearchValue = useCallback(() => JSON.stringify(docRef.current), [docRef]);
2187
+ const typeFilter = useMemo(() => schemaType.to.map(refType => refType.name), [schemaType]);
2188
+ return /* @__PURE__ */jsx(SemanticSearchAutocomplete, {
2189
+ ref: autocompleteRef,
2190
+ typeFilter,
2191
+ indexConfig,
2192
+ onSelect: handleChange,
2193
+ onFocus: handleFocus,
2194
+ onBlur: handleBlur,
2195
+ getEmptySearchValue,
2196
+ filterResult,
2197
+ readOnly
2198
+ });
2199
+ }
2120
2200
  function isType(schemaType, typeName) {
2121
2201
  if (schemaType.name === typeName) {
2122
2202
  return true;
@@ -2126,30 +2206,35 @@ function isType(schemaType, typeName) {
2126
2206
  }
2127
2207
  return isType(schemaType.type, typeName);
2128
2208
  }
2129
- const embeddingsIndexReferenceInput = definePlugin({
2130
- name: "@sanity/embeddings-index-reference-input",
2131
- studio: {
2132
- components: {
2133
- layout: props => {
2134
- return /* @__PURE__ */jsx(FeatureEnabledProvider, {
2135
- children: props.renderDefault(props)
2136
- });
2137
- }
2138
- }
2139
- },
2140
- form: {
2141
- components: {
2142
- input: props => {
2143
- var _a, _b;
2144
- if (isObjectInputProps(props) && isType(props.schemaType, "reference") && ((_b = (_a = props.schemaType.options) == null ? void 0 : _a.embeddingsIndex) == null ? void 0 : _b.indexName)) {
2145
- return /* @__PURE__ */jsx(SemanticSearchReferenceInput, {
2146
- ...props
2209
+ const embeddingsIndexReferenceInput = definePlugin(defaultConfig => {
2210
+ const config = typeof defaultConfig === "object" ? defaultConfig : void 0;
2211
+ return {
2212
+ name: "@sanity/embeddings-index-reference-input",
2213
+ studio: {
2214
+ components: {
2215
+ layout: props => {
2216
+ return /* @__PURE__ */jsx(FeatureEnabledProvider, {
2217
+ children: props.renderDefault(props)
2147
2218
  });
2148
2219
  }
2149
- return props.renderDefault(props);
2220
+ }
2221
+ },
2222
+ form: {
2223
+ components: {
2224
+ input: props => {
2225
+ var _a, _b;
2226
+ const embeddingsIndexConfig = (_b = (_a = props.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.embeddingsIndex;
2227
+ if (isObjectInputProps(props) && isType(props.schemaType, "reference") && (embeddingsIndexConfig === true || (embeddingsIndexConfig == null ? void 0 : embeddingsIndexConfig.indexName))) {
2228
+ return /* @__PURE__ */jsx(SemanticSearchReferenceInput, {
2229
+ ...props,
2230
+ defaultConfig: config
2231
+ });
2232
+ }
2233
+ return props.renderDefault(props);
2234
+ }
2150
2235
  }
2151
2236
  }
2152
- }
2237
+ };
2153
2238
  });
2154
2239
  const defaultProjection = "{...}";
2155
2240
  function useDefaultIndex(schema, dataset) {
@@ -2164,6 +2249,7 @@ function IndexFormInput(props) {
2164
2249
  var _a;
2165
2250
  const {
2166
2251
  label,
2252
+ description,
2167
2253
  index,
2168
2254
  prop,
2169
2255
  onChange,
@@ -2177,6 +2263,7 @@ function IndexFormInput(props) {
2177
2263
  })), [onChange, prop]);
2178
2264
  return /* @__PURE__ */jsx(FormInput, {
2179
2265
  label,
2266
+ description,
2180
2267
  onChange: handleChange,
2181
2268
  value: (_a = index[prop]) != null ? _a : "",
2182
2269
  readOnly,
@@ -2187,6 +2274,7 @@ function IndexFormInput(props) {
2187
2274
  function FormInput(props) {
2188
2275
  const {
2189
2276
  label,
2277
+ description,
2190
2278
  onChange,
2191
2279
  value,
2192
2280
  readOnly,
@@ -2204,6 +2292,12 @@ function FormInput(props) {
2204
2292
  htmlFor: id,
2205
2293
  children: label
2206
2294
  })
2295
+ }), description && /* @__PURE__ */jsx(Box, {
2296
+ children: /* @__PURE__ */jsx(Text, {
2297
+ size: 1,
2298
+ muted: true,
2299
+ children: description
2300
+ })
2207
2301
  }), type === "text" ? /* @__PURE__ */jsx(TextInput, {
2208
2302
  id,
2209
2303
  value,
@@ -2358,18 +2452,20 @@ function IndexEditor(props) {
2358
2452
  onChange: setIndex,
2359
2453
  readOnly: true
2360
2454
  }), /* @__PURE__ */jsx(IndexFormInput, {
2361
- label: "Projection",
2362
- placeholder: defaultIndex.projection,
2455
+ label: "Filter",
2456
+ description: "Must be a valid GROQ filter",
2457
+ placeholder: defaultIndex.filter,
2363
2458
  index,
2364
- prop: "projection",
2459
+ prop: "filter",
2365
2460
  onChange: setIndex,
2366
2461
  readOnly,
2367
2462
  type: "textarea"
2368
2463
  }), /* @__PURE__ */jsx(IndexFormInput, {
2369
- label: "Filter",
2370
- placeholder: defaultIndex.filter,
2464
+ label: "Projection",
2465
+ description: "Must be a valid GROQ projection, starting { and ending with }",
2466
+ placeholder: defaultIndex.projection,
2371
2467
  index,
2372
- prop: "filter",
2468
+ prop: "projection",
2373
2469
  onChange: setIndex,
2374
2470
  readOnly,
2375
2471
  type: "textarea"
@@ -2406,6 +2502,7 @@ function IndexList(props) {
2406
2502
  borderBottom: true,
2407
2503
  flex: 1,
2408
2504
  paddingBottom: 2,
2505
+ padding: 3,
2409
2506
  children: /* @__PURE__ */jsxs(Flex, {
2410
2507
  children: [/* @__PURE__ */jsx(Box, {
2411
2508
  flex: 1,
@@ -2460,12 +2557,15 @@ function IndexRow(props) {
2460
2557
  const onSelect = useCallback(() => onIndexSelected(index), [onIndexSelected, index]);
2461
2558
  return /* @__PURE__ */jsx(Button, {
2462
2559
  tone: (selectedIndex == null ? void 0 : selectedIndex.indexName) === index.indexName ? "primary" : "default",
2463
- mode: (selectedIndex == null ? void 0 : selectedIndex.indexName) === index.indexName ? "default" : "bleed",
2560
+ mode: (selectedIndex == null ? void 0 : selectedIndex.indexName) === index.indexName ? "default" : "ghost",
2464
2561
  onClick: onSelect,
2562
+ padding: 3,
2465
2563
  children: /* @__PURE__ */jsxs(Flex, {
2466
2564
  children: [/* @__PURE__ */jsx(Box, {
2467
2565
  flex: 1,
2468
- children: index.indexName
2566
+ children: /* @__PURE__ */jsx("strong", {
2567
+ children: index.indexName
2568
+ })
2469
2569
  }), /* @__PURE__ */jsx(Box, {
2470
2570
  flex: 1,
2471
2571
  children: index.dataset
@@ -2479,102 +2579,31 @@ function IndexRow(props) {
2479
2579
  })
2480
2580
  }, index.indexName);
2481
2581
  }
2482
- const NO_RESULTS = [];
2483
2582
  function QueryIndex(props) {
2484
2583
  const {
2485
2584
  indexName
2486
2585
  } = props;
2487
- const [query, setQuery] = useState("");
2488
- const [searching, setSearching] = useState(false);
2489
- const [results, setResults] = useState(NO_RESULTS);
2490
- const client = useApiClient();
2491
- const search = useCallback(queryString => {
2492
- setSearching(true);
2493
- return queryIndex({
2494
- query: queryString,
2495
- indexName,
2496
- maxResults: 5
2497
- }, client).then(setResults).finally(() => setSearching(false));
2498
- }, [client, indexName]);
2499
- const onInputChange = useCallback(e => {
2500
- setQuery(e.currentTarget.value);
2501
- }, []);
2502
- const onKeyDown = useCallback(e => {
2503
- if (e.key === "Enter") {
2504
- search(query).catch(console.error);
2505
- }
2506
- }, [search, query]);
2507
- return /* @__PURE__ */jsxs(Stack, {
2508
- space: 3,
2509
- flex: 1,
2510
- children: [/* @__PURE__ */jsx(Flex, {
2511
- flex: 1,
2512
- children: /* @__PURE__ */jsx(Card, {
2513
- flex: 1,
2514
- children: /* @__PURE__ */jsx(TextInput, {
2515
- iconRight: searching ? /* @__PURE__ */jsx(Box, {
2516
- style: {
2517
- marginTop: 5
2518
- },
2519
- children: /* @__PURE__ */jsx(Spinner, {})
2520
- }) : SearchIcon,
2521
- placeholder: "Find documents",
2522
- value: query,
2523
- disabled: searching,
2524
- onChange: onInputChange,
2525
- onKeyDown
2526
- })
2527
- })
2528
- }), /* @__PURE__ */jsx(Flex, {
2529
- gap: 4,
2530
- style: {
2531
- opacity: searching ? 0.5 : 1
2532
- },
2533
- children: /* @__PURE__ */jsx(Box, {
2534
- flex: 1,
2535
- children: /* @__PURE__ */jsx(ResultList, {
2536
- results,
2537
- query
2538
- })
2539
- })
2540
- })]
2541
- });
2542
- }
2543
- function ResultList(props) {
2586
+ const getEmpty = useCallback(() => "anything", []);
2587
+ const indexConfig = useMemo(() => ({
2588
+ indexName,
2589
+ maxResults: 8
2590
+ }), [indexName]);
2544
2591
  const {
2545
- results,
2546
- query
2547
- } = props;
2548
- return /* @__PURE__ */jsx(Stack, {
2549
- space: 4,
2550
- height: "fill",
2551
- children: /* @__PURE__ */jsxs(Stack, {
2552
- space: 2,
2553
- children: [results.map(r => /* @__PURE__ */jsx(ResultEntry, {
2554
- result: r
2555
- }, r.value.documentId)), !results.length && query ? "No results." : null]
2556
- })
2557
- });
2558
- }
2559
- function ResultEntry(props) {
2560
- const value = props.result.value;
2561
- return /* @__PURE__ */jsxs(Flex, {
2562
- gap: 4,
2563
- align: "center",
2564
- children: [/* @__PURE__ */jsx(Box, {
2565
- flex: 1,
2566
- children: /* @__PURE__ */jsx(DocumentPreview, {
2567
- documentId: value.documentId,
2568
- schemaTypeName: value.type,
2569
- button: true
2592
+ resolveIntentLink,
2593
+ navigateUrl
2594
+ } = useRouter();
2595
+ const onSelect = useCallback(hit => {
2596
+ navigateUrl({
2597
+ path: resolveIntentLink("edit", {
2598
+ id: hit.value.documentId,
2599
+ type: hit.value.type
2570
2600
  })
2571
- }), /* @__PURE__ */jsx(Box, {
2572
- children: /* @__PURE__ */jsxs(Text, {
2573
- muted: true,
2574
- size: 1,
2575
- children: [Math.floor(props.result.score * 100), " %"]
2576
- })
2577
- })]
2601
+ });
2602
+ }, [resolveIntentLink, navigateUrl]);
2603
+ return /* @__PURE__ */jsx(SemanticSearchAutocomplete, {
2604
+ getEmptySearchValue: getEmpty,
2605
+ indexConfig,
2606
+ onSelect
2578
2607
  });
2579
2608
  }
2580
2609
  function IndexInfo(_ref3) {