@xyd-js/plugin-orama 0.1.0-build.157

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 xyd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,170 @@
1
+ import React, { useState, lazy, Suspense, useEffect, useCallback, useRef } from "react"
2
+ import { createPortal } from 'react-dom'
3
+ import { create, insertMultiple } from '@orama/orama'
4
+
5
+ import {
6
+ useColorScheme
7
+ } from "@xyd-js/components/writer";
8
+ import { SearchButton } from "@xyd-js/components/system"
9
+ import { useUXEvents } from "@xyd-js/analytics"
10
+
11
+ const OramaSearchBox = lazy(() => import('@orama/react-components').then(mod => ({ default: mod.OramaSearchBox })));
12
+
13
+ export default function OramaSearch() {
14
+ const [isSearchOpen, setIsSearchOpen] = useState(false);
15
+ const ux = useUXEvents()
16
+
17
+ const handleClick = useCallback(() => {
18
+ if (isSearchOpen) {
19
+ return
20
+ }
21
+
22
+ ux.docs.search.open({})
23
+ setIsSearchOpen(true)
24
+ }, [])
25
+
26
+ const onModalClosed = useCallback(() => {
27
+ setIsSearchOpen(false)
28
+ }, [])
29
+
30
+ return <>
31
+ <SearchButton onClick={handleClick} />
32
+
33
+ <Suspense>
34
+ <$OramaSearchBoxWrapper isSearchOpen={isSearchOpen} onModalClosed={onModalClosed} />
35
+ </Suspense>
36
+ </>
37
+ }
38
+
39
+ function $OramaSearchBoxWrapper({ isSearchOpen, onModalClosed }: { isSearchOpen: boolean, onModalClosed: () => void }) {
40
+ const [oramaLocalClientInstance, setOramaLocalClientInstance] = useState<any>(null);
41
+ const [pluginOptions, setPluginOptions] = useState<any>(null);
42
+ const [colorScheme] = useColorScheme()
43
+ const ux = useUXEvents()
44
+ const uxTyping = useUXTyping((term: string) => {
45
+ ux.docs.search.query_change({
46
+ term: term
47
+ })
48
+ })
49
+
50
+ async function loadData() {
51
+ // @ts-ignore
52
+ const oramaDataModule = await import('virtual:xyd-plugin-orama-data')
53
+
54
+ const oramaDocs = oramaDataModule.default.docs
55
+ const oramaCloudConfig = oramaDataModule.default.cloudConfig || null
56
+ const oramaSuggestions = oramaDataModule.default.suggestions || []
57
+
58
+ if (oramaCloudConfig) {
59
+ setPluginOptions({
60
+ cloudConfig: oramaCloudConfig,
61
+ suggestions: oramaSuggestions
62
+ })
63
+ return
64
+ }
65
+
66
+ const db = await createOramaInstance(oramaDocs)
67
+
68
+ setOramaLocalClientInstance(db)
69
+ setPluginOptions({
70
+ suggestions: oramaSuggestions
71
+ })
72
+ }
73
+
74
+ useEffect(() => {
75
+ loadData()
76
+ }, [])
77
+
78
+ if (!isSearchOpen) return null;
79
+
80
+ const searchBox = (oramaLocalClientInstance || pluginOptions?.cloudConfig) ? (
81
+ <OramaSearchBox
82
+ colorScheme={colorScheme || "system"}
83
+ open={isSearchOpen}
84
+ clientInstance={oramaLocalClientInstance}
85
+ onModalClosed={onModalClosed}
86
+ suggestions={pluginOptions.suggestions}
87
+ highlightTitle={{
88
+ caseSensitive: false,
89
+ HTMLTag: 'b',
90
+ }}
91
+ highlightDescription={{
92
+ caseSensitive: false,
93
+ HTMLTag: 'b',
94
+ }}
95
+ index={pluginOptions?.cloudConfig && {
96
+ endpoint: pluginOptions.cloudConfig.endpoint,
97
+ api_key: pluginOptions.cloudConfig.apiKey
98
+ }}
99
+ disableChat={!pluginOptions?.cloudConfig}
100
+ onSearchResultClick={(resp) => {
101
+ const result = resp.detail.result
102
+
103
+ ux.docs.search.result_click({
104
+ title: result.title,
105
+ description: result.description,
106
+ })
107
+ }}
108
+ onSearchCompleted={(resp) => {
109
+ const term = resp.detail.clientSearchParams?.term
110
+ uxTyping(term)
111
+ }}
112
+ />
113
+ ) : null;
114
+
115
+
116
+ return createPortal(searchBox, document.body);
117
+ }
118
+
119
+ async function createOramaInstance(oramaDocs: any[]): Promise<any> {
120
+ const db = create({
121
+ schema: {
122
+ category: "string",
123
+ path: "string",
124
+ title: "string",
125
+ description: "string",
126
+ content: "string"
127
+ },
128
+ plugins: [
129
+ // TODO: finish pluganalytics
130
+ ]
131
+ })
132
+
133
+ await insertMultiple(db, oramaDocs as any)
134
+
135
+ return db
136
+ }
137
+
138
+ // TODO: move to uxsdk
139
+ function useUXTyping(callback: (term: string) => void, delay: number = 500) {
140
+ const timeoutRef = useRef<number | null>(null)
141
+ const lastTermRef = useRef<string>('')
142
+
143
+ const trackTyping = useCallback((term?: any) => {
144
+ // Only track if term has actually changed and is a valid string
145
+ if (term && typeof term === 'string' && term !== lastTermRef.current) {
146
+ // Clear existing timeout only when term changes
147
+ if (timeoutRef.current) {
148
+ clearTimeout(timeoutRef.current)
149
+ }
150
+ lastTermRef.current = term
151
+
152
+ timeoutRef.current = window.setTimeout(() => {
153
+ if (term.trim()) {
154
+ callback(term.trim())
155
+ }
156
+ }, delay)
157
+ }
158
+ }, [callback, delay])
159
+
160
+ // Cleanup timeout on unmount
161
+ useEffect(() => {
162
+ return () => {
163
+ if (timeoutRef.current) {
164
+ clearTimeout(timeoutRef.current)
165
+ }
166
+ }
167
+ }, [])
168
+
169
+ return trackTyping
170
+ }
@@ -0,0 +1,11 @@
1
+ import { Plugin } from '@xyd-js/plugins';
2
+
3
+ interface OramaPluginOptions {
4
+ endpoint?: string;
5
+ apiKey?: string;
6
+ suggestions?: string[];
7
+ }
8
+
9
+ declare function OramaPlugin(pluginOptions?: OramaPluginOptions): Plugin;
10
+
11
+ export { OramaPlugin as default };
package/dist/index.js ADDED
@@ -0,0 +1,223 @@
1
+ // src/index.ts
2
+ import { mapSettingsToDocSections } from "@xyd-js/content/md";
3
+
4
+ // src/const.ts
5
+ var DEFAULT_SUGGESTIONS = [
6
+ "How to install xyd?",
7
+ "How to generate API Docs?",
8
+ "How to built-in components?"
9
+ ];
10
+
11
+ // src/Search.tsx
12
+ import React, { useState, lazy, Suspense, useEffect, useCallback, useRef } from "react";
13
+ import { createPortal } from "react-dom";
14
+ import { create, insertMultiple } from "@orama/orama";
15
+ import {
16
+ useColorScheme
17
+ } from "@xyd-js/components/writer";
18
+ import { SearchButton } from "@xyd-js/components/system";
19
+ import { useUXEvents } from "@xyd-js/analytics";
20
+ var OramaSearchBox = lazy(() => import("@orama/react-components").then((mod) => ({ default: mod.OramaSearchBox })));
21
+ function OramaSearch() {
22
+ const [isSearchOpen, setIsSearchOpen] = useState(false);
23
+ const ux = useUXEvents();
24
+ const handleClick = useCallback(() => {
25
+ if (isSearchOpen) {
26
+ return;
27
+ }
28
+ ux.docs.search.open({});
29
+ setIsSearchOpen(true);
30
+ }, []);
31
+ const onModalClosed = useCallback(() => {
32
+ setIsSearchOpen(false);
33
+ }, []);
34
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SearchButton, { onClick: handleClick }), /* @__PURE__ */ React.createElement(Suspense, null, /* @__PURE__ */ React.createElement($OramaSearchBoxWrapper, { isSearchOpen, onModalClosed })));
35
+ }
36
+ function $OramaSearchBoxWrapper({ isSearchOpen, onModalClosed }) {
37
+ const [oramaLocalClientInstance, setOramaLocalClientInstance] = useState(null);
38
+ const [pluginOptions, setPluginOptions] = useState(null);
39
+ const [colorScheme] = useColorScheme();
40
+ const ux = useUXEvents();
41
+ const uxTyping = useUXTyping((term) => {
42
+ ux.docs.search.query_change({
43
+ term
44
+ });
45
+ });
46
+ async function loadData() {
47
+ const oramaDataModule = await import("virtual:xyd-plugin-orama-data");
48
+ const oramaDocs = oramaDataModule.default.docs;
49
+ const oramaCloudConfig = oramaDataModule.default.cloudConfig || null;
50
+ const oramaSuggestions = oramaDataModule.default.suggestions || [];
51
+ if (oramaCloudConfig) {
52
+ setPluginOptions({
53
+ cloudConfig: oramaCloudConfig,
54
+ suggestions: oramaSuggestions
55
+ });
56
+ return;
57
+ }
58
+ const db = await createOramaInstance(oramaDocs);
59
+ setOramaLocalClientInstance(db);
60
+ setPluginOptions({
61
+ suggestions: oramaSuggestions
62
+ });
63
+ }
64
+ useEffect(() => {
65
+ loadData();
66
+ }, []);
67
+ if (!isSearchOpen) return null;
68
+ const searchBox = oramaLocalClientInstance || pluginOptions?.cloudConfig ? /* @__PURE__ */ React.createElement(
69
+ OramaSearchBox,
70
+ {
71
+ colorScheme: colorScheme || "system",
72
+ open: isSearchOpen,
73
+ clientInstance: oramaLocalClientInstance,
74
+ onModalClosed,
75
+ suggestions: pluginOptions.suggestions,
76
+ highlightTitle: {
77
+ caseSensitive: false,
78
+ HTMLTag: "b"
79
+ },
80
+ highlightDescription: {
81
+ caseSensitive: false,
82
+ HTMLTag: "b"
83
+ },
84
+ index: pluginOptions?.cloudConfig && {
85
+ endpoint: pluginOptions.cloudConfig.endpoint,
86
+ api_key: pluginOptions.cloudConfig.apiKey
87
+ },
88
+ disableChat: !pluginOptions?.cloudConfig,
89
+ onSearchResultClick: (resp) => {
90
+ const result = resp.detail.result;
91
+ ux.docs.search.result_click({
92
+ title: result.title,
93
+ description: result.description
94
+ });
95
+ },
96
+ onSearchCompleted: (resp) => {
97
+ const term = resp.detail.clientSearchParams?.term;
98
+ uxTyping(term);
99
+ }
100
+ }
101
+ ) : null;
102
+ return createPortal(searchBox, document.body);
103
+ }
104
+ async function createOramaInstance(oramaDocs) {
105
+ const db = create({
106
+ schema: {
107
+ category: "string",
108
+ path: "string",
109
+ title: "string",
110
+ description: "string",
111
+ content: "string"
112
+ },
113
+ plugins: [
114
+ // TODO: finish pluganalytics
115
+ ]
116
+ });
117
+ await insertMultiple(db, oramaDocs);
118
+ return db;
119
+ }
120
+ function useUXTyping(callback, delay = 500) {
121
+ const timeoutRef = useRef(null);
122
+ const lastTermRef = useRef("");
123
+ const trackTyping = useCallback((term) => {
124
+ if (term && typeof term === "string" && term !== lastTermRef.current) {
125
+ if (timeoutRef.current) {
126
+ clearTimeout(timeoutRef.current);
127
+ }
128
+ lastTermRef.current = term;
129
+ timeoutRef.current = window.setTimeout(() => {
130
+ if (term.trim()) {
131
+ callback(term.trim());
132
+ }
133
+ }, delay);
134
+ }
135
+ }, [callback, delay]);
136
+ useEffect(() => {
137
+ return () => {
138
+ if (timeoutRef.current) {
139
+ clearTimeout(timeoutRef.current);
140
+ }
141
+ };
142
+ }, []);
143
+ return trackTyping;
144
+ }
145
+
146
+ // src/index.ts
147
+ function OramaPlugin(pluginOptions = {}) {
148
+ return function(settings) {
149
+ return {
150
+ name: "plugin-orama",
151
+ vite: [
152
+ vitePlugin(
153
+ settings,
154
+ pluginOptions
155
+ )
156
+ ],
157
+ components: [
158
+ {
159
+ component: OramaSearch,
160
+ name: "Search",
161
+ dist: "@xyd-js/plugin-orama/Search"
162
+ // TODO: better in the future
163
+ }
164
+ ]
165
+ };
166
+ };
167
+ }
168
+ function vitePlugin(settings, pluginOptions = {}) {
169
+ const virtualModuleId = "virtual:xyd-plugin-orama-data";
170
+ const resolvedVirtualModuleId = `\0${virtualModuleId}`;
171
+ let resolveConfig = null;
172
+ return {
173
+ name: "xyd-plugin-orama",
174
+ enforce: "pre",
175
+ async configResolved(config) {
176
+ if (resolveConfig) {
177
+ return;
178
+ }
179
+ resolveConfig = config;
180
+ },
181
+ async resolveId(id) {
182
+ if (id === virtualModuleId) {
183
+ return resolvedVirtualModuleId;
184
+ }
185
+ },
186
+ async load(id) {
187
+ if (id !== resolvedVirtualModuleId) {
188
+ return;
189
+ }
190
+ let cloudConfig = null;
191
+ if (pluginOptions.endpoint && pluginOptions.apiKey) {
192
+ cloudConfig = {
193
+ endpoint: pluginOptions.endpoint,
194
+ apiKey: pluginOptions.apiKey
195
+ };
196
+ }
197
+ const sections = (await mapSettingsToDocSections(settings)).map(mapDocSectionsToOrama);
198
+ return `
199
+ const docs = ${JSON.stringify(sections)};
200
+ const cloudConfig = ${JSON.stringify(cloudConfig)};
201
+ const suggestions = ${JSON.stringify(pluginOptions.suggestions || DEFAULT_SUGGESTIONS)};
202
+
203
+ export default { docs, cloudConfig, suggestions };
204
+ `;
205
+ }
206
+ };
207
+ }
208
+ function mapDocSectionsToOrama(doc) {
209
+ return {
210
+ category: "",
211
+ // TODO: finish
212
+ path: doc.pageUrl,
213
+ title: doc.headingTitle,
214
+ description: doc.content,
215
+ content: doc.content
216
+ // section: doc.content,
217
+ // version: string
218
+ };
219
+ }
220
+ export {
221
+ OramaPlugin as default
222
+ };
223
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/const.ts","../src/Search.tsx"],"sourcesContent":["import type {Plugin as VitePlugin, ResolvedConfig} from 'vite'\n\nimport type {Settings} from '@xyd-js/core'\nimport type {Plugin} from '@xyd-js/plugins'\nimport {mapSettingsToDocSections, type DocSectionSchema} from '@xyd-js/content/md'\n\nimport {DEFAULT_SUGGESTIONS} from './const'\nimport type {OramaPluginOptions, OramaCloudConfig, OramaSectionSchema} from './types'\nimport Search from './Search'\n\nexport default function OramaPlugin(\n pluginOptions: OramaPluginOptions = {}\n): Plugin {\n return function (settings: Settings) {\n return {\n name: \"plugin-orama\",\n vite: [\n vitePlugin(\n settings,\n pluginOptions,\n )\n ],\n components: [\n {\n component: Search,\n name: \"Search\",\n dist: \"@xyd-js/plugin-orama/Search\" // TODO: better in the future\n }\n ]\n }\n }\n}\n\nfunction vitePlugin(\n settings: Settings,\n pluginOptions: OramaPluginOptions = {}\n): VitePlugin {\n const virtualModuleId = 'virtual:xyd-plugin-orama-data'\n const resolvedVirtualModuleId = `\\0${virtualModuleId}`\n\n let resolveConfig: ResolvedConfig | null = null\n\n return {\n name: 'xyd-plugin-orama',\n enforce: 'pre',\n\n async configResolved(config: ResolvedConfig) {\n if (resolveConfig) {\n return\n }\n\n resolveConfig = config\n },\n\n async resolveId(id) {\n if (id === virtualModuleId) {\n return resolvedVirtualModuleId\n }\n },\n\n async load(this, id: string) {\n if (id !== resolvedVirtualModuleId) {\n return\n }\n\n let cloudConfig: OramaCloudConfig | null = null\n if (pluginOptions.endpoint && pluginOptions.apiKey) {\n cloudConfig = {\n endpoint: pluginOptions.endpoint,\n apiKey: pluginOptions.apiKey\n }\n }\n\n const sections = (await mapSettingsToDocSections(settings)).map(mapDocSectionsToOrama)\n\n return `\n const docs = ${JSON.stringify(sections)};\n const cloudConfig = ${JSON.stringify(cloudConfig)};\n const suggestions = ${JSON.stringify(pluginOptions.suggestions || DEFAULT_SUGGESTIONS)};\n\n export default { docs, cloudConfig, suggestions };\n `\n }\n }\n}\n\nfunction mapDocSectionsToOrama(doc: DocSectionSchema): OramaSectionSchema {\n return {\n category: '', // TODO: finish\n\n path: doc.pageUrl,\n\n title: doc.headingTitle,\n\n description: doc.content,\n\n content: doc.content,\n\n // section: doc.content,\n // version: string\n }\n}\n\n","export const DEFAULT_SUGGESTIONS = [\n \"How to install xyd?\",\n \"How to generate API Docs?\",\n \"How to built-in components?\",\n]","import React, { useState, lazy, Suspense, useEffect, useCallback, useRef } from \"react\"\nimport { createPortal } from 'react-dom'\nimport { create, insertMultiple } from '@orama/orama'\n\nimport {\n useColorScheme\n} from \"@xyd-js/components/writer\";\nimport { SearchButton } from \"@xyd-js/components/system\"\nimport { useUXEvents } from \"@xyd-js/analytics\"\n\nconst OramaSearchBox = lazy(() => import('@orama/react-components').then(mod => ({ default: mod.OramaSearchBox })));\n\nexport default function OramaSearch() {\n const [isSearchOpen, setIsSearchOpen] = useState(false);\n const ux = useUXEvents()\n\n const handleClick = useCallback(() => {\n if (isSearchOpen) {\n return\n }\n\n ux.docs.search.open({})\n setIsSearchOpen(true)\n }, [])\n\n const onModalClosed = useCallback(() => {\n setIsSearchOpen(false)\n }, [])\n\n return <>\n <SearchButton onClick={handleClick} />\n\n <Suspense>\n <$OramaSearchBoxWrapper isSearchOpen={isSearchOpen} onModalClosed={onModalClosed} />\n </Suspense>\n </>\n}\n\nfunction $OramaSearchBoxWrapper({ isSearchOpen, onModalClosed }: { isSearchOpen: boolean, onModalClosed: () => void }) {\n const [oramaLocalClientInstance, setOramaLocalClientInstance] = useState<any>(null);\n const [pluginOptions, setPluginOptions] = useState<any>(null);\n const [colorScheme] = useColorScheme()\n const ux = useUXEvents()\n const uxTyping = useUXTyping((term: string) => {\n ux.docs.search.query_change({\n term: term\n })\n })\n\n async function loadData() {\n // @ts-ignore\n const oramaDataModule = await import('virtual:xyd-plugin-orama-data')\n\n const oramaDocs = oramaDataModule.default.docs\n const oramaCloudConfig = oramaDataModule.default.cloudConfig || null\n const oramaSuggestions = oramaDataModule.default.suggestions || []\n\n if (oramaCloudConfig) {\n setPluginOptions({\n cloudConfig: oramaCloudConfig,\n suggestions: oramaSuggestions\n })\n return\n }\n\n const db = await createOramaInstance(oramaDocs)\n\n setOramaLocalClientInstance(db)\n setPluginOptions({\n suggestions: oramaSuggestions\n })\n }\n\n useEffect(() => {\n loadData()\n }, [])\n\n if (!isSearchOpen) return null;\n\n const searchBox = (oramaLocalClientInstance || pluginOptions?.cloudConfig) ? (\n <OramaSearchBox\n colorScheme={colorScheme || \"system\"}\n open={isSearchOpen}\n clientInstance={oramaLocalClientInstance}\n onModalClosed={onModalClosed}\n suggestions={pluginOptions.suggestions}\n highlightTitle={{\n caseSensitive: false,\n HTMLTag: 'b',\n }}\n highlightDescription={{\n caseSensitive: false,\n HTMLTag: 'b',\n }}\n index={pluginOptions?.cloudConfig && {\n endpoint: pluginOptions.cloudConfig.endpoint,\n api_key: pluginOptions.cloudConfig.apiKey\n }}\n disableChat={!pluginOptions?.cloudConfig}\n onSearchResultClick={(resp) => {\n const result = resp.detail.result\n\n ux.docs.search.result_click({\n title: result.title,\n description: result.description,\n })\n }}\n onSearchCompleted={(resp) => {\n const term = resp.detail.clientSearchParams?.term\n uxTyping(term)\n }}\n />\n ) : null;\n\n\n return createPortal(searchBox, document.body);\n}\n\nasync function createOramaInstance(oramaDocs: any[]): Promise<any> {\n const db = create({\n schema: {\n category: \"string\",\n path: \"string\",\n title: \"string\",\n description: \"string\",\n content: \"string\"\n },\n plugins: [\n // TODO: finish pluganalytics\n ]\n })\n\n await insertMultiple(db, oramaDocs as any)\n\n return db\n}\n\n// TODO: move to uxsdk\nfunction useUXTyping(callback: (term: string) => void, delay: number = 500) {\n const timeoutRef = useRef<number | null>(null)\n const lastTermRef = useRef<string>('')\n\n const trackTyping = useCallback((term?: any) => {\n // Only track if term has actually changed and is a valid string\n if (term && typeof term === 'string' && term !== lastTermRef.current) {\n // Clear existing timeout only when term changes\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n }\n lastTermRef.current = term\n \n timeoutRef.current = window.setTimeout(() => {\n if (term.trim()) {\n callback(term.trim())\n }\n }, delay)\n }\n }, [callback, delay])\n\n // Cleanup timeout on unmount\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n }\n }\n }, [])\n\n return trackTyping\n}"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@xyd-js/plugin-orama",
3
+ "version": "0.1.0-build.157",
4
+ "author": "",
5
+ "description": "",
6
+ "license": "MIT",
7
+ "main": "./dist/index.js",
8
+ "type": "module",
9
+ "exports": {
10
+ "./package.json": "./package.json",
11
+ ".": {
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./Search": {
15
+ "import": "./dist/Search.tsx"
16
+ }
17
+ },
18
+ "dependencies": {
19
+ "@orama/orama": "^3.1.6",
20
+ "@orama/plugin-data-persistence": "^3.1.6",
21
+ "@orama/react-components": "^0.7.0",
22
+ "@orama/searchbox": "1.0.0-rc53",
23
+ "slugify": "^1.6.6",
24
+ "unified": "^11.0.4",
25
+ "remark-parse": "^11.0.0",
26
+ "remark-rehype": "^11.0.0",
27
+ "rehype-stringify": "^10.0.0",
28
+ "@xyd-js/core": "0.1.0-build.170",
29
+ "@xyd-js/content": "0.1.0-build.171",
30
+ "@xyd-js/plugins": "0.1.0-build.157",
31
+ "@xyd-js/components": "0.1.0-build.168"
32
+ },
33
+ "peerDependencies": {
34
+ "react": "^19.1.0",
35
+ "react-dom": "^19.1.0",
36
+ "@xyd-js/analytics": "0.1.0-build.157"
37
+ },
38
+ "devDependencies": {
39
+ "vite": "^7.0.0",
40
+ "@vitest/coverage-v8": "^1.6.1",
41
+ "rimraf": "^3.0.2",
42
+ "tsup": "^8.4.0",
43
+ "vitest": "^1.6.1"
44
+ },
45
+ "scripts": {
46
+ "clean": "rimraf build",
47
+ "prebuild": "pnpm clean",
48
+ "build": "tsup",
49
+ "test": "vitest",
50
+ "test:coverage": "vitest run --coverage"
51
+ }
52
+ }
package/src/Search.tsx ADDED
@@ -0,0 +1,170 @@
1
+ import React, { useState, lazy, Suspense, useEffect, useCallback, useRef } from "react"
2
+ import { createPortal } from 'react-dom'
3
+ import { create, insertMultiple } from '@orama/orama'
4
+
5
+ import {
6
+ useColorScheme
7
+ } from "@xyd-js/components/writer";
8
+ import { SearchButton } from "@xyd-js/components/system"
9
+ import { useUXEvents } from "@xyd-js/analytics"
10
+
11
+ const OramaSearchBox = lazy(() => import('@orama/react-components').then(mod => ({ default: mod.OramaSearchBox })));
12
+
13
+ export default function OramaSearch() {
14
+ const [isSearchOpen, setIsSearchOpen] = useState(false);
15
+ const ux = useUXEvents()
16
+
17
+ const handleClick = useCallback(() => {
18
+ if (isSearchOpen) {
19
+ return
20
+ }
21
+
22
+ ux.docs.search.open({})
23
+ setIsSearchOpen(true)
24
+ }, [])
25
+
26
+ const onModalClosed = useCallback(() => {
27
+ setIsSearchOpen(false)
28
+ }, [])
29
+
30
+ return <>
31
+ <SearchButton onClick={handleClick} />
32
+
33
+ <Suspense>
34
+ <$OramaSearchBoxWrapper isSearchOpen={isSearchOpen} onModalClosed={onModalClosed} />
35
+ </Suspense>
36
+ </>
37
+ }
38
+
39
+ function $OramaSearchBoxWrapper({ isSearchOpen, onModalClosed }: { isSearchOpen: boolean, onModalClosed: () => void }) {
40
+ const [oramaLocalClientInstance, setOramaLocalClientInstance] = useState<any>(null);
41
+ const [pluginOptions, setPluginOptions] = useState<any>(null);
42
+ const [colorScheme] = useColorScheme()
43
+ const ux = useUXEvents()
44
+ const uxTyping = useUXTyping((term: string) => {
45
+ ux.docs.search.query_change({
46
+ term: term
47
+ })
48
+ })
49
+
50
+ async function loadData() {
51
+ // @ts-ignore
52
+ const oramaDataModule = await import('virtual:xyd-plugin-orama-data')
53
+
54
+ const oramaDocs = oramaDataModule.default.docs
55
+ const oramaCloudConfig = oramaDataModule.default.cloudConfig || null
56
+ const oramaSuggestions = oramaDataModule.default.suggestions || []
57
+
58
+ if (oramaCloudConfig) {
59
+ setPluginOptions({
60
+ cloudConfig: oramaCloudConfig,
61
+ suggestions: oramaSuggestions
62
+ })
63
+ return
64
+ }
65
+
66
+ const db = await createOramaInstance(oramaDocs)
67
+
68
+ setOramaLocalClientInstance(db)
69
+ setPluginOptions({
70
+ suggestions: oramaSuggestions
71
+ })
72
+ }
73
+
74
+ useEffect(() => {
75
+ loadData()
76
+ }, [])
77
+
78
+ if (!isSearchOpen) return null;
79
+
80
+ const searchBox = (oramaLocalClientInstance || pluginOptions?.cloudConfig) ? (
81
+ <OramaSearchBox
82
+ colorScheme={colorScheme || "system"}
83
+ open={isSearchOpen}
84
+ clientInstance={oramaLocalClientInstance}
85
+ onModalClosed={onModalClosed}
86
+ suggestions={pluginOptions.suggestions}
87
+ highlightTitle={{
88
+ caseSensitive: false,
89
+ HTMLTag: 'b',
90
+ }}
91
+ highlightDescription={{
92
+ caseSensitive: false,
93
+ HTMLTag: 'b',
94
+ }}
95
+ index={pluginOptions?.cloudConfig && {
96
+ endpoint: pluginOptions.cloudConfig.endpoint,
97
+ api_key: pluginOptions.cloudConfig.apiKey
98
+ }}
99
+ disableChat={!pluginOptions?.cloudConfig}
100
+ onSearchResultClick={(resp) => {
101
+ const result = resp.detail.result
102
+
103
+ ux.docs.search.result_click({
104
+ title: result.title,
105
+ description: result.description,
106
+ })
107
+ }}
108
+ onSearchCompleted={(resp) => {
109
+ const term = resp.detail.clientSearchParams?.term
110
+ uxTyping(term)
111
+ }}
112
+ />
113
+ ) : null;
114
+
115
+
116
+ return createPortal(searchBox, document.body);
117
+ }
118
+
119
+ async function createOramaInstance(oramaDocs: any[]): Promise<any> {
120
+ const db = create({
121
+ schema: {
122
+ category: "string",
123
+ path: "string",
124
+ title: "string",
125
+ description: "string",
126
+ content: "string"
127
+ },
128
+ plugins: [
129
+ // TODO: finish pluganalytics
130
+ ]
131
+ })
132
+
133
+ await insertMultiple(db, oramaDocs as any)
134
+
135
+ return db
136
+ }
137
+
138
+ // TODO: move to uxsdk
139
+ function useUXTyping(callback: (term: string) => void, delay: number = 500) {
140
+ const timeoutRef = useRef<number | null>(null)
141
+ const lastTermRef = useRef<string>('')
142
+
143
+ const trackTyping = useCallback((term?: any) => {
144
+ // Only track if term has actually changed and is a valid string
145
+ if (term && typeof term === 'string' && term !== lastTermRef.current) {
146
+ // Clear existing timeout only when term changes
147
+ if (timeoutRef.current) {
148
+ clearTimeout(timeoutRef.current)
149
+ }
150
+ lastTermRef.current = term
151
+
152
+ timeoutRef.current = window.setTimeout(() => {
153
+ if (term.trim()) {
154
+ callback(term.trim())
155
+ }
156
+ }, delay)
157
+ }
158
+ }, [callback, delay])
159
+
160
+ // Cleanup timeout on unmount
161
+ useEffect(() => {
162
+ return () => {
163
+ if (timeoutRef.current) {
164
+ clearTimeout(timeoutRef.current)
165
+ }
166
+ }
167
+ }, [])
168
+
169
+ return trackTyping
170
+ }
package/src/const.ts ADDED
@@ -0,0 +1,5 @@
1
+ export const DEFAULT_SUGGESTIONS = [
2
+ "How to install xyd?",
3
+ "How to generate API Docs?",
4
+ "How to built-in components?",
5
+ ]
package/src/index.css ADDED
@@ -0,0 +1,3 @@
1
+ .TestAbc123 {
2
+ color: red;
3
+ }