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