@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/README.md +33 -1
- package/dist/index.d.ts +21 -14
- package/dist/index.esm.js +303 -274
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +299 -270
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/embeddingsIndexDashboard/IndexEditor.tsx +8 -6
- package/src/embeddingsIndexDashboard/IndexFormInput.tsx +13 -3
- package/src/embeddingsIndexDashboard/IndexList.tsx +6 -3
- package/src/embeddingsIndexDashboard/QueryIndex.tsx +21 -98
- package/src/referenceInput/SemanticSearchAutocomplete.tsx +210 -0
- package/src/referenceInput/SemanticSearchReferenceInput.tsx +65 -144
- package/src/referenceInput/referencePlugin.tsx +35 -21
- package/src/schemas/typeDefExtensions.ts +21 -13
package/dist/index.esm.js
CHANGED
|
@@ -1,10 +1,57 @@
|
|
|
1
|
-
import { useSchema, DefaultPreview, useDocumentPreviewStore, SanityDefaultPreview, getPreviewValueWithFallback, getPreviewStateObservable,
|
|
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,
|
|
4
|
-
import { ErrorOutlineIcon, EarthGlobeIcon, LinkIcon, AddIcon,
|
|
5
|
-
import require$$0, {
|
|
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
|
|
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
|
|
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
|
-
|
|
1989
|
-
|
|
1947
|
+
indexConfig,
|
|
1948
|
+
filterResult,
|
|
1949
|
+
getEmptySearchValue,
|
|
1990
1950
|
readOnly,
|
|
1991
|
-
|
|
1992
|
-
|
|
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
|
|
1965
|
+
var _a;
|
|
2020
1966
|
setSearching(true);
|
|
2021
|
-
const
|
|
2022
|
-
const
|
|
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
|
|
1970
|
+
throw new Error("Reference option embeddingsIndex.indexName is required, but was missing");
|
|
2027
1971
|
}
|
|
2028
1972
|
queryIndex({
|
|
2029
|
-
query: queryString.trim().length ? queryString : (
|
|
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(
|
|
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,
|
|
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
|
|
2028
|
+
ref,
|
|
2077
2029
|
"data-testid": "semantic-autocomplete",
|
|
2078
2030
|
placeholder: "Type to search...",
|
|
2079
2031
|
openButton: openButtonConfig,
|
|
2080
|
-
onFocus
|
|
2032
|
+
onFocus,
|
|
2081
2033
|
onChange: handleChange,
|
|
2082
2034
|
loading: searching,
|
|
2083
|
-
onBlur
|
|
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
|
-
|
|
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
|
-
|
|
2079
|
+
disabled: true,
|
|
2080
|
+
children: /* @__PURE__ */jsx(Flex, {
|
|
2100
2081
|
gap: 2,
|
|
2101
2082
|
align: "center",
|
|
2102
|
-
children:
|
|
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
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
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
|
-
|
|
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: "
|
|
2362
|
-
|
|
2455
|
+
label: "Filter",
|
|
2456
|
+
description: "Must be a valid GROQ filter",
|
|
2457
|
+
placeholder: defaultIndex.filter,
|
|
2363
2458
|
index,
|
|
2364
|
-
prop: "
|
|
2459
|
+
prop: "filter",
|
|
2365
2460
|
onChange: setIndex,
|
|
2366
2461
|
readOnly,
|
|
2367
2462
|
type: "textarea"
|
|
2368
2463
|
}), /* @__PURE__ */jsx(IndexFormInput, {
|
|
2369
|
-
label: "
|
|
2370
|
-
|
|
2464
|
+
label: "Projection",
|
|
2465
|
+
description: "Must be a valid GROQ projection, starting { and ending with }",
|
|
2466
|
+
placeholder: defaultIndex.projection,
|
|
2371
2467
|
index,
|
|
2372
|
-
prop: "
|
|
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" : "
|
|
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:
|
|
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
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
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
|
-
|
|
2546
|
-
|
|
2547
|
-
} =
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
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
|
-
})
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
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) {
|