@supericons/mcp 0.4.7 → 0.4.9
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/generated/motion-lab-baseline.json +1 -1
- package/hosted-search-client.js +42 -16
- package/index.js +3 -1
- package/package.json +1 -1
- package/public/product-facts.json +2 -2
- package/recommend-icons.js +1016 -232
- package/remote-server.js +3 -1
- package/server.json +2 -2
package/recommend-icons.js
CHANGED
|
@@ -26,6 +26,8 @@ const multilingualSearchAliases = existsSync(multilingualAliasesPath)
|
|
|
26
26
|
? JSON.parse(readFileSync(multilingualAliasesPath, 'utf8')).aliases || []
|
|
27
27
|
: [];
|
|
28
28
|
const multilingualExpansionTerms = [...cjkSearchTerms, ...multilingualSearchAliases];
|
|
29
|
+
const SLOT_SEARCH_CONCURRENCY = 2;
|
|
30
|
+
const SLOT_QUERY_CONCURRENCY = 1;
|
|
29
31
|
|
|
30
32
|
const GENERIC_SLOT_WORDS = new Set([
|
|
31
33
|
'action',
|
|
@@ -46,17 +48,109 @@ const GENERIC_SLOT_WORDS = new Set([
|
|
|
46
48
|
'view',
|
|
47
49
|
]);
|
|
48
50
|
|
|
49
|
-
const VARIANT_PENALTIES = Object.freeze([
|
|
50
|
-
{ pattern: /circle/i, penalty:
|
|
51
|
-
{ pattern: /square/i, penalty:
|
|
52
|
-
{ pattern: /dash/i, penalty: 5 },
|
|
53
|
-
{ pattern: /badge/i, penalty: 4 },
|
|
54
|
-
{ pattern: /
|
|
55
|
-
{ pattern: /
|
|
56
|
-
{ pattern: /
|
|
57
|
-
|
|
51
|
+
const VARIANT_PENALTIES = Object.freeze([
|
|
52
|
+
{ token: 'circle', pattern: /circle/i, penalty: 12 },
|
|
53
|
+
{ token: 'square', pattern: /square/i, penalty: 12 },
|
|
54
|
+
{ token: 'dash', pattern: /dash/i, penalty: 5 },
|
|
55
|
+
{ token: 'badge', pattern: /badge/i, penalty: 4 },
|
|
56
|
+
{ token: 'brand', pattern: /\bbrand\b/i, penalty: 30 },
|
|
57
|
+
{ token: 'off', pattern: /\boff\b/i, penalty: 18 },
|
|
58
|
+
{ token: 'slash', pattern: /slash/i, penalty: 8 },
|
|
59
|
+
{ token: 'warning', pattern: /warning/i, penalty: 5 },
|
|
60
|
+
{ token: 'ai', pattern: /\bai\b/i, penalty: 18 },
|
|
61
|
+
{ token: 'add', pattern: /\badd\b/i, penalty: 12 },
|
|
62
|
+
{ token: 'plus', pattern: /\bplus\b/i, penalty: 18 },
|
|
63
|
+
{ token: 'edit', pattern: /\bedit\b/i, penalty: 12 },
|
|
64
|
+
{ token: 'remove', pattern: /\bremove\b/i, penalty: 12 },
|
|
65
|
+
{ token: 'delete', pattern: /\bdelete\b/i, penalty: 12 },
|
|
66
|
+
{ token: 'minus', pattern: /\bminus\b/i, penalty: 24 },
|
|
67
|
+
{ token: 'cancel', pattern: /\bcancel\b/i, penalty: 30 },
|
|
68
|
+
{ token: 'x', pattern: /\bx\b/i, penalty: 30 },
|
|
69
|
+
{ token: 'exclamation', pattern: /\bexclamation\b/i, penalty: 24 },
|
|
70
|
+
{ token: 'discount', pattern: /\bdiscount\b/i, penalty: 24 },
|
|
71
|
+
{ token: 'heart', pattern: /\bheart\b/i, penalty: 18 },
|
|
72
|
+
{ token: 'zap', pattern: /\bzap\b/i, penalty: 30 },
|
|
73
|
+
{ token: 'bolt', pattern: /\bbolt\b/i, penalty: 18 },
|
|
74
|
+
{ token: 'wifi', pattern: /\bwifi\b/i, penalty: 12 },
|
|
75
|
+
{ token: 'align', pattern: /\balign\b/i, penalty: 12 },
|
|
76
|
+
{ token: 'fruit', pattern: /\bfruit\b/i, penalty: 12 },
|
|
77
|
+
{ token: 'open', pattern: /\bopen\b/i, penalty: 28 },
|
|
78
|
+
{ token: 'unlock', pattern: /\bunlock(?:ed)?\b/i, penalty: 28 },
|
|
79
|
+
{ token: 'ban', pattern: /\bban\b/i, penalty: 24 },
|
|
80
|
+
{ token: 'blocked', pattern: /\bblocked\b/i, penalty: 24 },
|
|
81
|
+
{ token: 'rupee', pattern: /\brupee\b/i, penalty: 18 },
|
|
82
|
+
{ token: 'ruble', pattern: /\bruble\b/i, penalty: 18 },
|
|
83
|
+
{ token: 'franc', pattern: /\bfranc\b/i, penalty: 18 },
|
|
84
|
+
{ token: 'lira', pattern: /\blira\b/i, penalty: 18 },
|
|
85
|
+
{ token: 'bitcoin', pattern: /\bbitcoin\b/i, penalty: 18 },
|
|
86
|
+
{ token: 'dollar', pattern: /\bdollar\b/i, penalty: 18 },
|
|
87
|
+
{ token: 'cent', pattern: /\bcent\b/i, penalty: 18 },
|
|
88
|
+
{ token: 'yen', pattern: /\byen\b/i, penalty: 18 },
|
|
89
|
+
{ token: 'yuan', pattern: /\byuan\b/i, penalty: 18 },
|
|
90
|
+
{ token: 'euro', pattern: /\beuro\b/i, penalty: 18 },
|
|
91
|
+
{ token: 'pound', pattern: /\bpound\b/i, penalty: 18 },
|
|
92
|
+
{ token: 'down', pattern: /\bdown\b/i, penalty: 8 },
|
|
93
|
+
{ token: 'left', pattern: /\bleft\b/i, penalty: 8 },
|
|
94
|
+
{ token: 'up', pattern: /\bup\b/i, penalty: 8 },
|
|
95
|
+
{ token: 'corner', pattern: /\bcorner\b/i, penalty: 12 },
|
|
96
|
+
{ token: 'break', pattern: /\bbreak\b/i, penalty: 18 },
|
|
97
|
+
{ token: 'broken', pattern: /\bbroken\b/i, penalty: 18 },
|
|
98
|
+
{ token: 'locked', pattern: /\blocked\b/i, penalty: 18 },
|
|
99
|
+
{ token: 'orange', pattern: /\borange\b/i, penalty: 12 },
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
const VARIANT_TOKENS = new Set(VARIANT_PENALTIES.map((rule) => rule.token));
|
|
103
|
+
|
|
104
|
+
const REQUESTED_VARIANT_ALIASES = Object.freeze({
|
|
105
|
+
off: ['disabled', 'disable', 'muted', 'mute', 'off', 'broken'],
|
|
106
|
+
brand: ['brand', 'logo'],
|
|
107
|
+
open: ['open', 'unlock', 'unlocked'],
|
|
108
|
+
unlock: ['open', 'unlock', 'unlocked'],
|
|
109
|
+
ban: ['ban', 'banned', 'block', 'blocked'],
|
|
110
|
+
blocked: ['ban', 'banned', 'block', 'blocked'],
|
|
111
|
+
add: ['add', 'create', 'plus'],
|
|
112
|
+
plus: ['add', 'create', 'plus'],
|
|
113
|
+
edit: ['edit', 'editing', 'modify', 'pencil'],
|
|
114
|
+
remove: ['remove', 'removed', 'delete', 'minus'],
|
|
115
|
+
delete: ['delete', 'remove', 'trash'],
|
|
116
|
+
minus: ['minus', 'remove', 'removed', 'delete'],
|
|
117
|
+
cancel: ['cancel', 'canceled', 'cancelled', 'disabled', 'remove'],
|
|
118
|
+
x: ['x', 'close', 'remove', 'delete', 'blocked', 'broken', 'off'],
|
|
119
|
+
exclamation: ['alert', 'warning', 'exclamation'],
|
|
120
|
+
discount: ['discount', 'coupon', 'coupons', 'promo', 'promotion', 'deal'],
|
|
121
|
+
heart: ['heart', 'favorite', 'favourite', 'liked', 'wishlist'],
|
|
122
|
+
ai: ['ai', 'smart', 'assistant', 'automation'],
|
|
123
|
+
break: ['break', 'broken'],
|
|
124
|
+
broken: ['break', 'broken'],
|
|
125
|
+
locked: ['lock', 'locked', 'secure', 'security'],
|
|
126
|
+
ruble: ['ruble', 'rouble', 'rub'],
|
|
127
|
+
franc: ['franc', 'chf'],
|
|
128
|
+
lira: ['lira'],
|
|
129
|
+
bitcoin: ['bitcoin', 'btc'],
|
|
130
|
+
dollar: ['dollar', 'usd'],
|
|
131
|
+
yuan: ['yuan', 'cny'],
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const DIRECT_LOCALIZED_INTENT_RULES = Object.freeze([
|
|
135
|
+
{
|
|
136
|
+
pattern: /通知|お知らせ|알림|notificaciones?|benachrichtigungen?|notifica(?:ç|c)[aã]o|notificações?/iu,
|
|
137
|
+
terms: ['notification', 'notifications'],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
pattern: /关闭|關閉|オフ|꺼짐|끄기|desactivad[ao]s?|apagad[ao]s?|aus\b|deaktiviert|disabled|muted|mute|off/iu,
|
|
141
|
+
terms: ['off', 'disabled'],
|
|
142
|
+
},
|
|
143
|
+
]);
|
|
58
144
|
|
|
59
145
|
const COMMON_SLOT_PREFERENCE_RULES = Object.freeze([
|
|
146
|
+
{
|
|
147
|
+
slotPatterns: [/\busers\b/i, /team/i, /members?/i],
|
|
148
|
+
queryVariants: ['users', 'team', 'user group', 'people'],
|
|
149
|
+
iconPreferences: [
|
|
150
|
+
{ pattern: /^users(?:_|-|$)|(?:_|-)users(?:_|-|$)|users-group|user-group|user_circle|user-circle/i, bonus: 66 },
|
|
151
|
+
{ pattern: /^user(?:_|-|$)|(?:_|-)user(?:_|-|$)/i, bonus: 18 },
|
|
152
|
+
],
|
|
153
|
+
},
|
|
60
154
|
{
|
|
61
155
|
slotPatterns: [
|
|
62
156
|
/profile/i,
|
|
@@ -120,6 +214,15 @@ const COMMON_SLOT_PREFERENCE_RULES = Object.freeze([
|
|
|
120
214
|
{ pattern: /alarm|alert/i, bonus: 18 },
|
|
121
215
|
],
|
|
122
216
|
},
|
|
217
|
+
{
|
|
218
|
+
priority: 90,
|
|
219
|
+
slotPatterns: [/notifications?\s+off/i, /disabled notifications?/i, /muted notifications?/i, /notification\s+off/i],
|
|
220
|
+
queryVariants: ['bell slash', 'bell off', 'notification off', 'muted bell'],
|
|
221
|
+
iconPreferences: [
|
|
222
|
+
{ pattern: /^bell[_-]?(off|slash)$|^bell[_-]?simple[_-]?slash$|notification[_-]?off|notifications?[_-]?off/i, bonus: 180 },
|
|
223
|
+
{ pattern: /bell|notification/i, bonus: 22 },
|
|
224
|
+
],
|
|
225
|
+
},
|
|
123
226
|
{
|
|
124
227
|
slotPatterns: [
|
|
125
228
|
/privacy/i,
|
|
@@ -140,8 +243,30 @@ const COMMON_SLOT_PREFERENCE_RULES = Object.freeze([
|
|
|
140
243
|
],
|
|
141
244
|
queryVariants: ['shield lock', 'lock', 'shield', 'privacy security', 'security'],
|
|
142
245
|
iconPreferences: [
|
|
143
|
-
{ pattern:
|
|
144
|
-
{ pattern:
|
|
246
|
+
{ pattern: /^shield$|^shield[_-]?check$|shield-check|shield_check/i, bonus: 82 },
|
|
247
|
+
{ pattern: /shield.*lock|lock.*shield|shield/i, bonus: 58 },
|
|
248
|
+
{ pattern: /^lock$|^lock[_-]?keyhole$|(?:_|-)lock(?:_|-|$)|key|fingerprint/i, bonus: 34 },
|
|
249
|
+
{ pattern: /open|unlock|ban|minus|off|slash/i, bonus: -54 },
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
priority: 120,
|
|
254
|
+
slotPatterns: [/unlock/i, /open account/i, /unlocked account/i],
|
|
255
|
+
queryVariants: ['lock open', 'unlock', 'lock keyhole open'],
|
|
256
|
+
iconPreferences: [
|
|
257
|
+
{ pattern: /^lock[_-]?open$|^lock[_-]?keyhole[_-]?open$|^unlock(?:[_-]?keyhole)?$/i, bonus: 160 },
|
|
258
|
+
{ pattern: /lock.*open|open.*lock|unlock/i, bonus: 80 },
|
|
259
|
+
{ pattern: /^user(?:_|-|$)|(?:_|-)user(?:_|-|$)|file-user/i, bonus: -70 },
|
|
260
|
+
],
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
priority: 120,
|
|
264
|
+
slotPatterns: [/blocked user/i, /banned user/i, /user blocked/i, /user banned/i],
|
|
265
|
+
queryVariants: ['user x', 'user minus', 'ban user', 'blocked user'],
|
|
266
|
+
iconPreferences: [
|
|
267
|
+
{ pattern: /^user[_-]?x$|^user[_-]?minus$|user-round-x|user-round-minus|shield-ban|ban/i, bonus: 150 },
|
|
268
|
+
{ pattern: /^user(?:_|-|$)|(?:_|-)user(?:_|-|$)/i, bonus: 24 },
|
|
269
|
+
{ pattern: /^file-user$|^user-2$/i, bonus: -80 },
|
|
145
270
|
],
|
|
146
271
|
},
|
|
147
272
|
{
|
|
@@ -252,14 +377,33 @@ const COMMON_SLOT_PREFERENCE_RULES = Object.freeze([
|
|
|
252
377
|
{ pattern: /person|contact/i, bonus: 10 },
|
|
253
378
|
],
|
|
254
379
|
},
|
|
255
|
-
{
|
|
256
|
-
slotPatterns: [/model/i, /\bai\b/i, /\bml\b/i, /machine learning/i],
|
|
257
|
-
queryVariants: ['brain circuit', 'brain cog', 'neural network', 'model'],
|
|
380
|
+
{
|
|
381
|
+
slotPatterns: [/model/i, /\bai\b/i, /\bml\b/i, /machine learning/i],
|
|
382
|
+
queryVariants: ['brain circuit', 'brain cog', 'neural network', 'model'],
|
|
258
383
|
iconPreferences: [
|
|
259
384
|
{ pattern: /brain-circuit|brain_circuit/i, bonus: 44 },
|
|
260
|
-
{ pattern: /brain|circuit|nodes/i, bonus: 24 },
|
|
261
|
-
],
|
|
262
|
-
},
|
|
385
|
+
{ pattern: /brain|circuit|nodes/i, bonus: 24 },
|
|
386
|
+
],
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
priority: 120,
|
|
390
|
+
slotPatterns: [/\bai search\b/i, /smart search/i, /semantic search/i, /assistant search/i],
|
|
391
|
+
queryVariants: ['search ai', 'ai search', 'smart search'],
|
|
392
|
+
iconPreferences: [
|
|
393
|
+
{ pattern: /^search.*ai|ai.*search|search[_-]?[23]?[_-]?ai/i, bonus: 150 },
|
|
394
|
+
{ pattern: /^search(?:_|-|$)|(?:_|-)search(?:_|-|$)/i, bonus: 34 },
|
|
395
|
+
{ pattern: /brain|robot|spark/i, bonus: 24 },
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
priority: 90,
|
|
400
|
+
slotPatterns: [/automation/i, /workflow/i, /automate/i, /smart action/i],
|
|
401
|
+
queryVariants: ['automation', 'workflow', 'robot', 'refresh', 'sparkles'],
|
|
402
|
+
iconPreferences: [
|
|
403
|
+
{ pattern: /workflow|automation|robot|sparkles?|refresh|settings|adjustments/i, bonus: 90 },
|
|
404
|
+
{ pattern: /hand|finger|train/i, bonus: -90 },
|
|
405
|
+
],
|
|
406
|
+
},
|
|
263
407
|
{
|
|
264
408
|
slotPatterns: [/prompt/i],
|
|
265
409
|
queryVariants: ['message text', 'text input', 'terminal', 'text cursor'],
|
|
@@ -300,49 +444,430 @@ const COMMON_SLOT_PREFERENCE_RULES = Object.freeze([
|
|
|
300
444
|
{ pattern: /chart|signal|radar/i, bonus: 16 },
|
|
301
445
|
],
|
|
302
446
|
},
|
|
303
|
-
{
|
|
304
|
-
slotPatterns: [/billing/i, /payment/i, /invoice/i, /subscription/i],
|
|
305
|
-
queryVariants: ['credit card', 'receipt', 'invoice', 'payment', 'wallet'],
|
|
306
|
-
iconPreferences: [
|
|
307
|
-
{ pattern: /credit-card|receipt|wallet|invoice/i, bonus: 44 },
|
|
308
|
-
{ pattern: /card|banknote|currency|dollar/i, bonus: 18 },
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
447
|
+
{
|
|
448
|
+
slotPatterns: [/billing/i, /payment/i, /invoice/i, /subscription/i],
|
|
449
|
+
queryVariants: ['credit card', 'receipt', 'invoice', 'payment', 'wallet'],
|
|
450
|
+
iconPreferences: [
|
|
451
|
+
{ pattern: /credit-card|receipt|wallet|invoice/i, bonus: 44 },
|
|
452
|
+
{ pattern: /card|banknote|currency|dollar/i, bonus: 18 },
|
|
453
|
+
{ pattern: /ruble|franc|lira|bitcoin|yuan/i, bonus: -70 },
|
|
454
|
+
],
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
priority: 90,
|
|
458
|
+
slotPatterns: [/storefront/i, /\bstore\b/i, /\bshop\b/i],
|
|
459
|
+
queryVariants: ['store', 'shop', 'building store', 'shopping bag'],
|
|
460
|
+
iconPreferences: [
|
|
461
|
+
{ pattern: /^store$|^storefront$|building-store|shop[_-]?line|store[_-]?\d?[_-]?line|shopping-bag/i, bonus: 120 },
|
|
462
|
+
{ pattern: /brand-appstore|restore|cancel|\bx\b|off|discount|heart|exclamation|minus|plus|search|share|star|question|bolt|code|copy|dollar|down|up|pin|pause/i, bonus: -120 },
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
priority: 130,
|
|
467
|
+
slotPatterns: [/(store|shop|storefront)\s+(off|disabled|closed|cancelled|canceled)/i, /(off|disabled|closed|cancelled|canceled)\s+(store|shop|storefront)/i],
|
|
468
|
+
queryVariants: ['store off', 'shopping bag x', 'shopping cart off', 'store disabled'],
|
|
469
|
+
iconPreferences: [
|
|
470
|
+
{ pattern: /(store|shop|shopping|bag|cart).*(off|\bx\b|cancel|disabled)|(off|\bx\b|cancel|disabled).*(store|shop|shopping|bag|cart)/i, bonus: 220 },
|
|
471
|
+
{ pattern: /^building-store$|^store$|^shopping-bag$/i, bonus: -70 },
|
|
472
|
+
],
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
priority: 90,
|
|
476
|
+
slotPatterns: [/checkout/i],
|
|
477
|
+
queryVariants: ['shopping cart', 'credit card', 'payment', 'receipt checkout'],
|
|
478
|
+
iconPreferences: [
|
|
479
|
+
{ pattern: /^shopping[_-]?cart$|shopping-cart$|credit-card|card-pay|payment|receipt/i, bonus: 140 },
|
|
480
|
+
{ pattern: /fork|knife|git|branch|merge|forklift|grill|cancel|\bx\b|off|discount|heart|exclamation|minus|plus|search|share|star|question|bolt|code|copy|dollar|down|up|pin|pause/i, bonus: -140 },
|
|
481
|
+
],
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
priority: 120,
|
|
485
|
+
slotPatterns: [/^cart$/i, /shopping cart/i, /\bbasket\b/i],
|
|
486
|
+
queryVariants: ['shopping cart', 'cart', 'basket'],
|
|
487
|
+
iconPreferences: [
|
|
488
|
+
{ pattern: /^shopping[_-]?cart$|^basket$/i, bonus: 220 },
|
|
489
|
+
{ pattern: /shopping-cart-(cog|x|off|discount|dollar|exclamation|heart|minus|pause|plus|question|share|star|bolt|copy|down|up|search|pin)/i, bonus: -260 },
|
|
490
|
+
{ pattern: /basket-(cog|x|off|discount|dollar|exclamation|heart|minus|pause|plus|question|share|star|bolt|copy|down|up|search|pin)/i, bonus: -180 },
|
|
491
|
+
{ pattern: /garden-cart/i, bonus: -180 },
|
|
492
|
+
],
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
priority: 90,
|
|
496
|
+
slotPatterns: [/customers?/i, /shoppers?/i, /buyers?/i],
|
|
497
|
+
queryVariants: ['users', 'customers', 'user group'],
|
|
498
|
+
iconPreferences: [
|
|
499
|
+
{ pattern: /^users(?:_|-|$)|(?:_|-)users(?:_|-|$)|users-group|user-group|user-circle|user_circle|^user(?:_|-|$)/i, bonus: 110 },
|
|
500
|
+
{ pattern: /ticket|plane|caret|cancel|\bx\b|off|discount|heart|exclamation|minus|plus|search|share|star|question|bolt|code|copy|dollar|down|up|pin|pause/i, bonus: -120 },
|
|
501
|
+
],
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
priority: 90,
|
|
505
|
+
slotPatterns: [/coupons?/i, /discounts?/i, /promo/i, /promotion/i],
|
|
506
|
+
queryVariants: ['coupon', 'tag percent', 'discount', 'percentage'],
|
|
507
|
+
iconPreferences: [
|
|
508
|
+
{ pattern: /coupon|ticket-percent|badge-percent|percent|percentage|tag|shopping-cart-discount|shopping-bag-discount|seal-percent/i, bonus: 120 },
|
|
509
|
+
{ pattern: /^percentage-\d+$|bean|candy|cannabis|off|slash|disabled|alert/i, bonus: -180 },
|
|
510
|
+
],
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
priority: 130,
|
|
514
|
+
slotPatterns: [/(cancel|cancelled|canceled|remove|delete)\s+orders?/i, /orders?\s+(cancel|cancelled|canceled|remove|delete)/i],
|
|
515
|
+
queryVariants: ['shopping cart cancel', 'basket cancel', 'cancel order', 'order x'],
|
|
516
|
+
iconPreferences: [
|
|
517
|
+
{ pattern: /cancel|\bx\b|remove|delete|minus|trash/i, bonus: 220 },
|
|
518
|
+
{ pattern: /shopping|cart|basket|package|receipt|clipboard|list/i, bonus: 20 },
|
|
519
|
+
],
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
priority: 90,
|
|
523
|
+
slotPatterns: [/orders?/i, /purchases?/i],
|
|
524
|
+
queryVariants: ['package', 'receipt', 'clipboard list', 'shopping bag', 'ordered list'],
|
|
525
|
+
iconPreferences: [
|
|
526
|
+
{ pattern: /^package$|packages|receipt|shopping-bag$|clipboard|list-ordered|file-invoice|file-text/i, bonus: 115 },
|
|
527
|
+
{ pattern: /border|sort|align|cancel|\bx\b|off|discount|heart|exclamation|minus|plus|search|share|star|question|bolt|code|copy|dollar|down|up|pin|pause/i, bonus: -130 },
|
|
528
|
+
],
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
priority: 120,
|
|
532
|
+
slotPatterns: [/shipping/i, /delivery/i, /fulfillment/i, /dispatch/i],
|
|
533
|
+
queryVariants: ['truck delivery', 'delivery', 'shipping', 'package delivery'],
|
|
534
|
+
iconPreferences: [
|
|
535
|
+
{ pattern: /^truck[_-]?delivery$/i, bonus: 260 },
|
|
536
|
+
{ pattern: /^truck$|package[_-]?export|package[_-]?import/i, bonus: 180 },
|
|
537
|
+
{ pattern: /^truck[_-]?return$/i, bonus: -120 },
|
|
538
|
+
{ pattern: /cloud-upload|upload|ship-off|cloud|arrow-up/i, bonus: -220 },
|
|
539
|
+
],
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
priority: 120,
|
|
543
|
+
slotPatterns: [/returns?/i, /refunds?/i, /reverse logistics/i],
|
|
544
|
+
queryVariants: ['return', 'refund', 'truck return', 'receipt refund'],
|
|
545
|
+
iconPreferences: [
|
|
546
|
+
{ pattern: /^truck[_-]?return$|receipt[_-]?refund|credit-card[_-]?refund|arrow-back|cash.*move.*back/i, bonus: 220 },
|
|
547
|
+
{ pattern: /player|chevron|stack|upload|cloud|ship-off/i, bonus: -160 },
|
|
548
|
+
],
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
priority: 90,
|
|
552
|
+
slotPatterns: [/products?/i, /catalog/i, /inventory/i],
|
|
553
|
+
queryVariants: ['package', 'box', 'tag', 'shopping bag', 'products'],
|
|
554
|
+
iconPreferences: [
|
|
555
|
+
{ pattern: /^package$|packages|package[_-]?\d?|^box$|boxes|tag$|shopping-bag$|warehouse|building-warehouse/i, bonus: 115 },
|
|
556
|
+
{ pattern: /brand-producthunt|brand-stocktwits|border|sort|cancel|\bx\b|off|discount|heart|exclamation|minus|plus|search|share|star|question|bolt|code|copy|dollar|down|up|pin|pause/i, bonus: -130 },
|
|
557
|
+
],
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
slotPatterns: [/reports?/i, /analytics/i, /insights?/i],
|
|
561
|
+
queryVariants: ['bar chart', 'file chart', 'analytics chart', 'report document'],
|
|
314
562
|
iconPreferences: [
|
|
315
563
|
{ pattern: /file-.*chart|chart-bar|bar-chart-3|bar-chart|chart-line|line-chart/i, bonus: 40 },
|
|
316
|
-
{ pattern: /chart|report|analytics/i, bonus: 16 },
|
|
317
|
-
],
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
|
|
321
|
-
|
|
564
|
+
{ pattern: /chart|report|analytics/i, bonus: 16 },
|
|
565
|
+
],
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
priority: 100,
|
|
569
|
+
slotPatterns: [/^quotes?$/i, /blockquote/i, /quotation/i, /citation/i],
|
|
570
|
+
queryVariants: ['quotes', 'quotation', 'quote', 'blockquote'],
|
|
571
|
+
iconPreferences: [
|
|
572
|
+
{ pattern: /^quotes?$|quotation|blockquote/i, bonus: 180 },
|
|
573
|
+
{ pattern: /indent|text-align|receipt|ticket/i, bonus: -120 },
|
|
574
|
+
],
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
slotPatterns: [/settings?/i, /preferences?/i, /configure/i],
|
|
578
|
+
queryVariants: ['settings', 'cog', 'sliders'],
|
|
322
579
|
iconPreferences: [
|
|
323
580
|
{ pattern: /^settings$|^cog$|settings-2|sliders/i, bonus: 34 },
|
|
324
|
-
{ pattern: /settings|cog|adjustments/i, bonus: 16 },
|
|
325
|
-
],
|
|
326
|
-
},
|
|
327
|
-
{
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
{ pattern: /
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
581
|
+
{ pattern: /settings|cog|adjustments/i, bonus: 16 },
|
|
582
|
+
],
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
priority: 90,
|
|
586
|
+
slotPatterns: [/permissions?/i, /access control/i, /roles?/i],
|
|
587
|
+
queryVariants: ['user key', 'shield lock', 'key', 'settings permissions'],
|
|
588
|
+
iconPreferences: [
|
|
589
|
+
{ pattern: /user-key|user-lock|user-check|key|lock|shield|adjustments|settings/i, bonus: 120 },
|
|
590
|
+
{ pattern: /free-rights|premium-rights|icons$/i, bonus: -100 },
|
|
591
|
+
],
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
slotPatterns: [/database/i, /storage/i],
|
|
595
|
+
queryVariants: ['database', 'server database', 'data storage'],
|
|
596
|
+
iconPreferences: [
|
|
597
|
+
{ pattern: /^database$|database-stack/i, bonus: 36 },
|
|
598
|
+
{ pattern: /database|server/i, bonus: 16 },
|
|
599
|
+
],
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
slotPatterns: [/search/i, /find/i, /lookup/i],
|
|
603
|
+
queryVariants: ['search', 'find', 'magnifier', 'magnifying glass'],
|
|
604
|
+
iconPreferences: [
|
|
605
|
+
{ pattern: /^search$/i, bonus: 140 },
|
|
606
|
+
{ pattern: /^search(?:_|-|$)|(?:_|-)search(?:_|-|$)/i, bonus: 64 },
|
|
607
|
+
{ pattern: /magnifier|magnifying/i, bonus: 36 },
|
|
608
|
+
{ pattern: /file-search|folder-search|scan-search|mail-search|calendar-search/i, bonus: -90 },
|
|
609
|
+
],
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
priority: 115,
|
|
613
|
+
slotPatterns: [/\b(add|new|create)\s+bookmark\b/i, /\bbookmark\s+(add|new|create)\b/i],
|
|
614
|
+
queryVariants: ['bookmark add', 'add bookmark', 'bookmark plus'],
|
|
615
|
+
iconPreferences: [
|
|
616
|
+
{ pattern: /^bookmark[_-]?(add|plus)(?:_|-|$)|(?:_|-)bookmark[_-]?(add|plus)(?:_|-|$)/i, bonus: 220 },
|
|
617
|
+
{ pattern: /^bookmarks?(?:_|-|$)|(?:_|-)bookmarks?(?:_|-|$)/i, bonus: 46 },
|
|
618
|
+
{ pattern: /^(add|plus)(?:_|-|$)/i, bonus: -80 },
|
|
619
|
+
],
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
priority: 115,
|
|
623
|
+
slotPatterns: [/\bedit\s+bookmark\b/i, /\bbookmark\s+edit\b/i],
|
|
624
|
+
queryVariants: ['bookmark edit', 'edit bookmark', 'bookmark pencil'],
|
|
625
|
+
iconPreferences: [
|
|
626
|
+
{ pattern: /^bookmark[_-]?edit(?:_|-|$)|(?:_|-)bookmark[_-]?edit(?:_|-|$)/i, bonus: 190 },
|
|
627
|
+
{ pattern: /^bookmarks?(?:_|-|$)|(?:_|-)bookmarks?(?:_|-|$)/i, bonus: 46 },
|
|
628
|
+
{ pattern: /^edit(?:_|-|$)|pencil/i, bonus: -70 },
|
|
629
|
+
],
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
priority: 115,
|
|
633
|
+
slotPatterns: [/\b(remove|delete)\s+bookmark\b/i, /\bbookmark\s+(remove|delete)\b/i],
|
|
634
|
+
queryVariants: ['bookmark remove', 'remove bookmark', 'bookmark minus'],
|
|
635
|
+
iconPreferences: [
|
|
636
|
+
{ pattern: /^bookmark[_-]?(remove|minus|x)(?:_|-|$)|(?:_|-)bookmark[_-]?(remove|minus|x)(?:_|-|$)/i, bonus: 190 },
|
|
637
|
+
{ pattern: /^bookmarks?(?:_|-|$)|(?:_|-)bookmarks?(?:_|-|$)/i, bonus: 46 },
|
|
638
|
+
{ pattern: /^(remove|delete|minus)(?:_|-|$)/i, bonus: -70 },
|
|
639
|
+
],
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
slotPatterns: [/bookmark/i, /saved?/i, /save article/i],
|
|
643
|
+
queryVariants: ['bookmark', 'saved', 'save'],
|
|
644
|
+
iconPreferences: [
|
|
645
|
+
{ pattern: /^bookmarks?(?:_|-|$)|(?:_|-)bookmarks?(?:_|-|$)/i, bonus: 66 },
|
|
646
|
+
{ pattern: /save|favorite|star/i, bonus: 14 },
|
|
647
|
+
],
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
slotPatterns: [/share/i, /send article/i, /forward/i],
|
|
651
|
+
queryVariants: ['share', 'send', 'forward'],
|
|
652
|
+
iconPreferences: [
|
|
653
|
+
{ pattern: /^share(?:_|-|$)|(?:_|-)share(?:_|-|$)/i, bonus: 58 },
|
|
654
|
+
{ pattern: /send|forward/i, bonus: 18 },
|
|
655
|
+
],
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
priority: 110,
|
|
659
|
+
slotPatterns: [/previous page/i, /previous/i, /\bback\b/i, /go back/i],
|
|
660
|
+
queryVariants: ['arrow left', 'chevron left', 'back arrow', 'previous'],
|
|
661
|
+
iconPreferences: [
|
|
662
|
+
{ pattern: /^arrow[_-]?left$|^chevron[_-]?left$|^caret[_-]?left$|arrow[_-]?back$|back[_-]?line|arrow[_-]?to[_-]?left|left(?:_|-|$)/i, bonus: 140 },
|
|
663
|
+
{ pattern: /skip-back|step-back/i, bonus: 48 },
|
|
664
|
+
{ pattern: /send-to-back|file|archive|audio|floppy|cash|banknote|brand|copy/i, bonus: -140 },
|
|
665
|
+
],
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
priority: 90,
|
|
669
|
+
slotPatterns: [/read more/i, /more link/i, /continue/i, /open article/i, /next page/i, /^next$/i],
|
|
670
|
+
queryVariants: ['arrow right', 'move right', 'chevron right', 'read more', 'next'],
|
|
671
|
+
iconPreferences: [
|
|
672
|
+
{ pattern: /^arrow[_-]?right$|^move[_-]?right$|arrow[_-]?to[_-]?right|chevron[_-]?right|right(?:_|-|$)/i, bonus: 90 },
|
|
673
|
+
{ pattern: /square|circle|corner|up|down|left|banknote|archive/i, bonus: -70 },
|
|
674
|
+
],
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
slotPatterns: [/categor(?:y|ies)/i, /chips?/i, /filter/i, /topics?/i, /tags?/i],
|
|
678
|
+
queryVariants: ['filter', 'category', 'tag', 'grid'],
|
|
679
|
+
iconPreferences: [
|
|
680
|
+
{ pattern: /^filter(?:_|-|$)|(?:_|-)filter(?:_|-|$)/i, bonus: 56 },
|
|
681
|
+
{ pattern: /^tag(?:_|-|$)|(?:_|-)tag(?:_|-|$)|category|grid/i, bonus: 26 },
|
|
682
|
+
],
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
slotPatterns: [/trending/i, /popular/i, /top stories/i, /hot/i],
|
|
686
|
+
queryVariants: ['trending up', 'chart up', 'fire', 'popular'],
|
|
687
|
+
iconPreferences: [
|
|
688
|
+
{ pattern: /^trending[_-]?up(?:_|-|$)|chart.*up|up.*chart/i, bonus: 62 },
|
|
689
|
+
{ pattern: /^fire(?:_|-|$)|flame|hot/i, bonus: 22 },
|
|
690
|
+
],
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
slotPatterns: [/news/i, /article/i, /headline/i, /story/i, /publisher/i, /logo/i, /title/i],
|
|
694
|
+
queryVariants: ['news', 'article', 'newspaper', 'headline'],
|
|
695
|
+
iconPreferences: [
|
|
696
|
+
{ pattern: /^news(?:_|-|$)|(?:_|-)news(?:_|-|$)|newspaper|article/i, bonus: 66 },
|
|
697
|
+
{ pattern: /file|document|paper/i, bonus: 16 },
|
|
698
|
+
],
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
slotPatterns: [/dashboard/i],
|
|
702
|
+
queryVariants: ['dashboard', 'layout dashboard', 'grid dashboard'],
|
|
703
|
+
iconPreferences: [
|
|
704
|
+
{ pattern: /^dashboard$|layout-dashboard|dashboard/i, bonus: 50 },
|
|
705
|
+
{ pattern: /grid|layout/i, bonus: 12 },
|
|
706
|
+
],
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
slotPatterns: [/projects?/i],
|
|
710
|
+
queryVariants: ['folder', 'folders', 'project folder'],
|
|
711
|
+
iconPreferences: [
|
|
712
|
+
{ pattern: /^folders?$|(?:_|-)folders?(?:_|-|$)/i, bonus: 56 },
|
|
713
|
+
{ pattern: /briefcase|project/i, bonus: 12 },
|
|
714
|
+
],
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
slotPatterns: [/tasks?/i, /todo/i, /to do/i, /checklist/i],
|
|
718
|
+
queryVariants: ['list check', 'checklist', 'checkbox', 'task'],
|
|
719
|
+
iconPreferences: [
|
|
720
|
+
{ pattern: /list-check|list_check|checkbox|checklist|clipboard-check/i, bonus: 56 },
|
|
721
|
+
{ pattern: /check|task/i, bonus: 16 },
|
|
722
|
+
],
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
slotPatterns: [/team/i, /\busers\b/i, /members?/i],
|
|
726
|
+
queryVariants: ['users', 'team', 'user group'],
|
|
727
|
+
iconPreferences: [
|
|
728
|
+
{ pattern: /^users(?:_|-|$)|(?:_|-)users(?:_|-|$)|user-group|user_circle|user-circle/i, bonus: 64 },
|
|
729
|
+
{ pattern: /^user(?:_|-|$)|(?:_|-)user(?:_|-|$)/i, bonus: 18 },
|
|
730
|
+
],
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
slotPatterns: [/calendar/i, /schedule/i, /events?/i],
|
|
734
|
+
queryVariants: ['calendar', 'calendar event', 'schedule'],
|
|
735
|
+
iconPreferences: [
|
|
736
|
+
{ pattern: /^calendar(?:_|-|$)|(?:_|-)calendar(?:_|-|$)/i, bonus: 54 },
|
|
737
|
+
{ pattern: /event|schedule/i, bonus: 18 },
|
|
738
|
+
],
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
slotPatterns: [/\bbold\b/i],
|
|
742
|
+
queryVariants: ['text b', 'bold', 'text bold'],
|
|
743
|
+
iconPreferences: [
|
|
744
|
+
{ pattern: /^text[_-]?b$|text-bold|bold/i, bonus: 66 },
|
|
745
|
+
],
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
slotPatterns: [/italic/i],
|
|
749
|
+
queryVariants: ['text italic', 'italic'],
|
|
750
|
+
iconPreferences: [
|
|
751
|
+
{ pattern: /^text[_-]?italic$|italic/i, bonus: 66 },
|
|
752
|
+
],
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
priority: 130,
|
|
756
|
+
slotPatterns: [/broken\s+link/i, /link\s+(broken|break|disabled|off)/i],
|
|
757
|
+
queryVariants: ['broken link', 'link break', 'link slash'],
|
|
758
|
+
iconPreferences: [
|
|
759
|
+
{ pattern: /link.*(break|broken|slash|off)|(break|broken|slash|off).*link/i, bonus: 180 },
|
|
760
|
+
{ pattern: /^link(?:_|-|$)|(?:_|-)link(?:_|-|$)/i, bonus: 10 },
|
|
761
|
+
],
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
priority: 130,
|
|
765
|
+
slotPatterns: [/(broken|disabled|off)\s+(image|photo|picture)/i, /(image|photo|picture)\s+(broken|disabled|off)/i],
|
|
766
|
+
queryVariants: ['photo off', 'image off', 'broken image', 'image broken'],
|
|
767
|
+
iconPreferences: [
|
|
768
|
+
{ pattern: /(image|photo|picture).*(broken|off|slash)|(broken|off|slash).*(image|photo|picture)/i, bonus: 240 },
|
|
769
|
+
{ pattern: /^image(?:_|-|$)|(?:_|-)image(?:_|-|$)|picture|photo/i, bonus: 10 },
|
|
770
|
+
],
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
priority: 130,
|
|
774
|
+
slotPatterns: [/(comments?|chat|discussion)\s+(off|disabled|muted|slash)/i, /(off|disabled|muted|slash)\s+(comments?|chat|discussion)/i],
|
|
775
|
+
queryVariants: ['chat slash', 'comment off', 'comments off', 'message slash'],
|
|
776
|
+
iconPreferences: [
|
|
777
|
+
{ pattern: /(chat|comment|message).*(slash|off|x)|(slash|off|x).*(chat|comment|message)/i, bonus: 180 },
|
|
778
|
+
{ pattern: /chat|comment|message/i, bonus: 10 },
|
|
779
|
+
],
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
slotPatterns: [/\blink\b/i, /hyperlink/i],
|
|
783
|
+
queryVariants: ['link', 'link simple', 'hyperlink'],
|
|
784
|
+
iconPreferences: [
|
|
785
|
+
{ pattern: /^link(?:_|-|$)|(?:_|-)link(?:_|-|$)/i, bonus: 56 },
|
|
786
|
+
{ pattern: /chain/i, bonus: 14 },
|
|
787
|
+
{ pattern: /break|broken|slash|unlink|brand/i, bonus: -120 },
|
|
788
|
+
],
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
slotPatterns: [/image/i, /photo/i, /picture/i],
|
|
792
|
+
queryVariants: ['image', 'picture', 'photo'],
|
|
793
|
+
iconPreferences: [
|
|
794
|
+
{ pattern: /^image(?:_|-|$)|(?:_|-)image(?:_|-|$)|picture|photo/i, bonus: 56 },
|
|
795
|
+
{ pattern: /broken|off|slash|brand/i, bonus: -120 },
|
|
796
|
+
],
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
priority: 80,
|
|
800
|
+
slotPatterns: [/comments?/i, /chat/i, /discussion/i],
|
|
801
|
+
queryVariants: ['chat text', 'comments', 'message dots'],
|
|
802
|
+
iconPreferences: [
|
|
803
|
+
{ pattern: /chat|comment|message/i, bonus: 76 },
|
|
804
|
+
{ pattern: /slash|off|x$|brand/i, bonus: -120 },
|
|
805
|
+
],
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
slotPatterns: [/undo/i],
|
|
809
|
+
queryVariants: ['undo', 'arrow counter clockwise', 'rotate left'],
|
|
810
|
+
iconPreferences: [
|
|
811
|
+
{ pattern: /^undo$|arrow-counter-clockwise|arrow_counter_clockwise|rotate.*left/i, bonus: 66 },
|
|
812
|
+
{ pattern: /^redo$|^arrows?[_-]clockwise$|clock[_-]clockwise|arrow_clockwise|rotate.*right/i, bonus: -120 },
|
|
813
|
+
],
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
slotPatterns: [/redo/i],
|
|
817
|
+
queryVariants: ['redo', 'arrow clockwise', 'rotate right'],
|
|
818
|
+
iconPreferences: [
|
|
819
|
+
{ pattern: /^redo$|arrow-clockwise|arrow_clockwise|rotate.*right/i, bonus: 66 },
|
|
820
|
+
{ pattern: /^undo$|^arrows?[_-]counter[_-]clockwise$|clock[_-]counter[_-]clockwise|arrow_counter_clockwise|rotate.*left/i, bonus: -120 },
|
|
821
|
+
],
|
|
822
|
+
},
|
|
823
|
+
]);
|
|
336
824
|
|
|
337
|
-
const SLOT_PREFERENCE_RULES = Object.freeze({
|
|
338
|
-
|
|
825
|
+
const SLOT_PREFERENCE_RULES = Object.freeze({
|
|
826
|
+
lucide: [
|
|
827
|
+
{
|
|
828
|
+
slotPatterns: [/\b(add|new|create)\s+bookmark\b/i, /\bbookmark\s+(add|new|create)\b/i],
|
|
829
|
+
iconPreferences: [
|
|
830
|
+
{ pattern: /^bookmark-plus$/i, bonus: 500 },
|
|
831
|
+
{ pattern: /^bookmark$/i, bonus: 40 },
|
|
832
|
+
{ pattern: /waves-ladder|map-pin/i, bonus: -220 },
|
|
833
|
+
],
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
slotPatterns: [/users/i, /team/i],
|
|
837
|
+
iconPreferences: [
|
|
838
|
+
{ pattern: /^users$/i, bonus: 28 },
|
|
839
|
+
{ pattern: /^users-2$/i, bonus: 20 },
|
|
840
|
+
{ pattern: /^user-2$/i, bonus: -12 },
|
|
841
|
+
],
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
slotPatterns: [/database/i, /storage/i],
|
|
845
|
+
iconPreferences: [
|
|
846
|
+
{ pattern: /^database$/i, bonus: 48 },
|
|
847
|
+
{ pattern: /^database-(backup|search)$/i, bonus: 28 },
|
|
848
|
+
{ pattern: /^database-zap$/i, bonus: -34 },
|
|
849
|
+
],
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
slotPatterns: [/security/i, /privacy/i, /safe/i, /protection/i],
|
|
853
|
+
iconPreferences: [
|
|
854
|
+
{ pattern: /^shield$/i, bonus: 80 },
|
|
855
|
+
{ pattern: /^shield-check$/i, bonus: 76 },
|
|
856
|
+
{ pattern: /^lock$/i, bonus: 52 },
|
|
857
|
+
{ pattern: /^lock-keyhole$/i, bonus: 48 },
|
|
858
|
+
{ pattern: /open|unlock|ban|minus|off|slash/i, bonus: -80 },
|
|
859
|
+
],
|
|
860
|
+
},
|
|
861
|
+
],
|
|
862
|
+
mingcute: [
|
|
339
863
|
{
|
|
340
|
-
slotPatterns: [/home/i],
|
|
341
|
-
iconPreferences: [
|
|
342
|
-
{ pattern: /^home_3_line$/i, bonus:
|
|
343
|
-
{ pattern: /^home_2_line$/i, bonus: 8 },
|
|
344
|
-
{ pattern: /^home_1_line$/i, bonus: 4 },
|
|
345
|
-
|
|
864
|
+
slotPatterns: [/home/i],
|
|
865
|
+
iconPreferences: [
|
|
866
|
+
{ pattern: /^home_3_line$/i, bonus: 160 },
|
|
867
|
+
{ pattern: /^home_2_line$/i, bonus: 8 },
|
|
868
|
+
{ pattern: /^home_1_line$/i, bonus: 4 },
|
|
869
|
+
{ pattern: /^home_wifi_line$/i, bonus: -24 },
|
|
870
|
+
],
|
|
346
871
|
},
|
|
347
872
|
{
|
|
348
873
|
slotPatterns: [/create/i, /add/i, /plus/i, /compose/i],
|
|
@@ -352,36 +877,132 @@ const SLOT_PREFERENCE_RULES = Object.freeze({
|
|
|
352
877
|
{ pattern: /^add_circle_line$/i, bonus: 6 },
|
|
353
878
|
],
|
|
354
879
|
},
|
|
355
|
-
{
|
|
356
|
-
slotPatterns: [/alerts?/i, /notification/i],
|
|
357
|
-
iconPreferences: [
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
880
|
+
{
|
|
881
|
+
slotPatterns: [/alerts?/i, /notification/i],
|
|
882
|
+
iconPreferences: [
|
|
883
|
+
{ pattern: /^notification_line$/i, bonus: 36 },
|
|
884
|
+
{ pattern: /^notification_off_line$/i, bonus: -28 },
|
|
885
|
+
],
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
slotPatterns: [/profile/i, /user/i, /account/i],
|
|
889
|
+
iconPreferences: [
|
|
890
|
+
{ pattern: /^user_1_line$/i, bonus: 44 },
|
|
891
|
+
{ pattern: /^user_4_line$/i, bonus: -16 },
|
|
892
|
+
],
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
slotPatterns: [/search/i],
|
|
896
|
+
iconPreferences: [
|
|
897
|
+
{ pattern: /^search_line$/i, bonus: 70 },
|
|
898
|
+
{ pattern: /^search_[23]_line$/i, bonus: 18 },
|
|
899
|
+
{ pattern: /^search_.*_ai_line$/i, bonus: -70 },
|
|
900
|
+
],
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
slotPatterns: [/bookmark/i, /saved?/i],
|
|
904
|
+
iconPreferences: [
|
|
905
|
+
{ pattern: /^bookmark_line$/i, bonus: 34 },
|
|
906
|
+
{ pattern: /^bookmarks_line$/i, bonus: 28 },
|
|
907
|
+
{ pattern: /^bookmark_(add|edit|remove)_line$/i, bonus: -20 },
|
|
908
|
+
],
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
slotPatterns: [/trending/i, /popular/i, /top stories/i],
|
|
912
|
+
iconPreferences: [
|
|
913
|
+
{ pattern: /^trending_up_line$/i, bonus: 150 },
|
|
914
|
+
{ pattern: /^trending_down_line$/i, bonus: -30 },
|
|
915
|
+
],
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
slotPatterns: [/read more/i, /continue/i, /open article/i],
|
|
919
|
+
iconPreferences: [
|
|
920
|
+
{ pattern: /^arrow_right_line$/i, bonus: 72 },
|
|
921
|
+
{ pattern: /^arrow_to_right_line$/i, bonus: 40 },
|
|
922
|
+
{ pattern: /^align_arrow_right_line$/i, bonus: -30 },
|
|
923
|
+
],
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
slotPatterns: [/categor(?:y|ies)/i, /chips?/i, /filter/i, /topics?/i],
|
|
927
|
+
iconPreferences: [
|
|
928
|
+
{ pattern: /^filter_line$/i, bonus: 34 },
|
|
929
|
+
{ pattern: /^filter_[23]_line$/i, bonus: 22 },
|
|
930
|
+
{ pattern: /^tag_line$/i, bonus: 16 },
|
|
931
|
+
],
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
slotPatterns: [/news/i, /article/i, /headline/i, /logo/i, /title/i],
|
|
935
|
+
iconPreferences: [
|
|
936
|
+
{ pattern: /^news_line$/i, bonus: 76 },
|
|
937
|
+
{ pattern: /^news_2_line$/i, bonus: 46 },
|
|
938
|
+
{ pattern: /^appstore_line$/i, bonus: -44 },
|
|
939
|
+
{ pattern: /^apple_fruit_line$/i, bonus: -44 },
|
|
940
|
+
],
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
slotPatterns: [/projects?/i],
|
|
944
|
+
iconPreferences: [
|
|
945
|
+
{ pattern: /^folder_locked_line$/i, bonus: -70 },
|
|
946
|
+
],
|
|
947
|
+
},
|
|
948
|
+
],
|
|
949
|
+
});
|
|
365
950
|
|
|
366
|
-
function normalizeText(value) {
|
|
367
|
-
return String(value || '')
|
|
368
|
-
.toLowerCase()
|
|
951
|
+
function normalizeText(value) {
|
|
952
|
+
return String(value || '')
|
|
953
|
+
.toLowerCase()
|
|
369
954
|
.replace(/[_:]+/g, ' ')
|
|
370
955
|
.replace(/[^a-z0-9\s-]/g, ' ')
|
|
371
956
|
.replace(/-/g, ' ')
|
|
372
957
|
.replace(/\s+/g, ' ')
|
|
373
|
-
.trim();
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
958
|
+
.trim();
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
function normalizeToken(token) {
|
|
962
|
+
const value = String(token || '').toLowerCase();
|
|
963
|
+
if (value.length > 4 && value.endsWith('ies')) return `${value.slice(0, -3)}y`;
|
|
964
|
+
if (value.length > 3 && value.endsWith('es')) return value.slice(0, -2);
|
|
965
|
+
if (value.length > 3 && value.endsWith('s')) return value.slice(0, -1);
|
|
966
|
+
return value;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
function tokenizeText(value) {
|
|
970
|
+
const normalized = normalizeText(value);
|
|
971
|
+
if (!normalized) return [];
|
|
972
|
+
const tokens = normalized.split(' ');
|
|
973
|
+
return dedupe([...tokens, ...tokens.map(normalizeToken)]);
|
|
974
|
+
}
|
|
975
|
+
|
|
381
976
|
function dedupe(values) {
|
|
382
977
|
return [...new Set(values.filter(Boolean))];
|
|
383
978
|
}
|
|
384
979
|
|
|
980
|
+
function buildDirectLocalizedIntentTerms(value) {
|
|
981
|
+
const text = String(value || '');
|
|
982
|
+
return DIRECT_LOCALIZED_INTENT_RULES
|
|
983
|
+
.filter((rule) => rule.pattern.test(text))
|
|
984
|
+
.flatMap((rule) => rule.terms);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
function buildRequestedTermSet(intentTerms = []) {
|
|
988
|
+
return new Set(intentTerms.map(normalizeToken).filter(Boolean));
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function isVariantTokenRequested(token, requestedTerms) {
|
|
992
|
+
const normalizedToken = normalizeToken(token);
|
|
993
|
+
if (requestedTerms.has(normalizedToken)) return true;
|
|
994
|
+
const aliases = REQUESTED_VARIANT_ALIASES[normalizedToken] || [];
|
|
995
|
+
return aliases.some((alias) => requestedTerms.has(normalizeToken(alias)));
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function isIconVariantExplicitlyRequested(icon, intentTerms = []) {
|
|
999
|
+
const requestedTerms = buildRequestedTermSet(intentTerms);
|
|
1000
|
+
return tokenizeText(icon.id).some((token) => (
|
|
1001
|
+
VARIANT_TOKENS.has(normalizeToken(token)) &&
|
|
1002
|
+
isVariantTokenRequested(token, requestedTerms)
|
|
1003
|
+
));
|
|
1004
|
+
}
|
|
1005
|
+
|
|
385
1006
|
function buildLocalizedVariants(value, locale) {
|
|
386
1007
|
if (!locale) return [];
|
|
387
1008
|
const expanded = expandCjkQuery(value, {
|
|
@@ -433,13 +1054,17 @@ function buildSlotIntentTerms(task, slot, locale = null) {
|
|
|
433
1054
|
const taskTokens = tokenizeText(task);
|
|
434
1055
|
const slotTokens = tokenizeText(slot);
|
|
435
1056
|
const usefulSlotTokens = slotTokens.filter((token) => !GENERIC_SLOT_WORDS.has(token));
|
|
1057
|
+
const localizedSlotTokens = buildLocalizedVariants(slot, locale).flatMap(tokenizeText);
|
|
1058
|
+
const localizedTaskTokens = buildLocalizedVariants(task, locale).flatMap(tokenizeText);
|
|
1059
|
+
const directSlotTokens = buildDirectLocalizedIntentTerms(slot);
|
|
436
1060
|
|
|
437
1061
|
const expanded = [...usefulSlotTokens];
|
|
438
1062
|
|
|
439
1063
|
const usefulTaskTokens = taskTokens.filter((token) => !GENERIC_SLOT_WORDS.has(token));
|
|
440
1064
|
expanded.push(...usefulTaskTokens);
|
|
441
|
-
expanded.push(...
|
|
442
|
-
expanded.push(...
|
|
1065
|
+
expanded.push(...localizedSlotTokens);
|
|
1066
|
+
expanded.push(...localizedTaskTokens);
|
|
1067
|
+
expanded.push(...directSlotTokens);
|
|
443
1068
|
|
|
444
1069
|
const variants = buildIntentQueryVariants(`${slot} ${task}`, {
|
|
445
1070
|
baseQuery: slot,
|
|
@@ -449,18 +1074,20 @@ function buildSlotIntentTerms(task, slot, locale = null) {
|
|
|
449
1074
|
expanded.push(...tokenizeText(variant));
|
|
450
1075
|
}
|
|
451
1076
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
1077
|
+
const slotRuleTerms = dedupe([...usefulSlotTokens, ...localizedSlotTokens, ...directSlotTokens]);
|
|
1078
|
+
for (const rule of getMatchingSlotRules(slot, slotRuleTerms)) {
|
|
1079
|
+
for (const variant of rule.queryVariants || []) {
|
|
1080
|
+
expanded.push(...tokenizeText(variant));
|
|
1081
|
+
}
|
|
456
1082
|
}
|
|
457
1083
|
|
|
458
1084
|
return dedupe(expanded);
|
|
459
1085
|
}
|
|
460
1086
|
|
|
461
1087
|
function buildSlotQueryVariants(task, slot, locale = null) {
|
|
1088
|
+
const localizedSlotVariants = buildLocalizedVariants(slot, locale);
|
|
462
1089
|
const localizedVariants = [
|
|
463
|
-
...
|
|
1090
|
+
...localizedSlotVariants,
|
|
464
1091
|
...buildLocalizedVariants(`${slot} ${task}`, locale),
|
|
465
1092
|
];
|
|
466
1093
|
const variants = buildIntentQueryVariants(`${slot} ${task}`, {
|
|
@@ -470,82 +1097,115 @@ function buildSlotQueryVariants(task, slot, locale = null) {
|
|
|
470
1097
|
variants.unshift(...localizedVariants);
|
|
471
1098
|
const usefulSlotTokens = tokenizeText(slot).filter((token) => !GENERIC_SLOT_WORDS.has(token));
|
|
472
1099
|
variants.push(...usefulSlotTokens);
|
|
473
|
-
const
|
|
474
|
-
|
|
1100
|
+
const slotRuleTerms = [
|
|
1101
|
+
...tokenizeText(`${slot} ${localizedSlotVariants.join(' ')}`),
|
|
1102
|
+
...buildDirectLocalizedIntentTerms(slot),
|
|
1103
|
+
]
|
|
1104
|
+
.filter((token) => !GENERIC_SLOT_WORDS.has(token));
|
|
1105
|
+
const ruleVariants = getMatchingSlotRules(slot, slotRuleTerms)
|
|
475
1106
|
.flatMap((rule) => rule.queryVariants || []);
|
|
476
1107
|
variants.unshift(...ruleVariants);
|
|
477
1108
|
return dedupe(variants).slice(0, 12);
|
|
478
1109
|
}
|
|
479
1110
|
|
|
480
|
-
function scoreLexicalFit(icon, intentTerms, slotLabel) {
|
|
481
|
-
const tokens = new Set([
|
|
482
|
-
...tokenizeText(icon.id),
|
|
483
|
-
...tokenizeText(icon.name),
|
|
484
|
-
...tokenizeText(`${icon.lib}:${icon.id}`),
|
|
485
|
-
]);
|
|
486
|
-
const normalizedId = normalizeText(icon.id);
|
|
487
|
-
const normalizedName = normalizeText(icon.name);
|
|
488
|
-
const normalizedSlot = normalizeText(slotLabel);
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (normalizedId
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
1111
|
+
function scoreLexicalFit(icon, intentTerms, slotLabel, taskLabel = '') {
|
|
1112
|
+
const tokens = new Set([
|
|
1113
|
+
...tokenizeText(icon.id),
|
|
1114
|
+
...tokenizeText(icon.name),
|
|
1115
|
+
...tokenizeText(`${icon.lib}:${icon.id}`),
|
|
1116
|
+
]);
|
|
1117
|
+
const normalizedId = normalizeText(icon.id);
|
|
1118
|
+
const normalizedName = normalizeText(icon.name);
|
|
1119
|
+
const normalizedSlot = normalizeText(slotLabel);
|
|
1120
|
+
const slotTerms = tokenizeText(slotLabel).filter((token) => !GENERIC_SLOT_WORDS.has(token));
|
|
1121
|
+
const taskTerms = tokenizeText(taskLabel).filter((token) => !GENERIC_SLOT_WORDS.has(token));
|
|
1122
|
+
|
|
1123
|
+
let score = 0;
|
|
1124
|
+
for (const term of slotTerms) {
|
|
1125
|
+
if (tokens.has(term)) score += 22;
|
|
1126
|
+
else if (normalizedId.includes(term) || normalizedName.includes(term)) score += 14;
|
|
1127
|
+
|
|
1128
|
+
if (normalizedId === term || normalizedName === term) {
|
|
1129
|
+
score += 24;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
for (const term of intentTerms) {
|
|
1134
|
+
if (tokens.has(term)) score += 12;
|
|
1135
|
+
else if (normalizedId.includes(term) || normalizedName.includes(term)) score += 7;
|
|
1136
|
+
|
|
1137
|
+
if (normalizedId === term || normalizedName === term) {
|
|
1138
|
+
score += 14;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
for (const term of taskTerms) {
|
|
1143
|
+
if (tokens.has(term)) score += 3;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
if (normalizedSlot && (normalizedId === normalizedSlot || normalizedName === normalizedSlot)) {
|
|
1147
|
+
score += 26;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
return score;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
function getVariantPenalty(icon, intentTerms = []) {
|
|
1154
|
+
const normalizedId = normalizeText(icon.id);
|
|
1155
|
+
const requestedTerms = buildRequestedTermSet(intentTerms);
|
|
1156
|
+
let penalty = 0;
|
|
1157
|
+
for (const rule of VARIANT_PENALTIES) {
|
|
1158
|
+
if (!rule.pattern.test(normalizedId)) continue;
|
|
1159
|
+
if (isVariantTokenRequested(rule.token, requestedTerms)) continue;
|
|
1160
|
+
penalty += rule.penalty;
|
|
1161
|
+
}
|
|
1162
|
+
return penalty;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
function getBrandPenalty(icon, intentTerms = []) {
|
|
1166
|
+
const requestedTerms = buildRequestedTermSet(intentTerms);
|
|
1167
|
+
if (isVariantTokenRequested('brand', requestedTerms)) return 0;
|
|
1168
|
+
return icon.lib === 'simpleicons' ? 80 : 0;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
518
1171
|
function getMatchingSlotRules(slotLabel, intentTerms = []) {
|
|
519
1172
|
const rawSlotText = String(slotLabel || '');
|
|
520
1173
|
const slotText = normalizeText(slotLabel);
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
1174
|
+
const intentText = normalizeText(intentTerms.join(' '));
|
|
1175
|
+
return COMMON_SLOT_PREFERENCE_RULES
|
|
1176
|
+
.filter((rule) => rule.slotPatterns.some((pattern) => (
|
|
1177
|
+
pattern.test(slotText) ||
|
|
1178
|
+
pattern.test(rawSlotText) ||
|
|
1179
|
+
pattern.test(intentText)
|
|
1180
|
+
)))
|
|
1181
|
+
.sort((left, right) => (right.priority || 0) - (left.priority || 0));
|
|
524
1182
|
}
|
|
525
1183
|
|
|
526
|
-
function scoreSlotPreferenceRules(icon, rules = [],
|
|
527
|
-
let bonus = 0;
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
1184
|
+
function scoreSlotPreferenceRules(icon, rules = [], intentTerms = []) {
|
|
1185
|
+
let bonus = 0;
|
|
1186
|
+
const explicitlyRequestedVariant = isIconVariantExplicitlyRequested(icon, intentTerms);
|
|
1187
|
+
|
|
1188
|
+
for (const rule of rules) {
|
|
1189
|
+
for (const preference of rule.iconPreferences) {
|
|
1190
|
+
if (preference.pattern.test(icon.id)) {
|
|
1191
|
+
if (preference.bonus < 0 && explicitlyRequestedVariant) continue;
|
|
1192
|
+
bonus += preference.bonus;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
535
1195
|
}
|
|
536
1196
|
|
|
537
1197
|
return bonus;
|
|
538
1198
|
}
|
|
539
1199
|
|
|
540
|
-
function getSlotPreferenceBonus(icon, slotLabel, intentTerms, library) {
|
|
541
|
-
const slotText = `${slotLabel} ${
|
|
542
|
-
const commonRules = getMatchingSlotRules(slotLabel,
|
|
543
|
-
const libraryRules = (SLOT_PREFERENCE_RULES[library] || [])
|
|
544
|
-
.filter((rule) => rule.slotPatterns.some((pattern) => pattern.test(slotText)));
|
|
545
|
-
|
|
546
|
-
return scoreSlotPreferenceRules(icon, commonRules,
|
|
547
|
-
scoreSlotPreferenceRules(icon, libraryRules,
|
|
548
|
-
}
|
|
1200
|
+
function getSlotPreferenceBonus(icon, slotLabel, intentTerms, library, requestedVariantTerms = intentTerms) {
|
|
1201
|
+
const slotText = `${slotLabel} ${requestedVariantTerms.join(' ')}`;
|
|
1202
|
+
const commonRules = getMatchingSlotRules(slotLabel, requestedVariantTerms);
|
|
1203
|
+
const libraryRules = (SLOT_PREFERENCE_RULES[library] || [])
|
|
1204
|
+
.filter((rule) => rule.slotPatterns.some((pattern) => pattern.test(slotText)));
|
|
1205
|
+
|
|
1206
|
+
return scoreSlotPreferenceRules(icon, commonRules, requestedVariantTerms) +
|
|
1207
|
+
scoreSlotPreferenceRules(icon, libraryRules, requestedVariantTerms);
|
|
1208
|
+
}
|
|
549
1209
|
|
|
550
1210
|
function summarizeSemanticFit(slotLabel, semanticRecord, intentTerms) {
|
|
551
1211
|
if (semanticRecord?.depicts && semanticRecord?.use_when) {
|
|
@@ -563,7 +1223,7 @@ function summarizeSemanticFit(slotLabel, semanticRecord, intentTerms) {
|
|
|
563
1223
|
return `Best available match for ${slotLabel}.`;
|
|
564
1224
|
}
|
|
565
1225
|
|
|
566
|
-
function buildWhySelected(slotLabel, semanticRecord, iconResult) {
|
|
1226
|
+
function buildWhySelected(slotLabel, semanticRecord, iconResult) {
|
|
567
1227
|
const label = semanticRecord?.label || iconResult.name;
|
|
568
1228
|
if (semanticRecord?.depicts) {
|
|
569
1229
|
return `${label} matches ${slotLabel} and visually reads as ${String(semanticRecord.depicts).toLowerCase()}.`;
|
|
@@ -571,24 +1231,43 @@ function buildWhySelected(slotLabel, semanticRecord, iconResult) {
|
|
|
571
1231
|
if (semanticRecord?.use_when) {
|
|
572
1232
|
return `${label} matches ${slotLabel}. ${semanticRecord.use_when}`;
|
|
573
1233
|
}
|
|
574
|
-
return `${label} is the clearest match for ${slotLabel} from the current library.`;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
function buildCandidatePayload(
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
1234
|
+
return `${label} is the clearest match for ${slotLabel} from the current library.`;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
function buildCandidatePayload(
|
|
1238
|
+
slotLabel,
|
|
1239
|
+
iconResult,
|
|
1240
|
+
semanticRecord,
|
|
1241
|
+
intentTerms,
|
|
1242
|
+
responseMode = 'plan',
|
|
1243
|
+
includeSvg = false,
|
|
1244
|
+
includeReason = true
|
|
1245
|
+
) {
|
|
1246
|
+
const payload = {
|
|
1247
|
+
id: iconResult.id,
|
|
1248
|
+
library: iconResult.library,
|
|
1249
|
+
name: iconResult.name,
|
|
1250
|
+
style: iconResult.style || 'outline',
|
|
1251
|
+
label: semanticRecord?.label || iconResult.semantic?.label || iconResult.name,
|
|
1252
|
+
};
|
|
1253
|
+
|
|
1254
|
+
if (includeReason) {
|
|
1255
|
+
payload.semantic_fit = summarizeSemanticFit(slotLabel, semanticRecord, intentTerms);
|
|
1256
|
+
payload.why_selected = buildWhySelected(slotLabel, semanticRecord, iconResult);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
if (includeSvg) {
|
|
1260
|
+
payload.svg = iconResult.svg;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
if (responseMode === 'full') {
|
|
1264
|
+
payload.semantic = buildPublicSemanticPayload(semanticRecord) || iconResult.semantic || null;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
return payload;
|
|
1268
|
+
}
|
|
590
1269
|
|
|
591
|
-
async function mapWithConcurrency(items, limit, mapper) {
|
|
1270
|
+
async function mapWithConcurrency(items, limit, mapper) {
|
|
592
1271
|
const results = new Array(items.length);
|
|
593
1272
|
let nextIndex = 0;
|
|
594
1273
|
|
|
@@ -601,28 +1280,58 @@ async function mapWithConcurrency(items, limit, mapper) {
|
|
|
601
1280
|
}
|
|
602
1281
|
|
|
603
1282
|
const workerCount = Math.min(Math.max(1, limit), items.length);
|
|
604
|
-
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
605
|
-
return results;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
1283
|
+
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
1284
|
+
return results;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
function getConfidence(topScore, nextScore = 0) {
|
|
1288
|
+
if (topScore >= 90 && topScore - nextScore >= 20) {
|
|
1289
|
+
return { level: 'high', score: topScore };
|
|
1290
|
+
}
|
|
1291
|
+
if (topScore >= 45) {
|
|
1292
|
+
return { level: 'medium', score: topScore };
|
|
1293
|
+
}
|
|
1294
|
+
return { level: 'low', score: topScore };
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
function buildLowConfidenceHint(slotLabel, queriesUsed) {
|
|
1298
|
+
return `Low confidence for ${slotLabel}. Try search_icons with: ${queriesUsed.slice(0, 3).join(', ')}.`;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
function normalizeResponseMode(responseMode) {
|
|
1302
|
+
if (responseMode === 'assets' || responseMode === 'full') return responseMode;
|
|
1303
|
+
return 'plan';
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
function isNoisyAlternative(entry) {
|
|
1307
|
+
return entry.variantPenalty >= 12 || entry.brandPenalty >= 12 || entry.slotPreferenceBonus < 0;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
export async function recommendIconsForTask({
|
|
1311
|
+
task,
|
|
1312
|
+
library,
|
|
611
1313
|
style = 'any',
|
|
612
1314
|
locale = null,
|
|
613
1315
|
slots,
|
|
614
1316
|
limitPerSlot = 3,
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
1317
|
+
responseMode = 'plan',
|
|
1318
|
+
searchIconsForQuery,
|
|
1319
|
+
buildIconResult,
|
|
1320
|
+
semanticMap,
|
|
1321
|
+
}) {
|
|
1322
|
+
const normalizedResponseMode = normalizeResponseMode(responseMode);
|
|
1323
|
+
const scoredSlotResults = await mapWithConcurrency(slots, SLOT_SEARCH_CONCURRENCY, async (slotLabel) => {
|
|
620
1324
|
const intentTerms = buildSlotIntentTerms(task, slotLabel, locale);
|
|
621
|
-
const
|
|
1325
|
+
const requestedVariantTerms = dedupe([
|
|
1326
|
+
...tokenizeText(slotLabel),
|
|
1327
|
+
...buildLocalizedVariants(slotLabel, locale).flatMap(tokenizeText),
|
|
1328
|
+
...buildDirectLocalizedIntentTerms(slotLabel),
|
|
1329
|
+
]);
|
|
1330
|
+
const queryVariants = buildSlotQueryVariants(task, slotLabel, locale).slice(0, locale ? 8 : 4);
|
|
622
1331
|
const pooledIcons = [];
|
|
623
|
-
const seen = new Set();
|
|
624
|
-
|
|
625
|
-
const resultGroups = await mapWithConcurrency(queryVariants,
|
|
1332
|
+
const seen = new Set();
|
|
1333
|
+
|
|
1334
|
+
const resultGroups = await mapWithConcurrency(queryVariants, SLOT_QUERY_CONCURRENCY, async (queryVariant) => {
|
|
626
1335
|
try {
|
|
627
1336
|
return await searchIconsForQuery({
|
|
628
1337
|
query: queryVariant,
|
|
@@ -646,62 +1355,137 @@ export async function recommendIconsForTask({
|
|
|
646
1355
|
}
|
|
647
1356
|
|
|
648
1357
|
const scored = pooledIcons
|
|
649
|
-
.map((icon, index) => {
|
|
650
|
-
const semanticRecord = getSemanticRecordForIcon(semanticMap, icon);
|
|
651
|
-
const semanticQuery = queryVariants.join(' ');
|
|
652
|
-
const semanticScore = semanticRecord ? scoreSemanticAlignment(semanticQuery, semanticRecord) * 3 : 0;
|
|
653
|
-
const lexicalScore = scoreLexicalFit(icon, intentTerms, slotLabel);
|
|
654
|
-
const semanticBonus = semanticRecord ? 6 : 0;
|
|
655
|
-
const variantPenalty = getVariantPenalty(icon);
|
|
656
|
-
const
|
|
657
|
-
const
|
|
658
|
-
const
|
|
659
|
-
|
|
1358
|
+
.map((icon, index) => {
|
|
1359
|
+
const semanticRecord = getSemanticRecordForIcon(semanticMap, icon);
|
|
1360
|
+
const semanticQuery = queryVariants.join(' ');
|
|
1361
|
+
const semanticScore = semanticRecord ? scoreSemanticAlignment(semanticQuery, semanticRecord) * 3 : 0;
|
|
1362
|
+
const lexicalScore = scoreLexicalFit(icon, intentTerms, slotLabel, task);
|
|
1363
|
+
const semanticBonus = semanticRecord ? 6 : 0;
|
|
1364
|
+
const variantPenalty = getVariantPenalty(icon, requestedVariantTerms);
|
|
1365
|
+
const brandPenalty = getBrandPenalty(icon, requestedVariantTerms);
|
|
1366
|
+
const slotPreferenceBonus = getSlotPreferenceBonus(icon, slotLabel, intentTerms, library, requestedVariantTerms);
|
|
1367
|
+
const intentProfile = buildSearchIntentProfile(`${slotLabel} ${task}`);
|
|
1368
|
+
const intentAdjustment = getIntentCandidateAdjustment(icon, intentProfile);
|
|
1369
|
+
|
|
660
1370
|
return {
|
|
661
|
-
icon,
|
|
662
|
-
index,
|
|
663
|
-
semanticRecord,
|
|
664
|
-
|
|
665
|
-
|
|
1371
|
+
icon,
|
|
1372
|
+
index,
|
|
1373
|
+
semanticRecord,
|
|
1374
|
+
variantPenalty,
|
|
1375
|
+
brandPenalty,
|
|
1376
|
+
slotPreferenceBonus,
|
|
1377
|
+
totalScore:
|
|
666
1378
|
semanticScore +
|
|
667
1379
|
lexicalScore +
|
|
668
1380
|
semanticBonus +
|
|
669
|
-
slotPreferenceBonus +
|
|
670
|
-
intentAdjustment.boost -
|
|
671
|
-
intentAdjustment.penalty -
|
|
672
|
-
variantPenalty
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
.
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1381
|
+
slotPreferenceBonus +
|
|
1382
|
+
intentAdjustment.boost -
|
|
1383
|
+
intentAdjustment.penalty -
|
|
1384
|
+
variantPenalty -
|
|
1385
|
+
brandPenalty,
|
|
1386
|
+
};
|
|
1387
|
+
})
|
|
1388
|
+
.filter((entry) => entry.totalScore > 0)
|
|
1389
|
+
.sort((left, right) => {
|
|
1390
|
+
if (right.totalScore !== left.totalScore) return right.totalScore - left.totalScore;
|
|
1391
|
+
if (right.slotPreferenceBonus !== left.slotPreferenceBonus) {
|
|
1392
|
+
return right.slotPreferenceBonus - left.slotPreferenceBonus;
|
|
1393
|
+
}
|
|
1394
|
+
return left.index - right.index;
|
|
1395
|
+
})
|
|
1396
|
+
.slice(0, Math.max(limitPerSlot * 3, 8));
|
|
1397
|
+
|
|
1398
|
+
return {
|
|
1399
|
+
slot: slotLabel,
|
|
1400
|
+
queries_used: queryVariants,
|
|
1401
|
+
intentTerms,
|
|
1402
|
+
requestedVariantTerms,
|
|
1403
|
+
scored,
|
|
1404
|
+
};
|
|
1405
|
+
});
|
|
1406
|
+
|
|
1407
|
+
const usedIconKeys = new Set();
|
|
1408
|
+
const slotResults = [];
|
|
1409
|
+
for (const slotResult of scoredSlotResults) {
|
|
1410
|
+
const sorted = [...slotResult.scored].sort((left, right) => {
|
|
1411
|
+
const leftKey = `${left.icon.lib}:${left.icon.id}`;
|
|
1412
|
+
const rightKey = `${right.icon.lib}:${right.icon.id}`;
|
|
1413
|
+
const leftDuplicatePenalty = usedIconKeys.has(leftKey) ? 80 : 0;
|
|
1414
|
+
const rightDuplicatePenalty = usedIconKeys.has(rightKey) ? 80 : 0;
|
|
1415
|
+
const leftScore = left.totalScore - leftDuplicatePenalty;
|
|
1416
|
+
const rightScore = right.totalScore - rightDuplicatePenalty;
|
|
1417
|
+
|
|
1418
|
+
if (rightScore !== leftScore) return rightScore - leftScore;
|
|
1419
|
+
return left.index - right.index;
|
|
1420
|
+
});
|
|
1421
|
+
const selectedEntries = [];
|
|
1422
|
+
const primaryEntry = sorted.find((entry) => (
|
|
1423
|
+
!isNoisyAlternative(entry) &&
|
|
1424
|
+
!usedIconKeys.has(`${entry.icon.lib}:${entry.icon.id}`)
|
|
1425
|
+
)) || sorted.find((entry) => !isNoisyAlternative(entry)) || sorted[0];
|
|
1426
|
+
if (primaryEntry) {
|
|
1427
|
+
selectedEntries.push(primaryEntry);
|
|
1428
|
+
}
|
|
1429
|
+
for (const entry of sorted) {
|
|
1430
|
+
if (selectedEntries.length >= limitPerSlot) break;
|
|
1431
|
+
if (selectedEntries.includes(entry)) continue;
|
|
1432
|
+
if (isNoisyAlternative(entry)) continue;
|
|
1433
|
+
selectedEntries.push(entry);
|
|
1434
|
+
}
|
|
1435
|
+
const preparedCandidates = [];
|
|
1436
|
+
const preparedEntries = [];
|
|
1437
|
+
for (const [candidateIndex, entry] of selectedEntries.entries()) {
|
|
1438
|
+
const iconResult = await buildIconResult(entry.icon, { style });
|
|
1439
|
+
if (!iconResult?.svg) continue;
|
|
1440
|
+
const includeSvg = normalizedResponseMode === 'full' || (normalizedResponseMode === 'assets' && candidateIndex === 0);
|
|
1441
|
+
preparedCandidates.push(buildCandidatePayload(
|
|
1442
|
+
slotResult.slot,
|
|
1443
|
+
iconResult,
|
|
1444
|
+
entry.semanticRecord,
|
|
1445
|
+
slotResult.intentTerms,
|
|
1446
|
+
normalizedResponseMode,
|
|
1447
|
+
includeSvg,
|
|
1448
|
+
normalizedResponseMode !== 'plan' || candidateIndex === 0
|
|
1449
|
+
));
|
|
1450
|
+
preparedEntries.push(entry);
|
|
1451
|
+
}
|
|
1452
|
+
const chosen = preparedEntries[0] || primaryEntry || null;
|
|
1453
|
+
if (chosen) {
|
|
1454
|
+
usedIconKeys.add(`${chosen.icon.lib}:${chosen.icon.id}`);
|
|
1455
|
+
}
|
|
1456
|
+
const confidence = chosen
|
|
1457
|
+
? getConfidence(chosen.totalScore, sorted[1]?.totalScore || 0)
|
|
1458
|
+
: { level: 'low', score: 0 };
|
|
1459
|
+
|
|
1460
|
+
const slotPayload = {
|
|
1461
|
+
slot: slotResult.slot,
|
|
1462
|
+
confidence,
|
|
1463
|
+
recommended: preparedCandidates[0] || null,
|
|
1464
|
+
alternatives: preparedCandidates.slice(1),
|
|
1465
|
+
};
|
|
1466
|
+
if (confidence.level === 'low') {
|
|
1467
|
+
slotPayload.guidance = buildLowConfidenceHint(slotResult.slot, slotResult.queries_used);
|
|
1468
|
+
}
|
|
1469
|
+
if (normalizedResponseMode !== 'plan') {
|
|
1470
|
+
slotPayload.queries_used = slotResult.queries_used;
|
|
1471
|
+
}
|
|
1472
|
+
slotResults.push(slotPayload);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
const lowConfidenceSlots = slotResults
|
|
1476
|
+
.filter((slot) => !slot.recommended || slot.confidence?.level === 'low')
|
|
1477
|
+
.map((slot) => slot.slot);
|
|
1478
|
+
const allSlotsResolved = slotResults.every((slot) => Boolean(slot.recommended));
|
|
1479
|
+
|
|
1480
|
+
return {
|
|
1481
|
+
task,
|
|
1482
|
+
library: library || 'all',
|
|
1483
|
+
style,
|
|
1484
|
+
response_mode: normalizedResponseMode,
|
|
1485
|
+
slot_count: slots.length,
|
|
1486
|
+
all_slots_resolved: allSlotsResolved,
|
|
1487
|
+
low_confidence_slots: lowConfidenceSlots,
|
|
1488
|
+
fallback_recommended: !allSlotsResolved || lowConfidenceSlots.length > 0,
|
|
1489
|
+
results: slotResults,
|
|
1490
|
+
};
|
|
1491
|
+
}
|