@preply/ds-ai-core 11.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/AGENTS.md +7 -0
- package/README.md +34 -0
- package/dist/event-tracking.d.ts +21 -0
- package/dist/event-tracking.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38209 -0
- package/dist/tools/components/__tests__/get-component-docs.test.d.ts +2 -0
- package/dist/tools/components/__tests__/get-component-docs.test.d.ts.map +1 -0
- package/dist/tools/components/__tests__/list-components.test.d.ts +2 -0
- package/dist/tools/components/__tests__/list-components.test.d.ts.map +1 -0
- package/dist/tools/components/context/__test__/get-components-data.test.d.ts +2 -0
- package/dist/tools/components/context/__test__/get-components-data.test.d.ts.map +1 -0
- package/dist/tools/components/context/__test__/test-components/FixtureButton.d.ts +15 -0
- package/dist/tools/components/context/__test__/test-components/FixtureButton.d.ts.map +1 -0
- package/dist/tools/components/context/__test__/test-components/index.d.ts +2 -0
- package/dist/tools/components/context/__test__/test-components/index.d.ts.map +1 -0
- package/dist/tools/components/context/extract-component-docgen-info.d.ts +3 -0
- package/dist/tools/components/context/extract-component-docgen-info.d.ts.map +1 -0
- package/dist/tools/components/context/get-components-data.d.ts +24 -0
- package/dist/tools/components/context/get-components-data.d.ts.map +1 -0
- package/dist/tools/components/context/index.d.ts +6 -0
- package/dist/tools/components/context/index.d.ts.map +1 -0
- package/dist/tools/components/context/render-component-doc.d.ts +4 -0
- package/dist/tools/components/context/render-component-doc.d.ts.map +1 -0
- package/dist/tools/components/get-component-docs.d.ts +15 -0
- package/dist/tools/components/get-component-docs.d.ts.map +1 -0
- package/dist/tools/components/list-components.d.ts +9 -0
- package/dist/tools/components/list-components.d.ts.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/search-icon/__tests__/get-icons-data.test.d.ts +2 -0
- package/dist/tools/search-icon/__tests__/get-icons-data.test.d.ts.map +1 -0
- package/dist/tools/search-icon/__tests__/search-icon.test.d.ts +2 -0
- package/dist/tools/search-icon/__tests__/search-icon.test.d.ts.map +1 -0
- package/dist/tools/search-icon/get-icons-data.d.ts +6 -0
- package/dist/tools/search-icon/get-icons-data.d.ts.map +1 -0
- package/dist/tools/search-icon/search-icon.d.ts +18 -0
- package/dist/tools/search-icon/search-icon.d.ts.map +1 -0
- package/dist/tools/search-token/__tests__/search-token-by-name.test.d.ts +2 -0
- package/dist/tools/search-token/__tests__/search-token-by-name.test.d.ts.map +1 -0
- package/dist/tools/search-token/__tests__/search-token-by-value.test.d.ts +2 -0
- package/dist/tools/search-token/__tests__/search-token-by-value.test.d.ts.map +1 -0
- package/dist/tools/search-token/context/__tests__/get-tokens-data.test.d.ts +2 -0
- package/dist/tools/search-token/context/__tests__/get-tokens-data.test.d.ts.map +1 -0
- package/dist/tools/search-token/context/get-tokens-data.d.ts +13 -0
- package/dist/tools/search-token/context/get-tokens-data.d.ts.map +1 -0
- package/dist/tools/search-token/context/index.d.ts +6 -0
- package/dist/tools/search-token/context/index.d.ts.map +1 -0
- package/dist/tools/search-token/search-token-by-name.d.ts +26 -0
- package/dist/tools/search-token/search-token-by-name.d.ts.map +1 -0
- package/dist/tools/search-token/search-token-by-value.d.ts +24 -0
- package/dist/tools/search-token/search-token-by-value.d.ts.map +1 -0
- package/dist/tools/search-token/token-utils.d.ts +9 -0
- package/dist/tools/search-token/token-utils.d.ts.map +1 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/create-search-index.d.ts +48 -0
- package/dist/utils/create-search-index.d.ts.map +1 -0
- package/dist/utils/define-tool.d.ts +49 -0
- package/dist/utils/define-tool.d.ts.map +1 -0
- package/dist/utils/format-list.d.ts +26 -0
- package/dist/utils/format-list.d.ts.map +1 -0
- package/dist/utils/md.d.ts +24 -0
- package/dist/utils/md.d.ts.map +1 -0
- package/package.json +48 -0
- package/src/event-tracking.ts +117 -0
- package/src/index.ts +4 -0
- package/src/tools/components/__tests__/get-component-docs.test.ts +58 -0
- package/src/tools/components/__tests__/list-components.test.ts +63 -0
- package/src/tools/components/context/__test__/get-components-data.test.ts +57 -0
- package/src/tools/components/context/__test__/test-components/FixtureButton.tsx +18 -0
- package/src/tools/components/context/__test__/test-components/index.ts +1 -0
- package/src/tools/components/context/__test__/test-components/tsconfig.json +11 -0
- package/src/tools/components/context/extract-component-docgen-info.ts +108 -0
- package/src/tools/components/context/get-components-data.ts +94 -0
- package/src/tools/components/context/index.ts +4 -0
- package/src/tools/components/context/render-component-doc.ts +89 -0
- package/src/tools/components/get-component-docs.ts +26 -0
- package/src/tools/components/list-components.ts +36 -0
- package/src/tools/index.ts +5 -0
- package/src/tools/search-icon/__tests__/get-icons-data.test.ts +22 -0
- package/src/tools/search-icon/__tests__/search-icon.test.ts +235 -0
- package/src/tools/search-icon/__tests__/test-icons/NotIcon.md +1 -0
- package/src/tools/search-icon/__tests__/test-icons/OtherIcon.svg +1 -0
- package/src/tools/search-icon/__tests__/test-icons/TokyoUIClose.svg +1 -0
- package/src/tools/search-icon/__tests__/test-icons/TokyoUIHelp.svg +1 -0
- package/src/tools/search-icon/get-icons-data.ts +19 -0
- package/src/tools/search-icon/search-icon.ts +100 -0
- package/src/tools/search-token/__tests__/search-token-by-name.test.ts +384 -0
- package/src/tools/search-token/__tests__/search-token-by-value.test.ts +250 -0
- package/src/tools/search-token/context/__tests__/get-tokens-data.test.ts +148 -0
- package/src/tools/search-token/context/get-tokens-data.ts +103 -0
- package/src/tools/search-token/context/index.ts +4 -0
- package/src/tools/search-token/search-token-by-name.ts +110 -0
- package/src/tools/search-token/search-token-by-value.ts +107 -0
- package/src/tools/search-token/token-utils.ts +60 -0
- package/src/types.ts +3 -0
- package/src/utils/create-search-index.ts +121 -0
- package/src/utils/define-tool.ts +67 -0
- package/src/utils/format-list.ts +38 -0
- package/src/utils/md.ts +12 -0
- package/tsconfig.json +11 -0
- package/vite.config.ts +23 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PLATFORMS } from '../../types';
|
|
2
|
+
import dedent from 'dedent';
|
|
3
|
+
import { defineTool } from '../../utils/define-tool';
|
|
4
|
+
import * as z from 'zod';
|
|
5
|
+
import { formatList } from '../../utils/format-list';
|
|
6
|
+
import data from './context';
|
|
7
|
+
|
|
8
|
+
export const listComponents = defineTool({
|
|
9
|
+
name: 'list_components',
|
|
10
|
+
description: dedent`
|
|
11
|
+
Returns a list of all available components in the Preply design system,
|
|
12
|
+
available in @preply/ds-web-lib, @preply/ds-web-root and @preply/ds-rn-lib packages.
|
|
13
|
+
`,
|
|
14
|
+
options: z.object({
|
|
15
|
+
platform: z.enum(PLATFORMS).describe('Target platform to list components for. Required.'),
|
|
16
|
+
}),
|
|
17
|
+
callback: ({ platform }) => {
|
|
18
|
+
const componentsList = Object.values(data[platform]).map(c => c.meta);
|
|
19
|
+
const formattedComponentsList = formatList(componentsList);
|
|
20
|
+
const importExample =
|
|
21
|
+
platform === 'web'
|
|
22
|
+
? "import { Button } from '@preply/ds-web-lib';"
|
|
23
|
+
: "import { Button } from '@preply/ds-rn-lib';";
|
|
24
|
+
|
|
25
|
+
return [
|
|
26
|
+
dedent`
|
|
27
|
+
Each item has the following fields:
|
|
28
|
+
- name - The name of the component (e.g. Button)
|
|
29
|
+
- import - The import path of the component (e.g. "${importExample}")
|
|
30
|
+
- description - The description of the component
|
|
31
|
+
- deprecated - Whether the component is deprecated (false if missing)
|
|
32
|
+
`,
|
|
33
|
+
formattedComponentsList,
|
|
34
|
+
];
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { searchIcon } from './search-icon/search-icon';
|
|
2
|
+
export { searchTokenByName } from './search-token/search-token-by-name';
|
|
3
|
+
export { searchTokenByValue } from './search-token/search-token-by-value';
|
|
4
|
+
export { listComponents } from './components/list-components';
|
|
5
|
+
export { getComponentDocs } from './components/get-component-docs';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { getIconsData } from '../get-icons-data';
|
|
4
|
+
|
|
5
|
+
describe('buildIconsContext', () => {
|
|
6
|
+
it('generates names from TokyoUI SVG files, strips prefix and deduplicates', async () => {
|
|
7
|
+
const icons = getIconsData(path.resolve(import.meta.dirname, 'test-icons'));
|
|
8
|
+
|
|
9
|
+
expect(icons).toMatchInlineSnapshot(`
|
|
10
|
+
[
|
|
11
|
+
{
|
|
12
|
+
"react-native": "Close",
|
|
13
|
+
"web": "TokyoUIClose",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"react-native": "Help",
|
|
17
|
+
"web": "TokyoUIHelp",
|
|
18
|
+
},
|
|
19
|
+
]
|
|
20
|
+
`);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { searchIcon } from '../search-icon';
|
|
3
|
+
|
|
4
|
+
vi.mock(import('../get-icons-data'), () => ({
|
|
5
|
+
getIconsData: () => [
|
|
6
|
+
{ web: 'TokyoUIClose', 'react-native': 'Close' },
|
|
7
|
+
{ web: 'TokyoUIHelp', 'react-native': 'Help' },
|
|
8
|
+
{ web: 'TokyoUIHelpCenter', 'react-native': 'HelpCenter' },
|
|
9
|
+
{ web: 'TokyoUIX', 'react-native': 'X' },
|
|
10
|
+
],
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe('searchIcon tool', () => {
|
|
14
|
+
it('returns exact match response', () => {
|
|
15
|
+
expect(
|
|
16
|
+
searchIcon.callback({
|
|
17
|
+
name: 'Close',
|
|
18
|
+
platform: 'web',
|
|
19
|
+
includeExample: false,
|
|
20
|
+
}),
|
|
21
|
+
).toMatchInlineSnapshot(`
|
|
22
|
+
[
|
|
23
|
+
"Found exact match. The full icon name is "TokyoUIClose".",
|
|
24
|
+
]
|
|
25
|
+
`);
|
|
26
|
+
|
|
27
|
+
expect(
|
|
28
|
+
searchIcon.callback({
|
|
29
|
+
name: 'HelpCenter',
|
|
30
|
+
platform: 'web',
|
|
31
|
+
includeExample: false,
|
|
32
|
+
}),
|
|
33
|
+
).toMatchInlineSnapshot(`
|
|
34
|
+
[
|
|
35
|
+
"Found exact match. The full icon name is "TokyoUIHelpCenter".",
|
|
36
|
+
"Also, here you can find some icons with similar names:",
|
|
37
|
+
"TokyoUIHelp",
|
|
38
|
+
]
|
|
39
|
+
`);
|
|
40
|
+
|
|
41
|
+
expect(
|
|
42
|
+
searchIcon.callback({
|
|
43
|
+
name: 'Close',
|
|
44
|
+
platform: 'react-native',
|
|
45
|
+
includeExample: false,
|
|
46
|
+
}),
|
|
47
|
+
).toMatchInlineSnapshot(`
|
|
48
|
+
[
|
|
49
|
+
"Found exact match. The full icon name is "Close".",
|
|
50
|
+
]
|
|
51
|
+
`);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('returns exact match response with examples', () => {
|
|
55
|
+
expect(
|
|
56
|
+
searchIcon.callback({
|
|
57
|
+
name: 'Close',
|
|
58
|
+
platform: 'web',
|
|
59
|
+
includeExample: true,
|
|
60
|
+
}),
|
|
61
|
+
).toMatchInlineSnapshot(`
|
|
62
|
+
[
|
|
63
|
+
"Found exact match. The full icon name is "TokyoUIClose".",
|
|
64
|
+
"To use the icon, import it from @preply/ds-media-icons/dist/24/ with the TokyoUI prefix and use the Icon component:",
|
|
65
|
+
"import TokyoUIClose from '@preply/ds-media-icons/dist/24/TokyoUIClose.svg';
|
|
66
|
+
import { Icon } from '@preply/ds-web-lib';
|
|
67
|
+
|
|
68
|
+
<Icon svg={<TokyoUIClose />} />",
|
|
69
|
+
]
|
|
70
|
+
`);
|
|
71
|
+
|
|
72
|
+
expect(
|
|
73
|
+
searchIcon.callback({
|
|
74
|
+
name: 'Close',
|
|
75
|
+
platform: 'react-native',
|
|
76
|
+
includeExample: true,
|
|
77
|
+
}),
|
|
78
|
+
).toMatchInlineSnapshot(`
|
|
79
|
+
[
|
|
80
|
+
"Found exact match. The full icon name is "Close".",
|
|
81
|
+
"To use the icon in React Native, use the Icon component from @preply/ds-rn-lib:",
|
|
82
|
+
"import { Icon } from '@preply/ds-rn-lib';
|
|
83
|
+
|
|
84
|
+
<Icon name="Close" />",
|
|
85
|
+
]
|
|
86
|
+
`);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('returns exact match response with similar results', async () => {
|
|
90
|
+
expect(
|
|
91
|
+
searchIcon.callback({
|
|
92
|
+
name: 'Help',
|
|
93
|
+
platform: 'web',
|
|
94
|
+
includeExample: false,
|
|
95
|
+
}),
|
|
96
|
+
).toMatchInlineSnapshot(`
|
|
97
|
+
[
|
|
98
|
+
"Found exact match. The full icon name is "TokyoUIHelp".",
|
|
99
|
+
"Also, here you can find some icons with similar names:",
|
|
100
|
+
"TokyoUIHelpCenter",
|
|
101
|
+
]
|
|
102
|
+
`);
|
|
103
|
+
|
|
104
|
+
expect(
|
|
105
|
+
searchIcon.callback({
|
|
106
|
+
name: 'help',
|
|
107
|
+
platform: 'react-native',
|
|
108
|
+
includeExample: false,
|
|
109
|
+
}),
|
|
110
|
+
).toMatchInlineSnapshot(`
|
|
111
|
+
[
|
|
112
|
+
"Found exact match. The full icon name is "Help".",
|
|
113
|
+
"Also, here you can find some icons with similar names:",
|
|
114
|
+
"HelpCenter",
|
|
115
|
+
]
|
|
116
|
+
`);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('returns exact match response with similar results and examples', async () => {
|
|
120
|
+
expect(
|
|
121
|
+
searchIcon.callback({
|
|
122
|
+
name: 'help',
|
|
123
|
+
platform: 'web',
|
|
124
|
+
includeExample: true,
|
|
125
|
+
}),
|
|
126
|
+
).toMatchInlineSnapshot(`
|
|
127
|
+
[
|
|
128
|
+
"Found exact match. The full icon name is "TokyoUIHelp".",
|
|
129
|
+
"To use the icon, import it from @preply/ds-media-icons/dist/24/ with the TokyoUI prefix and use the Icon component:",
|
|
130
|
+
"import TokyoUIHelp from '@preply/ds-media-icons/dist/24/TokyoUIHelp.svg';
|
|
131
|
+
import { Icon } from '@preply/ds-web-lib';
|
|
132
|
+
|
|
133
|
+
<Icon svg={<TokyoUIHelp />} />",
|
|
134
|
+
"Also, here you can find some icons with similar names:",
|
|
135
|
+
"TokyoUIHelpCenter",
|
|
136
|
+
]
|
|
137
|
+
`);
|
|
138
|
+
|
|
139
|
+
expect(
|
|
140
|
+
searchIcon.callback({
|
|
141
|
+
name: 'help',
|
|
142
|
+
platform: 'react-native',
|
|
143
|
+
includeExample: true,
|
|
144
|
+
}),
|
|
145
|
+
).toMatchInlineSnapshot(`
|
|
146
|
+
[
|
|
147
|
+
"Found exact match. The full icon name is "Help".",
|
|
148
|
+
"To use the icon in React Native, use the Icon component from @preply/ds-rn-lib:",
|
|
149
|
+
"import { Icon } from '@preply/ds-rn-lib';
|
|
150
|
+
|
|
151
|
+
<Icon name="Help" />",
|
|
152
|
+
"Also, here you can find some icons with similar names:",
|
|
153
|
+
"HelpCenter",
|
|
154
|
+
]
|
|
155
|
+
`);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('returns similar results when no exact match', async () => {
|
|
159
|
+
expect(
|
|
160
|
+
searchIcon.callback({
|
|
161
|
+
name: 'center',
|
|
162
|
+
platform: 'web',
|
|
163
|
+
includeExample: false,
|
|
164
|
+
}),
|
|
165
|
+
).toMatchInlineSnapshot(`
|
|
166
|
+
[
|
|
167
|
+
"There is no exact match for "center" query, but here are some icons with similar names:",
|
|
168
|
+
"TokyoUIHelpCenter",
|
|
169
|
+
]
|
|
170
|
+
`);
|
|
171
|
+
|
|
172
|
+
expect(
|
|
173
|
+
searchIcon.callback({
|
|
174
|
+
name: 'center',
|
|
175
|
+
platform: 'react-native',
|
|
176
|
+
includeExample: false,
|
|
177
|
+
}),
|
|
178
|
+
).toMatchInlineSnapshot(`
|
|
179
|
+
[
|
|
180
|
+
"There is no exact match for "center" query, but here are some icons with similar names:",
|
|
181
|
+
"HelpCenter",
|
|
182
|
+
]
|
|
183
|
+
`);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('returns not-found message when no matches', async () => {
|
|
187
|
+
expect(
|
|
188
|
+
searchIcon.callback({
|
|
189
|
+
name: 'definitely-missing-icon-name',
|
|
190
|
+
platform: 'web',
|
|
191
|
+
includeExample: false,
|
|
192
|
+
}),
|
|
193
|
+
).toMatchInlineSnapshot(`
|
|
194
|
+
[
|
|
195
|
+
"No results found for "definitely-missing-icon-name"",
|
|
196
|
+
]
|
|
197
|
+
`);
|
|
198
|
+
|
|
199
|
+
expect(
|
|
200
|
+
searchIcon.callback({
|
|
201
|
+
name: 'definitely-missing-icon-name',
|
|
202
|
+
platform: 'react-native',
|
|
203
|
+
includeExample: false,
|
|
204
|
+
}),
|
|
205
|
+
).toMatchInlineSnapshot(`
|
|
206
|
+
[
|
|
207
|
+
"No results found for "definitely-missing-icon-name"",
|
|
208
|
+
]
|
|
209
|
+
`);
|
|
210
|
+
|
|
211
|
+
expect(
|
|
212
|
+
searchIcon.callback({
|
|
213
|
+
name: 'definitely-missing-icon-name',
|
|
214
|
+
platform: 'react-native',
|
|
215
|
+
includeExample: true,
|
|
216
|
+
}),
|
|
217
|
+
).toMatchInlineSnapshot(`
|
|
218
|
+
[
|
|
219
|
+
"No results found for "definitely-missing-icon-name"",
|
|
220
|
+
]
|
|
221
|
+
`);
|
|
222
|
+
|
|
223
|
+
expect(
|
|
224
|
+
searchIcon.callback({
|
|
225
|
+
name: 'definitely-missing-icon-name',
|
|
226
|
+
platform: 'react-native',
|
|
227
|
+
includeExample: true,
|
|
228
|
+
}),
|
|
229
|
+
).toMatchInlineSnapshot(`
|
|
230
|
+
[
|
|
231
|
+
"No results found for "definitely-missing-icon-name"",
|
|
232
|
+
]
|
|
233
|
+
`);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
🤷♂️
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg></svg>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const ICONS_DIRNAME = path.resolve(import.meta.dirname, '../../../../media-icons/svg/24');
|
|
6
|
+
|
|
7
|
+
export function getIconsData(iconsDirname = ICONS_DIRNAME) {
|
|
8
|
+
return fs
|
|
9
|
+
.readdirSync(iconsDirname)
|
|
10
|
+
.filter(filename => filename.endsWith('.svg'))
|
|
11
|
+
.filter(filename => /^TokyoUI/i.test(filename))
|
|
12
|
+
.map(filename => filename.replace(/\.svg$/, ''))
|
|
13
|
+
.map(prefixedName => ({
|
|
14
|
+
web: prefixedName,
|
|
15
|
+
'react-native': prefixedName.replace(/^TokyoUI/, ''),
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type Icon = ReturnType<typeof getIconsData>[number];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import kebabCase from 'kebab-case';
|
|
2
|
+
import { createSearchIndex } from '../../utils/create-search-index';
|
|
3
|
+
// Build-time macro: getIconsData() runs at compile time and its return value is inlined into the bundle
|
|
4
|
+
import { Icon, getIconsData } from './get-icons-data' with { type: 'macro' };
|
|
5
|
+
import { PLATFORMS, Platform } from '../../types';
|
|
6
|
+
import dedent from 'dedent';
|
|
7
|
+
import { defineTool } from '../../utils/define-tool';
|
|
8
|
+
import * as z from 'zod';
|
|
9
|
+
import { formatListInline } from '../../utils/format-list';
|
|
10
|
+
|
|
11
|
+
const icons = getIconsData();
|
|
12
|
+
|
|
13
|
+
const index = createSearchIndex(icons, {
|
|
14
|
+
ref: 'react-native',
|
|
15
|
+
/**
|
|
16
|
+
* Normalize here strips the "TokyoUI" prefix and converts the name to kebab-case
|
|
17
|
+
* so each part of the name is searchable independently.
|
|
18
|
+
*
|
|
19
|
+
* e.g. for TokyoUIHelpCenter, the normalized name is "help-center" which allows to search for "help" or "center" separately.
|
|
20
|
+
*/
|
|
21
|
+
normalize: term => kebabCase(term.replace(/^TokyoUI/i, ''), false),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const usageMessage = (platform: Platform) =>
|
|
25
|
+
platform === 'web'
|
|
26
|
+
? 'To use the icon, import it from @preply/ds-media-icons/dist/24/ with the TokyoUI prefix and use the Icon component:'
|
|
27
|
+
: 'To use the icon in React Native, use the Icon component from @preply/ds-rn-lib:';
|
|
28
|
+
|
|
29
|
+
const exampleMessage = (
|
|
30
|
+
platform: Platform,
|
|
31
|
+
icon: Icon = { web: 'TokyoUIClose', 'react-native': 'Close' },
|
|
32
|
+
) =>
|
|
33
|
+
platform === 'web'
|
|
34
|
+
? dedent`
|
|
35
|
+
import ${icon.web} from '@preply/ds-media-icons/dist/24/${icon.web}.svg';
|
|
36
|
+
import { Icon } from '@preply/ds-web-lib';
|
|
37
|
+
|
|
38
|
+
<Icon svg={<${icon.web} />} />
|
|
39
|
+
`
|
|
40
|
+
: dedent`
|
|
41
|
+
import { Icon } from '@preply/ds-rn-lib';
|
|
42
|
+
|
|
43
|
+
<Icon name="${icon['react-native']}" />
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const exactMatchMessage = (platform: Platform, icon: Icon) =>
|
|
47
|
+
`Found exact match. The full icon name is "${icon[platform]}".`;
|
|
48
|
+
|
|
49
|
+
export const searchIcon = defineTool({
|
|
50
|
+
name: 'search_icon',
|
|
51
|
+
description: 'Search icons available in @preply/ds-media-icons',
|
|
52
|
+
arguments: z.object({
|
|
53
|
+
name: z.string().describe(dedent`
|
|
54
|
+
The icon name to search for.
|
|
55
|
+
It can be provided with or without the "TokyoUI" prefix (e.g. "Close" or "TokyoUIClose").
|
|
56
|
+
`),
|
|
57
|
+
}),
|
|
58
|
+
options: z.object({
|
|
59
|
+
platform: z.enum(PLATFORMS).describe('Target platform for usage examples. Required.'),
|
|
60
|
+
includeExample: z
|
|
61
|
+
.boolean()
|
|
62
|
+
.default(true)
|
|
63
|
+
.describe('Whether to include an usage example. Default: true'),
|
|
64
|
+
}),
|
|
65
|
+
callback: ({ name, platform, includeExample }) => {
|
|
66
|
+
const matches = index.search(name);
|
|
67
|
+
|
|
68
|
+
if (!matches.length) {
|
|
69
|
+
return [`No results found for "${name}"`];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const [first, ...rest] = matches;
|
|
73
|
+
|
|
74
|
+
const examples = includeExample
|
|
75
|
+
? [usageMessage(platform), exampleMessage(platform, first?.item)]
|
|
76
|
+
: [];
|
|
77
|
+
|
|
78
|
+
if (!first.exact) {
|
|
79
|
+
return [
|
|
80
|
+
`There is no exact match for "${name}" query, but here are some icons with similar names:`,
|
|
81
|
+
formatListInline(matches.map(match => match.item[platform])),
|
|
82
|
+
...examples,
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const similarMessages = rest.length
|
|
87
|
+
? [
|
|
88
|
+
`Also, here you can find some icons with similar names:`,
|
|
89
|
+
formatListInline(rest.map(match => match.item[platform])),
|
|
90
|
+
]
|
|
91
|
+
: [];
|
|
92
|
+
|
|
93
|
+
// prettier-ignore
|
|
94
|
+
return [
|
|
95
|
+
exactMatchMessage(platform, first.item),
|
|
96
|
+
...examples,
|
|
97
|
+
...similarMessages
|
|
98
|
+
];
|
|
99
|
+
},
|
|
100
|
+
});
|