@indxsearch/intrface 3.11.0 → 3.13.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/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - SearchResults takes `showScore`, to show or hide each result's score from code. Given, it wins over the toggle in SearchSettingsPanel; left out, the toggle decides, as before.
8
+
9
+ ### Patch Changes
10
+
11
+ - bac9b80: SearchField has a built-in Clear button. It shows while the field holds text and puts the focus back in the field. Turn it off with `clearable={false}`. SearchInput keeps its own Clear, which also resets the filters.
12
+ - Updated dependencies [ffda712]
13
+ - Updated dependencies
14
+ - Updated dependencies [bac9b80]
15
+ - Updated dependencies
16
+ - Updated dependencies [f1bb38b]
17
+ - @indxsearch/systm@2.21.0
18
+
19
+ ## 3.12.0
20
+
21
+ ### Minor Changes
22
+
23
+ - 1533dad: SearchInput shows what the search is doing. A blue chip counts the active filters. A Nearest chip appears when nothing matched your words directly and the list holds the nearest results instead; its tooltip says so. A No results chip appears when a search returns nothing. There is one Clear button, and it now resets the query and the filters together, so it also shows on an empty search with filters set. `showStatus={false}` hides the chips.
24
+
25
+ SearchField leaves room for what sits on its right, such as a Clear button or chips, so typed text no longer runs underneath them.
26
+
27
+ The search icon lights up only while the input has focus. Clicking Clear used to light it for a moment.
28
+
29
+ - 43b8279: SearchResults shows a failed search as a state the visitor can read, not as an error message. Each kind of failure has its own icon and a short line: the server can't be reached, the key was not accepted or has no access, the dataset doesn't exist, the dataset is getting ready, too many searches, or a server error. Under it, a small technical line tells whoever built the page what happened and what to do. Where trying again can help there is a Try again button, and a dataset that is still loading or indexing is retried by itself when the server says it is ready.
30
+
31
+ No results now shows nothing in the list, since SearchInput says it in the field.
32
+
33
+ For your own components, the context has `retry()`, and `authErrorInfo` and `state.errorInfo` next to the existing messages, with the kind of failure, whether retrying can help, and the technical line. `describeSearchError` classifies any error the same way, and `SearchResultsMessage` is the component SearchResults uses, if you want it elsewhere.
34
+
35
+ A key that cannot reach the dataset (403) used to be reported as a failed connection. The startup messages in `authError` now follow the same form as search errors, for example "Dataset status failed: HTTP 404".
36
+
37
+ ### Patch Changes
38
+
39
+ - 383e0f5: Filter panels are calmer when something is wrong. A panel whose field isn't set up for it (not Filterable, or not Facetable for a value panel) no longer shows a red message: it keeps its title, says "Not available" in muted text, names the missing role in a small technical line, and tells the console what to change. When the search fails to start, the filter panels step aside instead of each reporting its field as broken or showing a loading skeleton that never ends; the results area explains the failure once.
40
+ - Updated dependencies [f9400f4]
41
+ - Updated dependencies [1533dad]
42
+ - @indxsearch/systm@2.20.0
43
+
3
44
  ## 3.11.0
4
45
 
5
46
  ### Minor Changes
