@sanity/embeddings-index-ui 1.0.3 → 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/README.md +33 -1
- package/dist/index.d.ts +21 -14
- package/dist/index.esm.js +289 -276
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +285 -272
- 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 +8 -103
- package/src/referenceInput/SemanticSearchAutocomplete.tsx +205 -0
- package/src/referenceInput/SemanticSearchReferenceInput.tsx +62 -140
- 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 } 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
|
+
onChange,
|
|
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,187 @@ 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
|
+
onChange == null ? void 0 : onChange(value);
|
|
2022
|
+
}, [onChange]);
|
|
2074
2023
|
return /* @__PURE__ */jsx(Autocomplete, {
|
|
2075
2024
|
id,
|
|
2076
|
-
ref
|
|
2025
|
+
ref,
|
|
2077
2026
|
"data-testid": "semantic-autocomplete",
|
|
2078
2027
|
placeholder: "Type to search...",
|
|
2079
2028
|
openButton: openButtonConfig,
|
|
2080
|
-
onFocus
|
|
2029
|
+
onFocus,
|
|
2081
2030
|
onChange: handleChange,
|
|
2082
2031
|
loading: searching,
|
|
2083
|
-
onBlur
|
|
2032
|
+
onBlur,
|
|
2084
2033
|
readOnly,
|
|
2085
2034
|
filterOption: NO_FILTER,
|
|
2086
2035
|
onQueryChange: handleQueryChange,
|
|
2087
2036
|
options,
|
|
2088
2037
|
renderOption: AutocompleteOption
|
|
2089
2038
|
});
|
|
2090
|
-
}
|
|
2039
|
+
});
|
|
2091
2040
|
function AutocompleteOption(props) {
|
|
2092
|
-
|
|
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
|
+
}
|
|
2093
2070
|
return /* @__PURE__ */jsx(Button, {
|
|
2094
2071
|
mode: "bleed",
|
|
2095
2072
|
padding: 1,
|
|
2096
2073
|
style: {
|
|
2097
2074
|
width: "100%"
|
|
2098
2075
|
},
|
|
2099
|
-
|
|
2076
|
+
disabled: true,
|
|
2077
|
+
children: /* @__PURE__ */jsx(Flex, {
|
|
2100
2078
|
gap: 2,
|
|
2101
2079
|
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
|
-
})]
|
|
2080
|
+
children: "No results."
|
|
2117
2081
|
})
|
|
2118
2082
|
});
|
|
2119
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
|
+
}
|
|
2120
2197
|
function isType(schemaType, typeName) {
|
|
2121
2198
|
if (schemaType.name === typeName) {
|
|
2122
2199
|
return true;
|
|
@@ -2126,30 +2203,35 @@ function isType(schemaType, typeName) {
|
|
|
2126
2203
|
}
|
|
2127
2204
|
return isType(schemaType.type, typeName);
|
|
2128
2205
|
}
|
|
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
|
|
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)
|
|
2147
2215
|
});
|
|
2148
2216
|
}
|
|
2149
|
-
|
|
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
|
+
}
|
|
2150
2232
|
}
|
|
2151
2233
|
}
|
|
2152
|
-
}
|
|
2234
|
+
};
|
|
2153
2235
|
});
|
|
2154
2236
|
const defaultProjection = "{...}";
|
|
2155
2237
|
function useDefaultIndex(schema, dataset) {
|
|
@@ -2164,6 +2246,7 @@ function IndexFormInput(props) {
|
|
|
2164
2246
|
var _a;
|
|
2165
2247
|
const {
|
|
2166
2248
|
label,
|
|
2249
|
+
description,
|
|
2167
2250
|
index,
|
|
2168
2251
|
prop,
|
|
2169
2252
|
onChange,
|
|
@@ -2177,6 +2260,7 @@ function IndexFormInput(props) {
|
|
|
2177
2260
|
})), [onChange, prop]);
|
|
2178
2261
|
return /* @__PURE__ */jsx(FormInput, {
|
|
2179
2262
|
label,
|
|
2263
|
+
description,
|
|
2180
2264
|
onChange: handleChange,
|
|
2181
2265
|
value: (_a = index[prop]) != null ? _a : "",
|
|
2182
2266
|
readOnly,
|
|
@@ -2187,6 +2271,7 @@ function IndexFormInput(props) {
|
|
|
2187
2271
|
function FormInput(props) {
|
|
2188
2272
|
const {
|
|
2189
2273
|
label,
|
|
2274
|
+
description,
|
|
2190
2275
|
onChange,
|
|
2191
2276
|
value,
|
|
2192
2277
|
readOnly,
|
|
@@ -2204,6 +2289,12 @@ function FormInput(props) {
|
|
|
2204
2289
|
htmlFor: id,
|
|
2205
2290
|
children: label
|
|
2206
2291
|
})
|
|
2292
|
+
}), description && /* @__PURE__ */jsx(Box, {
|
|
2293
|
+
children: /* @__PURE__ */jsx(Text, {
|
|
2294
|
+
size: 1,
|
|
2295
|
+
muted: true,
|
|
2296
|
+
children: description
|
|
2297
|
+
})
|
|
2207
2298
|
}), type === "text" ? /* @__PURE__ */jsx(TextInput, {
|
|
2208
2299
|
id,
|
|
2209
2300
|
value,
|
|
@@ -2358,18 +2449,20 @@ function IndexEditor(props) {
|
|
|
2358
2449
|
onChange: setIndex,
|
|
2359
2450
|
readOnly: true
|
|
2360
2451
|
}), /* @__PURE__ */jsx(IndexFormInput, {
|
|
2361
|
-
label: "
|
|
2362
|
-
|
|
2452
|
+
label: "Filter",
|
|
2453
|
+
description: "Must be a valid GROQ filter",
|
|
2454
|
+
placeholder: defaultIndex.filter,
|
|
2363
2455
|
index,
|
|
2364
|
-
prop: "
|
|
2456
|
+
prop: "filter",
|
|
2365
2457
|
onChange: setIndex,
|
|
2366
2458
|
readOnly,
|
|
2367
2459
|
type: "textarea"
|
|
2368
2460
|
}), /* @__PURE__ */jsx(IndexFormInput, {
|
|
2369
|
-
label: "
|
|
2370
|
-
|
|
2461
|
+
label: "Projection",
|
|
2462
|
+
description: "Must be a valid GROQ projection, starting { and ending with }",
|
|
2463
|
+
placeholder: defaultIndex.projection,
|
|
2371
2464
|
index,
|
|
2372
|
-
prop: "
|
|
2465
|
+
prop: "projection",
|
|
2373
2466
|
onChange: setIndex,
|
|
2374
2467
|
readOnly,
|
|
2375
2468
|
type: "textarea"
|
|
@@ -2406,6 +2499,7 @@ function IndexList(props) {
|
|
|
2406
2499
|
borderBottom: true,
|
|
2407
2500
|
flex: 1,
|
|
2408
2501
|
paddingBottom: 2,
|
|
2502
|
+
padding: 3,
|
|
2409
2503
|
children: /* @__PURE__ */jsxs(Flex, {
|
|
2410
2504
|
children: [/* @__PURE__ */jsx(Box, {
|
|
2411
2505
|
flex: 1,
|
|
@@ -2460,12 +2554,15 @@ function IndexRow(props) {
|
|
|
2460
2554
|
const onSelect = useCallback(() => onIndexSelected(index), [onIndexSelected, index]);
|
|
2461
2555
|
return /* @__PURE__ */jsx(Button, {
|
|
2462
2556
|
tone: (selectedIndex == null ? void 0 : selectedIndex.indexName) === index.indexName ? "primary" : "default",
|
|
2463
|
-
mode: (selectedIndex == null ? void 0 : selectedIndex.indexName) === index.indexName ? "default" : "
|
|
2557
|
+
mode: (selectedIndex == null ? void 0 : selectedIndex.indexName) === index.indexName ? "default" : "ghost",
|
|
2464
2558
|
onClick: onSelect,
|
|
2559
|
+
padding: 3,
|
|
2465
2560
|
children: /* @__PURE__ */jsxs(Flex, {
|
|
2466
2561
|
children: [/* @__PURE__ */jsx(Box, {
|
|
2467
2562
|
flex: 1,
|
|
2468
|
-
children:
|
|
2563
|
+
children: /* @__PURE__ */jsx("strong", {
|
|
2564
|
+
children: index.indexName
|
|
2565
|
+
})
|
|
2469
2566
|
}), /* @__PURE__ */jsx(Box, {
|
|
2470
2567
|
flex: 1,
|
|
2471
2568
|
children: index.dataset
|
|
@@ -2479,102 +2576,18 @@ function IndexRow(props) {
|
|
|
2479
2576
|
})
|
|
2480
2577
|
}, index.indexName);
|
|
2481
2578
|
}
|
|
2482
|
-
const NO_RESULTS = [];
|
|
2483
2579
|
function QueryIndex(props) {
|
|
2484
2580
|
const {
|
|
2485
2581
|
indexName
|
|
2486
2582
|
} = props;
|
|
2487
|
-
const
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
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) {
|
|
2544
|
-
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
|
|
2570
|
-
})
|
|
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
|
-
})]
|
|
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
|
|
2578
2591
|
});
|
|
2579
2592
|
}
|
|
2580
2593
|
function IndexInfo(_ref3) {
|