@mixpeek/react-searchkit 0.1.2

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 ADDED
@@ -0,0 +1,224 @@
1
+ # @mixpeek/react-searchkit
2
+
3
+ **React Components for Multimodal Search**
4
+
5
+ Composable search UI with stages streaming, AI answers, filters, and theming. Free and open source.
6
+
7
+ - Composable components — use the full widget or individual pieces
8
+ - Stages streaming — watch results flow through search → rerank → enrich in real time
9
+ - AI-generated answers with citations
10
+ - Cmd+K keyboard shortcut
11
+ - Light/dark theme with full customization
12
+ - React component + CDN script tag
13
+
14
+ **[Live demo: mxp.co/searchkit](https://mxp.co/searchkit)**
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install mixpeek @mixpeek/react-searchkit
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### React
25
+
26
+ ```tsx
27
+ import { SearchKit } from "@mixpeek/react-searchkit";
28
+ import "@mixpeek/react-searchkit/styles.css";
29
+
30
+ export default function App() {
31
+ return <SearchKit projectKey="your-retriever-slug" />;
32
+ }
33
+ ```
34
+
35
+ ### CDN (no build step)
36
+
37
+ ```html
38
+ <link rel="stylesheet" href="https://cdn.mixpeek.com/searchkit/v1/searchkit.css" />
39
+ <script src="https://cdn.mixpeek.com/searchkit/v1/loader.js"
40
+ data-project-key="your-retriever-slug"
41
+ data-mount="search-container">
42
+ </script>
43
+ ```
44
+
45
+ ## Bootstrap Your Pipeline
46
+
47
+ Don't have a retriever yet? Set up a complete AI search pipeline for your site using the Mixpeek JS SDK.
48
+
49
+ ### 1. Scrape & Index Your Site
50
+
51
+ Create a namespace, bucket, and collection with the web scraper extractor.
52
+
53
+ ```javascript
54
+ import Mixpeek from "mixpeek";
55
+ const mx = new Mixpeek({ apiKey: "YOUR_API_KEY" });
56
+
57
+ // Create a namespace with web scraper + text search
58
+ const namespace = await mx.namespaces.create({
59
+ namespace_name: "my-site",
60
+ feature_extractors: [
61
+ { feature_extractor_name: "web_scraper", version: "v1" },
62
+ { feature_extractor_name: "text_extractor", version: "v1" }
63
+ ]
64
+ });
65
+
66
+ // Create a bucket and upload your URLs
67
+ const bucket = await mx.buckets.create({ bucket_name: "pages" });
68
+ await mx.buckets.objects.create(bucket.bucket_id, {
69
+ blobs: [{ property: "url", type: "text", data: "https://yoursite.com" }]
70
+ });
71
+
72
+ // Create a collection to process pages
73
+ const collection = await mx.collections.create({
74
+ collection_name: "site-pages",
75
+ source: { type: "bucket", bucket_ids: [bucket.bucket_id] },
76
+ feature_extractor: {
77
+ feature_extractor_name: "web_scraper",
78
+ version: "v1",
79
+ input_mappings: { url: "url" }
80
+ }
81
+ });
82
+
83
+ // Trigger processing
84
+ await mx.collections.trigger(collection.collection_id);
85
+ ```
86
+
87
+ ### 2. Create a Retriever with Streaming Stages
88
+
89
+ Build a multi-stage search pipeline with semantic search and reranking. Each stage streams results to the UI in real time.
90
+
91
+ ```javascript
92
+ const retriever = await mx.retrievers.create({
93
+ retriever_name: "site-search",
94
+ collection_identifiers: ["site-pages"],
95
+ stages: [
96
+ {
97
+ stage_name: "search",
98
+ stage_type: "feature_search",
99
+ parameters: {
100
+ feature_uri: "mixpeek://text_extractor@v1/multilingual_e5_large_instruct_v1",
101
+ input_mode: "text",
102
+ limit: 20
103
+ }
104
+ },
105
+ {
106
+ stage_name: "rerank",
107
+ stage_type: "rerank",
108
+ parameters: { model: "mixpeek/reranker", top_k: 10 }
109
+ }
110
+ ]
111
+ });
112
+
113
+ // Publish it so the widget can access it
114
+ await mx.retrievers.publish(retriever.retriever_id, {
115
+ slug: "my-site-search"
116
+ });
117
+ ```
118
+
119
+ ### 3. Drop in the Widget
120
+
121
+ ```tsx
122
+ import { SearchKit } from "@mixpeek/react-searchkit";
123
+ import "@mixpeek/react-searchkit/styles.css";
124
+
125
+ export default function App() {
126
+ return <SearchKit projectKey="my-site-search" enableAIAnswer />;
127
+ }
128
+ ```
129
+
130
+ ## Composable Components
131
+
132
+ Use the full `<SearchKit />` widget or import individual pieces and compose your own UI.
133
+
134
+ ### Context Hook
135
+
136
+ Access search state from any child component:
137
+
138
+ ```tsx
139
+ import { useSearchKit } from "@mixpeek/react-searchkit";
140
+
141
+ function MyResults() {
142
+ const { query, results, isLoading, stages, open, close } = useSearchKit();
143
+
144
+ return (
145
+ <div>
146
+ {stages.map((stage) => (
147
+ <div key={stage.name}>
148
+ {stage.name}: {stage.status} ({stage.resultCount} results)
149
+ </div>
150
+ ))}
151
+ {results.map((result, i) => (
152
+ <div key={i}>{result.title}</div>
153
+ ))}
154
+ </div>
155
+ );
156
+ }
157
+ ```
158
+
159
+ ### Custom Result Rendering
160
+
161
+ ```tsx
162
+ <SearchKit
163
+ projectKey="my-retriever"
164
+ renderResult={(result, index) => (
165
+ <div className="my-result">
166
+ <h3>{result.title}</h3>
167
+ <p>{result.content}</p>
168
+ </div>
169
+ )}
170
+ />
171
+ ```
172
+
173
+ ### Field Mapping
174
+
175
+ Use `transformResults` to map your retriever's fields to the widget format:
176
+
177
+ ```tsx
178
+ <SearchKit
179
+ projectKey="my-retriever"
180
+ transformResults={(results) =>
181
+ results.map((r) => ({
182
+ ...r,
183
+ title: r.product_name || r.title,
184
+ content: r.description,
185
+ page_url: `/products/${r.id}`,
186
+ }))
187
+ }
188
+ />
189
+ ```
190
+
191
+ ## Props
192
+
193
+ | Prop | Type | Default | Description |
194
+ |------|------|---------|-------------|
195
+ | `projectKey` | `string` | required | Public retriever slug or `ret_sk_` API key |
196
+ | `placeholder` | `string` | `"Search..."` | Input placeholder text |
197
+ | `maxResults` | `number` | `10` | Maximum results to display |
198
+ | `theme` | `"light" \| "dark" \| "auto"` | `"auto"` | Color theme |
199
+ | `accentColor` | `string` | `"#6366f1"` | Accent color (hex) |
200
+ | `position` | `"modal" \| "inline"` | `"modal"` | Modal overlay or inline embed |
201
+ | `keyboardShortcut` | `boolean` | `true` | Enable Cmd+K / Ctrl+K |
202
+ | `showPoweredBy` | `boolean` | `true` | Show "Search by Mixpeek" badge |
203
+ | `enableAIAnswer` | `boolean` | `false` | Show AI-generated answer panel |
204
+ | `enableShareLinks` | `boolean` | `false` | Enable shareable search URLs |
205
+ | `onSearch` | `(query: string) => void` | - | Callback when search is performed |
206
+ | `onResultClick` | `(result, index) => void` | - | Callback when result is clicked |
207
+ | `onZeroResults` | `(query: string) => void` | - | Callback when no results found |
208
+ | `transformResults` | `(results) => results` | - | Transform results before rendering |
209
+ | `renderResult` | `(result, index) => ReactNode` | - | Custom result renderer |
210
+ | `retrieverSlug` | `string` | - | Retriever slug when using `ret_sk_` key |
211
+ | `apiBaseUrl` | `string` | `"https://api.mixpeek.com"` | Custom API URL |
212
+ | `className` | `string` | - | Additional CSS class |
213
+ | `defaultOpen` | `boolean` | `false` | Start with modal open |
214
+
215
+ ## Links
216
+
217
+ - [Connector page](https://mxp.co/searchkit)
218
+ - [GitHub](https://github.com/mixpeek/searchkit)
219
+ - [npm](https://www.npmjs.com/package/@mixpeek/react-searchkit)
220
+ - [Docs](https://mixpeek.com/docs)
221
+
222
+ ## License
223
+
224
+ MIT
@@ -0,0 +1,9 @@
1
+ import { default as React } from 'react';
2
+ import { AIAnswerData } from './types';
3
+
4
+ interface AIAnswerProps {
5
+ data: AIAnswerData;
6
+ onCitationClick?: (resultIndex: number) => void;
7
+ }
8
+ export declare const AIAnswer: React.FC<AIAnswerProps>;
9
+ export {};
@@ -0,0 +1,8 @@
1
+ import { default as React } from 'react';
2
+ import { CTAConfig } from './types';
3
+
4
+ interface IntentCTAProps {
5
+ config: CTAConfig;
6
+ }
7
+ export declare const IntentCTA: React.FC<IntentCTAProps>;
8
+ export {};
@@ -0,0 +1,9 @@
1
+ import { default as React } from 'react';
2
+ import { ThemeMode } from './types';
3
+
4
+ interface PoweredByProps {
5
+ /** Explicit theme override (CSS custom properties handle it by default) */
6
+ theme?: ThemeMode;
7
+ }
8
+ export declare const PoweredBy: React.FC<PoweredByProps>;
9
+ export {};
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+ import { SearchResult } from './types';
3
+
4
+ interface ResultCardProps {
5
+ result: SearchResult;
6
+ index: number;
7
+ query: string;
8
+ onResultClick?: (result: SearchResult, index: number) => void;
9
+ }
10
+ export declare const ResultCard: React.FC<ResultCardProps>;
11
+ export {};
@@ -0,0 +1,9 @@
1
+ import { default as React } from 'react';
2
+
3
+ interface SearchButtonProps {
4
+ onClick: () => void;
5
+ placeholder: string;
6
+ keyboardShortcut: boolean;
7
+ }
8
+ export declare const SearchButton: React.FC<SearchButtonProps>;
9
+ export {};
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+
3
+ interface SearchInputProps {
4
+ value: string;
5
+ onChange: (value: string) => void;
6
+ placeholder: string;
7
+ isLoading: boolean;
8
+ onClear: () => void;
9
+ }
10
+ export declare const SearchInput: React.FC<SearchInputProps>;
11
+ export {};
@@ -0,0 +1,35 @@
1
+ import { default as React } from 'react';
2
+ import { SearchKitProps, SearchContextValue } from './types';
3
+
4
+ /**
5
+ * Hook to access the SearchKit context from child components.
6
+ */
7
+ export declare function useSearchKit(): SearchContextValue;
8
+ /** @deprecated Use useSearchKit instead */
9
+ export declare const useMixpeekSearch: typeof useSearchKit;
10
+ /**
11
+ * SearchKit - React component library for multimodal search.
12
+ *
13
+ * Renders a search button that opens a search modal with stages streaming,
14
+ * AI answers, filters, recent searches, and theming.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * <SearchKit projectKey="my-retriever-slug" />
19
+ * ```
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * <SearchKit
24
+ * projectKey="ret_sk_abc123"
25
+ * theme="dark"
26
+ * placeholder="Search documentation..."
27
+ * maxResults={20}
28
+ * onResultClick={(result) => window.location.href = result.page_url}
29
+ * />
30
+ * ```
31
+ */
32
+ export declare const SearchKit: React.FC<SearchKitProps>;
33
+ /** @deprecated Use SearchKit instead */
34
+ export declare const MixpeekSearch: React.FC<SearchKitProps>;
35
+ export default SearchKit;
@@ -0,0 +1,28 @@
1
+ import { default as React, ReactNode } from 'react';
2
+ import { SearchResult, AIAnswerData, SearchResponseMetadata, CTAConfig, StageGroup } from './types';
3
+
4
+ interface SearchModalProps {
5
+ isOpen: boolean;
6
+ onClose: () => void;
7
+ query: string;
8
+ onQueryChange: (query: string) => void;
9
+ results: SearchResult[];
10
+ stages: StageGroup[];
11
+ isStreaming: boolean;
12
+ isLoading: boolean;
13
+ error: string | null;
14
+ aiAnswer: AIAnswerData | null;
15
+ metadata: SearchResponseMetadata | null;
16
+ placeholder: string;
17
+ showPoweredBy: boolean;
18
+ enableShareLinks: boolean;
19
+ enableAIAnswer: boolean;
20
+ recentSearches: string[];
21
+ onClearRecent: () => void;
22
+ onResultClick?: (result: SearchResult, index: number) => void;
23
+ renderResult?: (result: SearchResult, index: number) => ReactNode;
24
+ ctaConfig?: CTAConfig;
25
+ theme?: string;
26
+ }
27
+ export declare const SearchModal: React.FC<SearchModalProps>;
28
+ export {};
@@ -0,0 +1,14 @@
1
+ import { default as React, ReactNode } from 'react';
2
+ import { SearchResult, StageGroup } from './types';
3
+
4
+ interface SearchResultsProps {
5
+ results: SearchResult[];
6
+ stages: StageGroup[];
7
+ isStreaming: boolean;
8
+ query: string;
9
+ isLoading: boolean;
10
+ onResultClick?: (result: SearchResult, index: number) => void;
11
+ renderResult?: (result: SearchResult, index: number) => ReactNode;
12
+ }
13
+ export declare const SearchResults: React.FC<SearchResultsProps>;
14
+ export {};
@@ -0,0 +1,7 @@
1
+ import { default as React } from 'react';
2
+
3
+ interface ShareLinkProps {
4
+ query: string;
5
+ }
6
+ export declare const ShareLink: React.FC<ShareLinkProps>;
7
+ export {};
@@ -0,0 +1,7 @@
1
+ import { default as React } from 'react';
2
+
3
+ interface ZeroResultsProps {
4
+ query: string;
5
+ }
6
+ export declare const ZeroResults: React.FC<ZeroResultsProps>;
7
+ export {};
@@ -0,0 +1,44 @@
1
+ import { SearchResponse, StageEvent } from '../types';
2
+
3
+ interface MixpeekClientConfig {
4
+ projectKey: string;
5
+ baseUrl?: string;
6
+ /** When using a ret_sk_ key, specify the public retriever slug for the endpoint URL */
7
+ retrieverSlug?: string;
8
+ /**
9
+ * Bearer token for private API access (local dev / self-hosted).
10
+ * When set, uses `Authorization: Bearer <bearerToken>` instead of the public key header,
11
+ * and routes directly to the private `/v1/retrievers/{id}/execute` endpoint.
12
+ */
13
+ bearerToken?: string;
14
+ }
15
+ interface SearchParams {
16
+ query: string;
17
+ limit?: number;
18
+ stream?: boolean;
19
+ inputs?: Record<string, unknown>;
20
+ }
21
+ export declare class MixpeekClient {
22
+ private projectKey;
23
+ private baseUrl;
24
+ private retrieverSlug;
25
+ private bearerToken;
26
+ private abortController;
27
+ constructor(config: MixpeekClientConfig);
28
+ private isApiKey;
29
+ private getHeaders;
30
+ private getEndpoint;
31
+ /**
32
+ * Cancel any in-flight search request.
33
+ */
34
+ cancel(): void;
35
+ /**
36
+ * Execute a search query against the configured retriever.
37
+ */
38
+ search(params: SearchParams): Promise<SearchResponse>;
39
+ /**
40
+ * Execute a streaming search query. Yields StageEvent objects as the pipeline executes.
41
+ */
42
+ searchStream(params: SearchParams): AsyncGenerator<StageEvent>;
43
+ }
44
+ export {};
@@ -0,0 +1,10 @@
1
+ import { default as React } from 'react';
2
+ import { FacetFilterConfig } from '../types';
3
+
4
+ interface FacetFilterProps {
5
+ config: FacetFilterConfig;
6
+ value: unknown;
7
+ onChange: (field: string, value: unknown) => void;
8
+ }
9
+ export declare const FacetFilter: React.FC<FacetFilterProps>;
10
+ export {};
@@ -0,0 +1,12 @@
1
+ import { default as React } from 'react';
2
+ import { FilterPanelConfig } from '../types';
3
+
4
+ interface FilterPanelProps {
5
+ config: FilterPanelConfig;
6
+ filterInputs: Record<string, unknown>;
7
+ onFilterChange: (field: string, value: unknown) => void;
8
+ onClearAll?: () => void;
9
+ hasActiveFilters?: boolean;
10
+ }
11
+ export declare const FilterPanel: React.FC<FilterPanelProps>;
12
+ export {};
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+ import { RangeFilterConfig } from '../types';
3
+
4
+ interface RangeFilterProps {
5
+ config: RangeFilterConfig;
6
+ minValue?: number;
7
+ maxValue?: number;
8
+ onChange: (field: string, value: unknown) => void;
9
+ }
10
+ export declare const RangeFilter: React.FC<RangeFilterProps>;
11
+ export {};
@@ -0,0 +1,10 @@
1
+ import { default as React } from 'react';
2
+ import { SmartFilterConfig } from '../types';
3
+
4
+ interface SmartFilterProps {
5
+ config: SmartFilterConfig;
6
+ value?: string;
7
+ onChange: (field: string, value: unknown) => void;
8
+ }
9
+ export declare const SmartFilter: React.FC<SmartFilterProps>;
10
+ export {};
@@ -0,0 +1,4 @@
1
+ export { FilterPanel } from './FilterPanel';
2
+ export { FacetFilter } from './FacetFilter';
3
+ export { RangeFilter } from './RangeFilter';
4
+ export { SmartFilter } from './SmartFilter';
@@ -0,0 +1,10 @@
1
+ interface UseFiltersReturn {
2
+ filterInputs: Record<string, unknown>;
3
+ setFilter: (field: string, value: unknown) => void;
4
+ removeFilter: (field: string) => void;
5
+ clearFilters: () => void;
6
+ hasActiveFilters: boolean;
7
+ getFilter: (field: string) => unknown;
8
+ }
9
+ export declare function useFilters(defaultFilters?: Record<string, unknown>): UseFiltersReturn;
10
+ export {};
@@ -0,0 +1,12 @@
1
+ interface UseKeyboardShortcutOptions {
2
+ /** Whether the shortcut is enabled */
3
+ enabled: boolean;
4
+ /** Callback when shortcut is triggered */
5
+ onTrigger: () => void;
6
+ }
7
+ /**
8
+ * Listens for Cmd+K (Mac) or Ctrl+K (Windows/Linux) to trigger search.
9
+ * Also listens for Escape to close.
10
+ */
11
+ export declare function useKeyboardShortcut(options: UseKeyboardShortcutOptions): void;
12
+ export {};
@@ -0,0 +1,7 @@
1
+ interface UseRecentSearchesReturn {
2
+ recentSearches: string[];
3
+ addSearch: (query: string) => void;
4
+ clearRecentSearches: () => void;
5
+ }
6
+ export declare function useRecentSearches(): UseRecentSearchesReturn;
7
+ export {};
@@ -0,0 +1,22 @@
1
+ import { SearchResult, AIAnswerData, SearchResponseMetadata, SearchKitConfig, StageGroup } from '../types';
2
+
3
+ interface UseSearchOptions {
4
+ config: SearchKitConfig;
5
+ filterInputs?: Record<string, unknown>;
6
+ onSearch?: (query: string) => void;
7
+ onSearchExecuted?: (query: string) => void;
8
+ onZeroResults?: (query: string) => void;
9
+ transformResults?: (results: SearchResult[]) => SearchResult[];
10
+ }
11
+ interface UseSearchReturn {
12
+ results: SearchResult[];
13
+ stages: StageGroup[];
14
+ isLoading: boolean;
15
+ isStreaming: boolean;
16
+ error: string | null;
17
+ aiAnswer: AIAnswerData | null;
18
+ metadata: SearchResponseMetadata | null;
19
+ search: (query: string) => Promise<void>;
20
+ }
21
+ export declare function useSearch(options: UseSearchOptions): UseSearchReturn;
22
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @mixpeek/react-searchkit - React component library for multimodal search.
3
+ *
4
+ * Composable search UI with stages streaming, AI answers, filters, and theming.
5
+ *
6
+ * @example React usage:
7
+ * ```tsx
8
+ * import { SearchKit } from "@mixpeek/react-searchkit";
9
+ * import "@mixpeek/react-searchkit/styles.css";
10
+ *
11
+ * function App() {
12
+ * return <SearchKit projectKey="my-retriever-slug" />;
13
+ * }
14
+ * ```
15
+ *
16
+ * @example CDN usage:
17
+ * ```html
18
+ * <script src="https://cdn.mixpeek.com/searchkit/v1/searchkit.umd.js"></script>
19
+ * <div id="mixpeek-search" data-project-key="my-retriever-slug"></div>
20
+ * ```
21
+ */
22
+ export { SearchKit, MixpeekSearch, useSearchKit, useMixpeekSearch } from './SearchKit';
23
+ export { SearchButton } from './SearchButton';
24
+ export { SearchModal } from './SearchModal';
25
+ export { SearchInput } from './SearchInput';
26
+ export { AIAnswer } from './AIAnswer';
27
+ export { SearchResults } from './SearchResults';
28
+ export { ResultCard } from './ResultCard';
29
+ export { PoweredBy } from './PoweredBy';
30
+ export { ShareLink } from './ShareLink';
31
+ export { ZeroResults } from './ZeroResults';
32
+ export { IntentCTA } from './IntentCTA';
33
+ export { FilterPanel, FacetFilter, RangeFilter, SmartFilter } from './filters';
34
+ export { useSearch } from './hooks/useSearch';
35
+ export { useFilters } from './hooks/useFilters';
36
+ export { useKeyboardShortcut } from './hooks/useKeyboardShortcut';
37
+ export { useRecentSearches } from './hooks/useRecentSearches';
38
+ export { MixpeekClient } from './api/client';
39
+ export type { SearchKitProps, SearchKitProps as MixpeekSearchProps, SearchResult, AIAnswerData, AIAnswerCitation, SearchResponse, SearchResponseMetadata, CTAConfig, SearchContextValue, SearchKitConfig, SearchKitConfig as MixpeekSearchConfig, ThemeMode, Position, RecentSearch, StageEvent, StageStatistics, StageGroup, FacetOption, FacetFilterConfig, RangeFilterConfig, SmartFilterConfig, FilterConfig, FilterPanelConfig, } from './types';