@@ -0,0 +1,43 @@
1
+ import { default as React } from 'react';
2
+ import { AskSource, AskStatus, AskFilters } from '../hooks/useAsk';
3
+ export interface AskPanelProps {
4
+ /** The question to answer. A new value asks it and starts a new conversation; null or empty
5
+ * shows nothing. To ask the same text again, give the panel a new `key`. */
6
+ question: string | null;
7
+ /** Lets the visitor ask follow-up questions under the answer. Each is answered with the
8
+ * conversation so far (the server uses the last three turns), and the earlier turns stay on
9
+ * screen above it. */
10
+ followUps?: boolean;
11
+ /** Called when the visitor picks a cited source. Look the record up by its documentKey. */
12
+ onSourceClick?: (source: AskSource) => void;
13
+ /** The label over each answer. Default "Answer". */
14
+ label?: string;
15
+ /** Renders the answer text, for a page with a markdown renderer of its own. `cite(n)` gives the
16
+ * citation mark for source n. Default: intrface's own markdown (paragraphs, headings, lists,
17
+ * code blocks, inline code, bold, italics, links), with [n] as citation marks. */
18
+ renderAnswer?: (answer: string, cite: (n: number) => React.ReactNode) => React.ReactNode;
19
+ /** Shown under the answer as "Ask a follow-up" when `followUps` is off: e.g. back to the
20
+ * search field. With `followUps` on, that button goes to the follow-up field instead. */
21
+ onFollowUp?: () => void;
22
+ /** On a catalogue, called with the filters a description was searched with, as soon as they are
23
+ * known: give them to `useFilterSuggestion().apply` so the page's list shows the same set. */
24
+ onFilters?: (filters: AskFilters) => void;
25
+ /** How a filter reads on its chip. Default: a text value as itself, a yes/no by its field's
26
+ * name, a range by its bounds. */
27
+ filterLabel?: (filter: AskFilters['filters'][number]) => string;
28
+ /** Told when the answer starts and stops streaming, e.g. to show a Stop button elsewhere. */
29
+ onStatusChange?: (status: AskStatus) => void;
30
+ /** Fills the height its container gives it, for a panel beside the results: the conversation
31
+ * scrolls on its own, the follow-up field stays at the bottom, and the view follows an answer
32
+ * as it streams while the visitor is at the end of it. Give the container a height (or make it
33
+ * a flex child that has one). Without it the panel is as tall as its content. */
34
+ fill?: boolean;
35
+ className?: string;
36
+ }
37
+ /**
38
+ * An answer to a question, written from the dataset's records by Search or ask: the question, the
39
+ * answer as it streams, the sources it cites, and what to do when it fails or is limited. With
40
+ * `followUps`, a conversation: a field under the answer asks the next question with the earlier
41
+ * ones as context. Pair it with `SearchInput`'s `onAsk`, or `useSearchOrAsk` (when to offer).
42
+ */
43
+ export declare const AskPanel: React.FC<AskPanelProps>;
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+ export interface FilterPanelNoticeProps {
3
+ /** The panel's own title, so the notice sits where the panel would have been. */
4
+ label?: string;
5
+ field: string;
6
+ /** The roles the field lacks, such as ['filterable', 'facetable']. */
7
+ missing: string[];
8
+ }
9
+ /** A filter panel whose field cannot be used: the field is not marked for the role the panel needs.
10
+ * A setup mistake, so it is said quietly in the panel and fully in the console, not in red. */
11
+ export declare const FilterPanelNotice: React.FC<FilterPanelNoticeProps>;
@@ -1,8 +1,24 @@
1
1
  import { default as React } from 'react';
2
2
  import { InputSize } from '@indxsearch/systm';
3
+ import { SearchOrAskState } from '../hooks/useSearchOrAsk';
3
4
  export interface SearchInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
4
5
  inputSize?: InputSize;
6
+ /** One Clear button that resets the query and the filters, shown whenever there is either. */
5
7
  showClear?: boolean;
6
8
  showFocus?: boolean;
9
+ /** Chips that say what the search is doing: the number of active filters, "Nearest" when no
10
+ * result reached the coverage bar, and "No results". */
11
+ showStatus?: boolean;
12
+ /** Search or ask: when given, and the dataset has it switched on, an offer to ask appears in
13
+ * the chip row while the visitor types something an answer would serve. A strong offer also
14
+ * makes Enter ask. Called with the text to ask; show the answer with `AskPanel`. */
15
+ onAsk?: (text: string) => void;
16
+ /** The offer, when the page runs `useSearchOrAsk` itself (to show it elsewhere too, or to use
17
+ * `describes`): the field then shows this one and does not judge again. */
18
+ searchOrAsk?: SearchOrAskState;
19
+ /** Where the offer to ask shows. `chip` (default): a button in the field's chip row. `below`: a
20
+ * row of its own under the field, as on the Indx docs: a full-size button and an Enter hint.
21
+ * The row keeps its height while Search or ask is on, so an offer appearing moves nothing under it. */
22
+ askOffer?: 'chip' | 'below';
7
23
  }
8
24
  export declare const SearchInput: React.FC<SearchInputProps>;
@@ -1,5 +1,6 @@
1
1
  import { default as React } from 'react';
2
2
  import { SearchResult as SearchResultData } from '../context/SearchContext';
3
+ import { SearchOrAskState } from '../hooks/useSearchOrAsk';
3
4
  export interface SearchResultsProps {
4
5
  fields?: string[];
5
6
  resultsPerPage?: number;
@@ -16,6 +17,17 @@ export interface SearchResultsProps {
16
17
  * result, so the dataset's statistics get click-through and click position.
17
18
  */
18
19
  children: (item: Record<string, any>, meta: SearchResultMeta) => React.ReactNode;
20
+ /**
21
+ * The offer, when the page runs `useSearchOrAsk`: while it is strong and the dataset has the
22
+ * rule on, the list shows coverage hits only (up to the truncation point), and nothing when
23
+ * nothing is confirmed, so the offer stands alone. Without it the list is never cut.
24
+ */
25
+ searchOrAsk?: SearchOrAskState;
26
+ /**
27
+ * Shows each result's score beside it. Given, it wins over the `showScore` search setting (the
28
+ * toggle in SearchSettingsPanel); left out, that setting decides, and it is on by default.
29
+ */
30
+ showScore?: boolean;
19
31
  }
20
32
  export interface SearchResultMeta {
21
33
  result: SearchResultData;
@@ -0,0 +1,12 @@
1
+ import { default as React } from 'react';
2
+ import { SearchErrorInfo } from '../context/searchError';
3
+ export interface SearchResultsMessageProps {
4
+ error: SearchErrorInfo;
5
+ /** Tries again. Shown as a button where retrying can help; a dataset that is getting ready is retried by itself. */
6
+ onRetry?: () => void;
7
+ /** Hides the technical line, for a page whose visitors should not see it. */
8
+ hideTechnical?: boolean;
9
+ }
10
+ /** What SearchResults shows in place of the list when a search fails: an icon, a line for the
11
+ * visitor, the technical reason, and Try again where that can help. */
12
+ export declare const SearchResultsMessage: React.FC<SearchResultsMessageProps>;
@@ -1,5 +1,6 @@
1
1
  import { default as React } from 'react';
2
2
  import { CoverageSetup } from '@indxsearch/indx-types';
3
+ import { SearchErrorInfo } from './searchError';
3
4
  import { ValueMatch, NumericRange } from './buildFilterProxy';
4
5
  export type { ValueMatch, NumericRange };
5
6
  export interface SearchSettings {
@@ -25,6 +26,7 @@ export interface SearchState {
25
26
  resultsSuppressed?: boolean;
26
27
  facetDebounceDelayMillis?: number;
27
28
  error?: string;
29
+ errorInfo?: SearchErrorInfo;
28
30
  facets?: any | null;
29
31
  filterableFields: string[];
30
32
  facetableFields: string[];
@@ -58,7 +60,14 @@ export interface SearchContextType {
58
60
  state: SearchState;
59
61
  isFetchingInitial: boolean;
60
62
  authError?: string;
63
+ authErrorInfo?: SearchErrorInfo;
64
+ retry?: () => void;
61
65
  allowEmptySearch: boolean;
66
+ facetsEnabled: boolean;
67
+ datasetBounds: Record<string, {
68
+ min: number;
69
+ max: number;
70
+ }>;
62
71
  url: string;
63
72
  team: string;
64
73
  dataset: string;
@@ -78,7 +87,19 @@ export interface SearchContextType {
78
87
  fetchMoreResults: (newMax: number) => void;
79
88
  selectResult?: (result: SearchResult) => Promise<void>;
80
89
  sessionId?: string;
90
+ source?: string;
91
+ count?: boolean;
92
+ /** What the search runs on: `state.query`, or what `searchTextOf` or a registered rewrite made of it. */
93
+ searchText?: string;
94
+ /** Searches the typed text as it is, this once: the way back from a rewrite ("Search for your exact words"). */
95
+ searchAsTyped?: () => void;
96
+ /** Registers what the search runs on for what is typed, for a hook inside the provider
97
+ * (useSearchOrAsk's Ask phrases); null takes it off. The provider's own `searchTextOf` wins. */
98
+ setSearchRewrite?: (rewrite: ((typed: string) => string) | null) => void;
81
99
  }
100
+ /** The field's placeholder unless the page sets one. SearchInput says "Search or ask" instead
101
+ * while asking is on, so it must know this one from a page's own. */
102
+ export declare const DEFAULT_PLACEHOLDER = "Type to search";
82
103
  export declare const SearchContext: React.Context<SearchContextType | undefined>;
83
104
  export declare const SearchProvider: React.FC<{
84
105
  children: React.ReactNode;
@@ -97,5 +118,10 @@ export declare const SearchProvider: React.FC<{
97
118
  preAuthenticatedToken?: string;
98
119
  source?: string;
99
120
  count?: boolean;
121
+ /** The text to search for what is typed, when it is not the typed text itself: the field keeps
122
+ * showing what the visitor typed, and the search runs on this. Search or ask's Ask phrases use
123
+ * it ("help me find the movie about the green ogre" searches "green ogre"). Called on every
124
+ * render with the typed text; return it unchanged to search it as typed. */
125
+ searchTextOf?: (typed: string) => string;
100
126
  }>;
101
127
  export declare const useSearchContext: () => SearchContextType;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * What kind of failure stopped the search, so a component can show it to the visitor rather than
3
+ * print the error's text:
4
+ *
5
+ * - `unreachable` — no answer at all: the server is down, the URL is wrong, or the network is gone
6
+ * - `unauthorized` — 401, the key is invalid or has expired
7
+ * - `forbidden` — 403, the key does not reach this dataset
8
+ * - `notFound` — 404, no such team or dataset
9
+ * - `notReady` — 409 `invalidState` with `retryable`: loading or indexing, the same request succeeds soon
10
+ * - `unavailable` — 409 otherwise: the dataset is in a state search cannot use (Error, or a build in progress)
11
+ * - `rateLimited` — 429, too many requests for this key
12
+ * - `server` — 5xx
13
+ * - `setup` — SearchProvider was not given what it needs (url, team, dataset, token)
14
+ * - `unknown` — anything else
15
+ */
16
+ export type SearchErrorKind = 'unreachable' | 'unauthorized' | 'forbidden' | 'notFound' | 'notReady' | 'unavailable' | 'rateLimited' | 'server' | 'setup' | 'unknown';
17
+ export interface SearchErrorInfo {
18
+ kind: SearchErrorKind;
19
+ /** The error's own message, as `authError` and `state.error` carry it. */
20
+ message: string;
21
+ /** For whoever built the page: the status, the server's code and what to do. */
22
+ technical: string;
23
+ status?: number;
24
+ code?: string;
25
+ /** Seconds the server asked to wait (`Retry-After`), for `notReady` and `rateLimited`. */
26
+ retryAfterSeconds?: number;
27
+ /** Whether trying the same request again can succeed. */
28
+ retryable: boolean;
29
+ }
30
+ /** Thrown for a missing SearchProvider prop, so it is told apart from a failed request. */
31
+ export declare class SearchSetupError extends Error {
32
+ constructor(message: string);
33
+ }
34
+ export declare function describeSearchError(err: unknown, where: {
35
+ team: string;
36
+ dataset: string;
37
+ }): SearchErrorInfo;
@@ -1,8 +1,13 @@
1
+ import { SearchErrorInfo } from './searchError';
1
2
  export interface IndxAuthResult {
2
3
  token: string | null;
3
4
  isFetchingInitial: boolean;
4
5
  /** Set when initialization failed (bad token, unknown dataset, unreachable server). */
5
6
  authError: string | null;
7
+ /** The same failure, classified: what kind it was, whether retrying can help, and a technical line. */
8
+ authErrorInfo: SearchErrorInfo | null;
9
+ /** Runs initialisation again, after a failure. */
10
+ retry: () => void;
6
11
  initialFacetStats: Record<string, {
7
12
  min: number;
8
13
  max: number;
@@ -15,6 +15,11 @@ export interface UseSearchExecutionOptions {
15
15
  sessionId: string;
16
16
  source?: string;
17
17
  count: boolean;
18
+ /** The text to search for what is typed (SearchProvider's `searchTextOf`). Absent: the typed text. */
19
+ searchTextOf?: (typed: string) => string;
18
20
  }
19
- export type UseSearchExecutionResult = void;
20
- export declare function useSearchExecution({ state, setState, authenticatedFetch, auth, url, team, dataset, allowEmptySearch, facetsEnabled, enableDebugLogs, shouldFetchMore, sessionId, source, count, }: UseSearchExecutionOptions): UseSearchExecutionResult;
21
+ export interface UseSearchExecutionResult {
22
+ /** Runs the last search again, after a failure. */
23
+ retrySearch: () => void;
24
+ }
25
+ export declare function useSearchExecution({ state, setState, authenticatedFetch, auth, url, team, dataset, allowEmptySearch, facetsEnabled, enableDebugLogs, shouldFetchMore, sessionId, source, count, searchTextOf, }: UseSearchExecutionOptions): UseSearchExecutionResult;
@@ -0,0 +1,89 @@
1
+ /** A record the answer may use, numbered as the answer cites it ([n]). */
2
+ export interface AskSource {
3
+ n: number;
4
+ documentKey: number;
5
+ title: string;
6
+ }
7
+ /** An earlier question and its answer, sent with a follow-up so the answer can build on it. */
8
+ export interface AskTurn {
9
+ question: string;
10
+ answer: string;
11
+ /** On a catalogue, the list that turn put on the page: a follow-up is about it. */
12
+ shown?: {
13
+ filters: AskFilters['filters'];
14
+ text: string;
15
+ sort?: AskFilters['sort'];
16
+ };
17
+ }
18
+ export type AskStatus = 'idle' | 'searching' | 'writing' | 'answered' | 'error' | 'limited';
19
+ export interface AskState {
20
+ question: string;
21
+ status: AskStatus;
22
+ /** The answer as written so far, citing sources as [n]. */
23
+ answer: string;
24
+ sources: AskSource[];
25
+ /** Why it failed, when status is 'error': the server's code (nothingFound, upstreamBusy, failed,
26
+ * or an HTTP problem code) and a sentence for the page. */
27
+ error?: {
28
+ code: string;
29
+ message: string;
30
+ };
31
+ /** When status is 'limited': seconds until asking again may succeed. */
32
+ retryAfterSeconds?: number;
33
+ /** The server's id for this ask (Indx-Ask-Id). A source opened is reported against it. */
34
+ askId?: string;
35
+ /** On a catalogue, a description's filters, as the server searched with them, and what it found. */
36
+ filters?: AskFilters;
37
+ /** What the agent has done so far ("Searching: Folkehøyskole, by Elevtall"), while it works. */
38
+ steps: string[];
39
+ }
40
+ /** What a catalogue's description was searched with: filters, words no filter covers, and the count. */
41
+ export interface AskFilters {
42
+ filters: {
43
+ field: string;
44
+ value?: string | null;
45
+ min?: number | null;
46
+ max?: number | null;
47
+ }[];
48
+ text: string;
49
+ /** An order the description asked for ("the largest"): a Sortable field. */
50
+ sort?: {
51
+ field: string;
52
+ descending: boolean;
53
+ } | null;
54
+ found: number;
55
+ /** True when the count is a cap: there are more. */
56
+ more: boolean;
57
+ }
58
+ /**
59
+ * Asks the dataset a question (`search-or-ask/ask`) and follows the answer as it streams: the
60
+ * sources first, then the text, then done. One question at a time; asking again, or `stop`, ends
61
+ * the one in flight and keeps what had arrived. The answer uses the dataset's records and nothing
62
+ * else, and says so when they do not hold it.
63
+ */
64
+ export declare function useAsk(): {
65
+ ask: (question: string, history?: AskTurn[]) => Promise<void>;
66
+ stop: () => void | undefined;
67
+ reset: () => void;
68
+ openSource: (source: AskSource, ofAsk?: string) => Promise<void>;
69
+ busy: boolean;
70
+ question: string;
71
+ status: AskStatus;
72
+ /** The answer as written so far, citing sources as [n]. */
73
+ answer: string;
74
+ sources: AskSource[];
75
+ /** Why it failed, when status is 'error': the server's code (nothingFound, upstreamBusy, failed,
76
+ * or an HTTP problem code) and a sentence for the page. */
77
+ error?: {
78
+ code: string;
79
+ message: string;
80
+ };
81
+ /** When status is 'limited': seconds until asking again may succeed. */
82
+ retryAfterSeconds?: number;
83
+ /** The server's id for this ask (Indx-Ask-Id). A source opened is reported against it. */
84
+ askId?: string;
85
+ /** On a catalogue, a description's filters, as the server searched with them, and what it found. */
86
+ filters?: AskFilters;
87
+ /** What the agent has done so far ("Searching: Folkehøyskole, by Elevtall"), while it works. */
88
+ steps: string[];
89
+ };
@@ -0,0 +1,42 @@
1
+ import { SearchOrAskState } from './useSearchOrAsk';
2
+ /** One filter a description asked for: a value (text, or "true"/"false") or a number range. */
3
+ export interface FilterSuggestion {
4
+ field: string;
5
+ value?: string | null;
6
+ min?: number | null;
7
+ max?: number | null;
8
+ }
9
+ export interface FilterSuggestionResult {
10
+ filters: FilterSuggestion[];
11
+ /** What no filter covers, to search for; empty when the filters cover it all. */
12
+ text: string;
13
+ /** An order the description asked for, on a Sortable field. */
14
+ sort?: {
15
+ field: string;
16
+ descending: boolean;
17
+ } | null;
18
+ }
19
+ export declare function localFilters(text: string, facets: Record<string, {
20
+ key: string;
21
+ }[]> | null | undefined): FilterSuggestionResult | null;
22
+ export interface UseFilterSuggestionOptions {
23
+ /** The offer from `useSearchOrAsk`. With it, a description is turned into filters while the
24
+ * visitor types, the way the dataset's `filtersWhileTyping` says: facet values at once, the
25
+ * model for the rest, or nothing until Ask. */
26
+ auto?: SearchOrAskState;
27
+ }
28
+ /**
29
+ * Search or ask on a catalogue: turns a description of what to list ("private videregående i
30
+ * bergen") into filters on the dataset's own fields (`search-or-ask/filters`), and applies them
31
+ * to the search, where ActiveFiltersPanel shows them and the visitor can remove each. Ask for it
32
+ * when `useSearchOrAsk` says `describes`, or hand it the offer (`auto`) and it acts on its own.
33
+ */
34
+ export declare function useFilterSuggestion(options?: UseFilterSuggestionOptions): {
35
+ suggestion: FilterSuggestionResult | null;
36
+ forText: string;
37
+ loading: boolean;
38
+ suggest: (text: string) => Promise<void>;
39
+ apply: (result: FilterSuggestionResult) => void;
40
+ clear: () => void;
41
+ appliedText: string | null;
42
+ };
@@ -0,0 +1,52 @@
1
+ import { CutRule, Offer } from '../utils/searchOrAsk';
2
+ export interface UseSearchOrAskOptions {
3
+ /** false keeps the hook quiet: no settings read, no judgement. Default true. */
4
+ enabled?: boolean;
5
+ /** The text to judge. Defaults to the search query of the surrounding SearchProvider. */
6
+ text?: string;
7
+ /** How long the visitor must pause before the server is asked. Default 300 ms. */
8
+ pauseMs?: number;
9
+ }
10
+ export interface SearchOrAskState {
11
+ /** True once the dataset's settings say Search or ask is on and the instance can run it. Until
12
+ * then, and for good when it is off, the offer stays 'none' and nothing is sent. */
13
+ available: boolean;
14
+ /** What to show: nothing, a quiet "ask about this", or a strong offer where Enter asks. */
15
+ offer: Offer;
16
+ /** The latest probability from the server, for a readout; null before the first judgement. */
17
+ p: number | null;
18
+ /** The judge's reading of whether the top results could answer what is typed; null when it did not say. */
19
+ answerable: number | null;
20
+ /** The text the offer is about: what would be asked. */
21
+ text: string;
22
+ /** On a catalogue: the probability the visitor describes what to list. Null elsewhere. */
23
+ criteria: number | null;
24
+ /** On a catalogue: the text describes what to list, so it can become filters (useFilterSuggestion). */
25
+ describes: boolean;
26
+ /** The list should show coverage hits only: the offer is strong and the dataset has the rule on
27
+ * (`cutWhileAsking`). SearchResults applies it when handed this state; `visibleResults` is the rule. */
28
+ cutResults: boolean;
29
+ /** The cut's two lines, the dataset's, for `visibleResults`. */
30
+ cutRule: CutRule;
31
+ /** The page's searches run with whole-word coverage: the offer is strong and the dataset has
32
+ * that rule on. The hook sets and clears it on the search context itself. */
33
+ strictCoverage: boolean;
34
+ /** On a catalogue, what to do with a description while typing (the dataset's `filtersWhileTyping`):
35
+ * 'off' leaves the list until Ask, 'facets' applies typed facet values at once, 'model' also asks
36
+ * the filters route for the rest. `useFilterSuggestion({ auto })` acts on it. */
37
+ filtersWhileTyping: 'off' | 'facets' | 'model';
38
+ /** The offer is for what is in the field now: the field still extends the text it was judged on.
39
+ * Typing on from a question keeps it (the hook does not ask again while the text only grows);
40
+ * backspacing into it, or changing it, does not, until the judge has read the new text. Show an
41
+ * offer only while this holds, or it blinks out on every keystroke or lingers after a change. */
42
+ stands: boolean;
43
+ }
44
+ /**
45
+ * Decides, while the visitor types, whether to offer an answer beside the results. Reads the
46
+ * dataset's Search or ask settings once, then on each typing pause asks the server's judge
47
+ * (`search-or-ask/intent`) when the rules allow, keeps only the newest answer, and applies the
48
+ * dataset's thresholds with hysteresis. It never asks again while the text only grows and the
49
+ * offer is already strong. A failure of any kind leaves the offer at 'none': the search itself is
50
+ * never affected.
51
+ */
52
+ export declare function useSearchOrAsk(options?: UseSearchOrAskOptions): SearchOrAskState;