Vijay Rohit Kanchusthambham nsarrazin commited on
Commit
7e91987
·
unverified ·
1 Parent(s): c3962a6

Search feature enhancements (#1841)

Browse files

* Added infinite scrolling to search feature.

* Added code to highlight the search keyword in search description

* feat: improve description layout

* feat: improve search context finding

* fix: remove all debug bits

---------

Co-authored-by: Nathan Sarrazin <sarrazin.nathan@gmail.com>

package-lock.json CHANGED
@@ -41,6 +41,7 @@
41
  "marked": "^12.0.1",
42
  "mongodb": "^5.8.0",
43
  "nanoid": "^5.0.9",
 
44
  "openid-client": "^5.4.2",
45
  "parquetjs": "^0.11.2",
46
  "pino": "^9.0.0",
@@ -3158,7 +3159,6 @@
3158
  "version": "1.1.9",
3159
  "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz",
3160
  "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==",
3161
- "devOptional": true,
3162
  "dependencies": {
3163
  "sparse-bitfield": "^3.0.3"
3164
  }
@@ -3891,6 +3891,65 @@
3891
  "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
3892
  "license": "BSD-3-Clause"
3893
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3894
  "node_modules/@reflink/reflink": {
3895
  "resolved": "node_modules/ipull/stub/@reflink/reflink",
3896
  "link": true
@@ -6120,6 +6179,26 @@
6120
  "node": ">=0.4.0"
6121
  }
6122
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6123
  "node_modules/agent-base": {
6124
  "version": "7.1.1",
6125
  "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
@@ -6212,6 +6291,18 @@
6212
  "node": ">= 8"
6213
  }
6214
  },
 
 
 
 
 
 
 
 
 
 
 
 
6215
  "node_modules/aproba": {
6216
  "version": "2.0.0",
6217
  "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
@@ -6292,6 +6383,12 @@
6292
  "node": ">=12"
6293
  }
6294
  },
 
 
 
 
 
 
6295
  "node_modules/async-mutex": {
6296
  "version": "0.5.0",
6297
  "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
@@ -6463,6 +6560,24 @@
6463
  }
6464
  ]
6465
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6466
  "node_modules/before-after-hook": {
6467
  "version": "3.0.2",
6468
  "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz",
@@ -7034,6 +7149,15 @@
7034
  "node": ">=6"
7035
  }
7036
  },
 
 
 
 
 
 
 
 
 
7037
  "node_modules/cmake-js": {
7038
  "version": "7.3.0",
7039
  "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.3.0.tgz",
@@ -7308,6 +7432,15 @@
7308
  "node": ">= 0.10"
7309
  }
7310
  },
 
 
 
 
 
 
 
 
 
7311
  "node_modules/create-require": {
7312
  "version": "1.1.1",
7313
  "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
@@ -8728,7 +8861,6 @@
8728
  "version": "1.15.9",
8729
  "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
8730
  "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
8731
- "dev": true,
8732
  "funding": [
8733
  {
8734
  "type": "individual",
@@ -9008,6 +9140,15 @@
9008
  "node": ">=14"
9009
  }
9010
  },
 
 
 
 
 
 
 
 
 
9011
  "node_modules/get-caller-file": {
9012
  "version": "2.0.5",
9013
  "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -9288,6 +9429,15 @@
9288
  "node": ">= 0.4"
9289
  }
9290
  },
 
 
 
 
 
 
 
 
 
9291
  "node_modules/headers-polyfill": {
9292
  "version": "4.0.3",
9293
  "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz",
@@ -9361,6 +9511,20 @@
9361
  "node": ">= 0.8"
9362
  }
9363
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9364
  "node_modules/http-proxy-agent": {
9365
  "version": "5.0.0",
9366
  "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
@@ -9385,6 +9549,39 @@
9385
  "node": ">= 6.0.0"
9386
  }
9387
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9388
  "node_modules/https-proxy-agent": {
9389
  "version": "7.0.5",
9390
  "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
@@ -10315,6 +10512,15 @@
10315
  "safe-buffer": "^5.0.1"
10316
  }
10317
  },
 
 
 
 
 
 
 
 
 
10318
  "node_modules/katex": {
10319
  "version": "0.16.21",
10320
  "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz",
@@ -10860,6 +11066,15 @@
10860
  "node": ">= 0.6"
10861
  }
10862
  },
 
 
 
 
 
 
 
 
 
10863
  "node_modules/memory-pager": {
10864
  "version": "1.5.0",
10865
  "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
@@ -11350,6 +11565,223 @@
11350
  "node": ">=16"
11351
  }
11352
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11353
  "node_modules/mri": {
11354
  "version": "1.2.0",
11355
  "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@@ -11470,12 +11902,51 @@
11470
  "node": "^18 || >=20"
11471
  }
11472
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11473
  "node_modules/natural-compare": {
11474
  "version": "1.4.0",
11475
  "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
11476
  "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
11477
  "dev": true
11478
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
11479
  "node_modules/negotiator": {
11480
  "version": "0.6.3",
11481
  "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -12041,6 +12512,15 @@
12041
  "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
12042
  "optional": true
12043
  },
 
 
 
 
 
 
 
 
 
12044
  "node_modules/openid-client": {
12045
  "version": "5.6.5",
12046
  "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz",
@@ -12441,6 +12921,95 @@
12441
  "dev": true,
12442
  "license": "MIT"
12443
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12444
  "node_modules/picocolors": {
12445
  "version": "1.1.1",
12446
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -12669,6 +13238,19 @@
12669
  "node": ">=18"
12670
  }
12671
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
12672
  "node_modules/postcss": {
12673
  "version": "8.5.3",
12674
  "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
@@ -12893,6 +13475,45 @@
12893
  "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
12894
  }
12895
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12896
  "node_modules/prelude-ls": {
12897
  "version": "1.2.1",
12898
  "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -13199,7 +13820,6 @@
13199
  "version": "6.11.2",
13200
  "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
13201
  "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
13202
- "optional": true,
13203
  "dependencies": {
13204
  "side-channel": "^1.0.4"
13205
  },
@@ -13351,6 +13971,23 @@
13351
  "node": ">= 12.13.0"
13352
  }
13353
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13354
  "node_modules/regenerator-runtime": {
13355
  "version": "0.14.1",
13356
  "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
@@ -13730,6 +14367,12 @@
13730
  "sanitize-html": "^2.3.2"
13731
  }
13732
  },
 
 
 
 
 
 
13733
  "node_modules/secure-json-parse": {
13734
  "version": "2.7.0",
13735
  "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
@@ -13968,6 +14611,12 @@
13968
  "url": "https://github.com/sponsors/ljharb"
13969
  }
13970
  },
 
 
 
 
 
 
13971
  "node_modules/siginfo": {
13972
  "version": "2.0.0",
13973
  "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
@@ -14278,6 +14927,15 @@
14278
  "url": "https://github.com/sponsors/typicode"
14279
  }
14280
  },
 
 
 
 
 
 
 
 
 
14281
  "node_modules/streamx": {
14282
  "version": "2.20.1",
14283
  "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz",
@@ -14663,6 +15321,14 @@
14663
  "@types/estree": "^1.0.6"
14664
  }
14665
  },
 
 
 
 
 
 
 
 
14666
  "node_modules/symbol-tree": {
14667
  "version": "3.2.4",
14668
  "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
@@ -15270,6 +15936,17 @@
15270
  "tiny-inflate": "^1.0.0"
15271
  }
15272
  },
 
 
 
 
 
 
 
 
 
 
 
15273
  "node_modules/universal-github-app-jwt": {
15274
  "version": "2.2.2",
15275
  "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz",
@@ -15404,8 +16081,7 @@
15404
  "node_modules/url-join": {
15405
  "version": "4.0.1",
15406
  "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
15407
- "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
15408
- "devOptional": true
15409
  },
15410
  "node_modules/url-parse": {
15411
  "version": "1.5.10",
@@ -15912,6 +16588,15 @@
15912
  "node": ">=0.10.0"
15913
  }
15914
  },
 
 
 
 
 
 
 
 
 
15915
  "node_modules/wordwrap": {
15916
  "version": "1.0.0",
15917
  "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
@@ -16051,6 +16736,15 @@
16051
  "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
16052
  "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
16053
  },
 
 
 
 
 
 
 
 
 
16054
  "node_modules/y18n": {
16055
  "version": "5.0.8",
16056
  "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
 
41
  "marked": "^12.0.1",
42
  "mongodb": "^5.8.0",
43
  "nanoid": "^5.0.9",
44
+ "natural": "^8.1.0",
45
  "openid-client": "^5.4.2",
46
  "parquetjs": "^0.11.2",
47
  "pino": "^9.0.0",
 
3159
  "version": "1.1.9",
3160
  "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz",
3161
  "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==",
 
3162
  "dependencies": {
3163
  "sparse-bitfield": "^3.0.3"
3164
  }
 
3891
  "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
3892
  "license": "BSD-3-Clause"
3893
  },
3894
+ "node_modules/@redis/bloom": {
3895
+ "version": "1.2.0",
3896
+ "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
3897
+ "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
3898
+ "license": "MIT",
3899
+ "peerDependencies": {
3900
+ "@redis/client": "^1.0.0"
3901
+ }
3902
+ },
3903
+ "node_modules/@redis/client": {
3904
+ "version": "1.6.1",
3905
+ "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
3906
+ "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
3907
+ "license": "MIT",
3908
+ "dependencies": {
3909
+ "cluster-key-slot": "1.1.2",
3910
+ "generic-pool": "3.9.0",
3911
+ "yallist": "4.0.0"
3912
+ },
3913
+ "engines": {
3914
+ "node": ">=14"
3915
+ }
3916
+ },
3917
+ "node_modules/@redis/graph": {
3918
+ "version": "1.1.1",
3919
+ "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
3920
+ "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
3921
+ "license": "MIT",
3922
+ "peerDependencies": {
3923
+ "@redis/client": "^1.0.0"
3924
+ }
3925
+ },
3926
+ "node_modules/@redis/json": {
3927
+ "version": "1.0.7",
3928
+ "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
3929
+ "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==",
3930
+ "license": "MIT",
3931
+ "peerDependencies": {
3932
+ "@redis/client": "^1.0.0"
3933
+ }
3934
+ },
3935
+ "node_modules/@redis/search": {
3936
+ "version": "1.2.0",
3937
+ "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz",
3938
+ "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==",
3939
+ "license": "MIT",
3940
+ "peerDependencies": {
3941
+ "@redis/client": "^1.0.0"
3942
+ }
3943
+ },
3944
+ "node_modules/@redis/time-series": {
3945
+ "version": "1.1.0",
3946
+ "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz",
3947
+ "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==",
3948
+ "license": "MIT",
3949
+ "peerDependencies": {
3950
+ "@redis/client": "^1.0.0"
3951
+ }
3952
+ },
3953
  "node_modules/@reflink/reflink": {
3954
  "resolved": "node_modules/ipull/stub/@reflink/reflink",
3955
  "link": true
 
6179
  "node": ">=0.4.0"
6180
  }
6181
  },
6182
+ "node_modules/afinn-165": {
6183
+ "version": "1.0.4",
6184
+ "resolved": "https://registry.npmjs.org/afinn-165/-/afinn-165-1.0.4.tgz",
6185
+ "integrity": "sha512-7+Wlx3BImrK0HiG6y3lU4xX7SpBPSSu8T9iguPMlaueRFxjbYwAQrp9lqZUuFikqKbd/en8lVREILvP2J80uJA==",
6186
+ "license": "MIT",
6187
+ "funding": {
6188
+ "type": "github",
6189
+ "url": "https://github.com/sponsors/wooorm"
6190
+ }
6191
+ },
6192
+ "node_modules/afinn-165-financialmarketnews": {
6193
+ "version": "3.0.0",
6194
+ "resolved": "https://registry.npmjs.org/afinn-165-financialmarketnews/-/afinn-165-financialmarketnews-3.0.0.tgz",
6195
+ "integrity": "sha512-0g9A1S3ZomFIGDTzZ0t6xmv4AuokBvBmpes8htiyHpH7N4xDmvSQL6UxL/Zcs2ypRb3VwgCscaD8Q3zEawKYhw==",
6196
+ "license": "MIT",
6197
+ "funding": {
6198
+ "type": "github",
6199
+ "url": "https://github.com/sponsors/wooorm"
6200
+ }
6201
+ },
6202
  "node_modules/agent-base": {
6203
  "version": "7.1.1",
6204
  "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
 
6291
  "node": ">= 8"
6292
  }
6293
  },
6294
+ "node_modules/apparatus": {
6295
+ "version": "0.0.10",
6296
+ "resolved": "https://registry.npmjs.org/apparatus/-/apparatus-0.0.10.tgz",
6297
+ "integrity": "sha512-KLy/ugo33KZA7nugtQ7O0E1c8kQ52N3IvD/XgIh4w/Nr28ypfkwDfA67F1ev4N1m5D+BOk1+b2dEJDfpj/VvZg==",
6298
+ "license": "MIT",
6299
+ "dependencies": {
6300
+ "sylvester": ">= 0.0.8"
6301
+ },
6302
+ "engines": {
6303
+ "node": ">=0.2.6"
6304
+ }
6305
+ },
6306
  "node_modules/aproba": {
6307
  "version": "2.0.0",
6308
  "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
 
6383
  "node": ">=12"
6384
  }
6385
  },
6386
+ "node_modules/async": {
6387
+ "version": "3.2.6",
6388
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
6389
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
6390
+ "license": "MIT"
6391
+ },
6392
  "node_modules/async-mutex": {
6393
  "version": "0.5.0",
6394
  "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
 
6560
  }
6561
  ]
6562
  },
6563
+ "node_modules/basic-auth": {
6564
+ "version": "2.0.1",
6565
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
6566
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
6567
+ "license": "MIT",
6568
+ "dependencies": {
6569
+ "safe-buffer": "5.1.2"
6570
+ },
6571
+ "engines": {
6572
+ "node": ">= 0.8"
6573
+ }
6574
+ },
6575
+ "node_modules/basic-auth/node_modules/safe-buffer": {
6576
+ "version": "5.1.2",
6577
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
6578
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
6579
+ "license": "MIT"
6580
+ },
6581
  "node_modules/before-after-hook": {
6582
  "version": "3.0.2",
6583
  "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz",
 
7149
  "node": ">=6"
7150
  }
7151
  },
7152
+ "node_modules/cluster-key-slot": {
7153
+ "version": "1.1.2",
7154
+ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
7155
+ "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
7156
+ "license": "Apache-2.0",
7157
+ "engines": {
7158
+ "node": ">=0.10.0"
7159
+ }
7160
+ },
7161
  "node_modules/cmake-js": {
7162
  "version": "7.3.0",
7163
  "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.3.0.tgz",
 
7432
  "node": ">= 0.10"
7433
  }
7434
  },
7435
+ "node_modules/corser": {
7436
+ "version": "2.0.1",
7437
+ "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
7438
+ "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==",
7439
+ "license": "MIT",
7440
+ "engines": {
7441
+ "node": ">= 0.4.0"
7442
+ }
7443
+ },
7444
  "node_modules/create-require": {
7445
  "version": "1.1.1",
7446
  "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
 
8861
  "version": "1.15.9",
8862
  "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
8863
  "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
 
8864
  "funding": [
8865
  {
8866
  "type": "individual",
 
9140
  "node": ">=14"
9141
  }
9142
  },
9143
+ "node_modules/generic-pool": {
9144
+ "version": "3.9.0",
9145
+ "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
9146
+ "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
9147
+ "license": "MIT",
9148
+ "engines": {
9149
+ "node": ">= 4"
9150
+ }
9151
+ },
9152
  "node_modules/get-caller-file": {
9153
  "version": "2.0.5",
9154
  "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
 
9429
  "node": ">= 0.4"
9430
  }
9431
  },
9432
+ "node_modules/he": {
9433
+ "version": "1.2.0",
9434
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
9435
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
9436
+ "license": "MIT",
9437
+ "bin": {
9438
+ "he": "bin/he"
9439
+ }
9440
+ },
9441
  "node_modules/headers-polyfill": {
9442
  "version": "4.0.3",
9443
  "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz",
 
9511
  "node": ">= 0.8"
9512
  }
9513
  },
9514
+ "node_modules/http-proxy": {
9515
+ "version": "1.18.1",
9516
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
9517
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
9518
+ "license": "MIT",
9519
+ "dependencies": {
9520
+ "eventemitter3": "^4.0.0",
9521
+ "follow-redirects": "^1.0.0",
9522
+ "requires-port": "^1.0.0"
9523
+ },
9524
+ "engines": {
9525
+ "node": ">=8.0.0"
9526
+ }
9527
+ },
9528
  "node_modules/http-proxy-agent": {
9529
  "version": "5.0.0",
9530
  "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
 
9549
  "node": ">= 6.0.0"
9550
  }
9551
  },
9552
+ "node_modules/http-proxy/node_modules/eventemitter3": {
9553
+ "version": "4.0.7",
9554
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
9555
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
9556
+ "license": "MIT"
9557
+ },
9558
+ "node_modules/http-server": {
9559
+ "version": "14.1.1",
9560
+ "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz",
9561
+ "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==",
9562
+ "license": "MIT",
9563
+ "dependencies": {
9564
+ "basic-auth": "^2.0.1",
9565
+ "chalk": "^4.1.2",
9566
+ "corser": "^2.0.1",
9567
+ "he": "^1.2.0",
9568
+ "html-encoding-sniffer": "^3.0.0",
9569
+ "http-proxy": "^1.18.1",
9570
+ "mime": "^1.6.0",
9571
+ "minimist": "^1.2.6",
9572
+ "opener": "^1.5.1",
9573
+ "portfinder": "^1.0.28",
9574
+ "secure-compare": "3.0.1",
9575
+ "union": "~0.5.0",
9576
+ "url-join": "^4.0.1"
9577
+ },
9578
+ "bin": {
9579
+ "http-server": "bin/http-server"
9580
+ },
9581
+ "engines": {
9582
+ "node": ">=12"
9583
+ }
9584
+ },
9585
  "node_modules/https-proxy-agent": {
9586
  "version": "7.0.5",
9587
  "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
 
10512
  "safe-buffer": "^5.0.1"
10513
  }
10514
  },
10515
+ "node_modules/kareem": {
10516
+ "version": "2.6.3",
10517
+ "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
10518
+ "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==",
10519
+ "license": "Apache-2.0",
10520
+ "engines": {
10521
+ "node": ">=12.0.0"
10522
+ }
10523
+ },
10524
  "node_modules/katex": {
10525
  "version": "0.16.21",
10526
  "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz",
 
11066
  "node": ">= 0.6"
11067
  }
11068
  },
11069
+ "node_modules/memjs": {
11070
+ "version": "1.3.2",
11071
+ "resolved": "https://registry.npmjs.org/memjs/-/memjs-1.3.2.tgz",
11072
+ "integrity": "sha512-qUEg2g8vxPe+zPn09KidjIStHPtoBO8Cttm8bgJFWWabbsjQ9Av9Ky+6UcvKx6ue0LLb/LEhtcyQpRyKfzeXcg==",
11073
+ "license": "MIT",
11074
+ "engines": {
11075
+ "node": ">=0.10.0"
11076
+ }
11077
+ },
11078
  "node_modules/memory-pager": {
11079
  "version": "1.5.0",
11080
  "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
 
11565
  "node": ">=16"
11566
  }
11567
  },
11568
+ "node_modules/mongoose": {
11569
+ "version": "8.15.1",
11570
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.15.1.tgz",
11571
+ "integrity": "sha512-RhQ4DzmBi5BNGcS0w4u1vdMRIKcteXTCNzDt1j7XRcdWYBz1MjMjulBhPaeC5jBCHOD1yinuOFTTSOWLLGexWw==",
11572
+ "license": "MIT",
11573
+ "dependencies": {
11574
+ "bson": "^6.10.3",
11575
+ "kareem": "2.6.3",
11576
+ "mongodb": "~6.16.0",
11577
+ "mpath": "0.9.0",
11578
+ "mquery": "5.0.0",
11579
+ "ms": "2.1.3",
11580
+ "sift": "17.1.3"
11581
+ },
11582
+ "engines": {
11583
+ "node": ">=16.20.1"
11584
+ },
11585
+ "funding": {
11586
+ "type": "opencollective",
11587
+ "url": "https://opencollective.com/mongoose"
11588
+ }
11589
+ },
11590
+ "node_modules/mongoose/node_modules/@types/whatwg-url": {
11591
+ "version": "11.0.5",
11592
+ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
11593
+ "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
11594
+ "license": "MIT",
11595
+ "dependencies": {
11596
+ "@types/webidl-conversions": "*"
11597
+ }
11598
+ },
11599
+ "node_modules/mongoose/node_modules/agent-base": {
11600
+ "version": "6.0.2",
11601
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
11602
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
11603
+ "license": "MIT",
11604
+ "optional": true,
11605
+ "peer": true,
11606
+ "dependencies": {
11607
+ "debug": "4"
11608
+ },
11609
+ "engines": {
11610
+ "node": ">= 6.0.0"
11611
+ }
11612
+ },
11613
+ "node_modules/mongoose/node_modules/bson": {
11614
+ "version": "6.10.3",
11615
+ "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.3.tgz",
11616
+ "integrity": "sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==",
11617
+ "license": "Apache-2.0",
11618
+ "engines": {
11619
+ "node": ">=16.20.1"
11620
+ }
11621
+ },
11622
+ "node_modules/mongoose/node_modules/gaxios": {
11623
+ "version": "5.1.3",
11624
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz",
11625
+ "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==",
11626
+ "license": "Apache-2.0",
11627
+ "optional": true,
11628
+ "peer": true,
11629
+ "dependencies": {
11630
+ "extend": "^3.0.2",
11631
+ "https-proxy-agent": "^5.0.0",
11632
+ "is-stream": "^2.0.0",
11633
+ "node-fetch": "^2.6.9"
11634
+ },
11635
+ "engines": {
11636
+ "node": ">=12"
11637
+ }
11638
+ },
11639
+ "node_modules/mongoose/node_modules/gcp-metadata": {
11640
+ "version": "5.3.0",
11641
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
11642
+ "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
11643
+ "license": "Apache-2.0",
11644
+ "optional": true,
11645
+ "peer": true,
11646
+ "dependencies": {
11647
+ "gaxios": "^5.0.0",
11648
+ "json-bigint": "^1.0.0"
11649
+ },
11650
+ "engines": {
11651
+ "node": ">=12"
11652
+ }
11653
+ },
11654
+ "node_modules/mongoose/node_modules/https-proxy-agent": {
11655
+ "version": "5.0.1",
11656
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
11657
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
11658
+ "license": "MIT",
11659
+ "optional": true,
11660
+ "peer": true,
11661
+ "dependencies": {
11662
+ "agent-base": "6",
11663
+ "debug": "4"
11664
+ },
11665
+ "engines": {
11666
+ "node": ">= 6"
11667
+ }
11668
+ },
11669
+ "node_modules/mongoose/node_modules/is-stream": {
11670
+ "version": "2.0.1",
11671
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
11672
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
11673
+ "license": "MIT",
11674
+ "optional": true,
11675
+ "peer": true,
11676
+ "engines": {
11677
+ "node": ">=8"
11678
+ },
11679
+ "funding": {
11680
+ "url": "https://github.com/sponsors/sindresorhus"
11681
+ }
11682
+ },
11683
+ "node_modules/mongoose/node_modules/mongodb": {
11684
+ "version": "6.16.0",
11685
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.16.0.tgz",
11686
+ "integrity": "sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw==",
11687
+ "license": "Apache-2.0",
11688
+ "dependencies": {
11689
+ "@mongodb-js/saslprep": "^1.1.9",
11690
+ "bson": "^6.10.3",
11691
+ "mongodb-connection-string-url": "^3.0.0"
11692
+ },
11693
+ "engines": {
11694
+ "node": ">=16.20.1"
11695
+ },
11696
+ "peerDependencies": {
11697
+ "@aws-sdk/credential-providers": "^3.188.0",
11698
+ "@mongodb-js/zstd": "^1.1.0 || ^2.0.0",
11699
+ "gcp-metadata": "^5.2.0",
11700
+ "kerberos": "^2.0.1",
11701
+ "mongodb-client-encryption": ">=6.0.0 <7",
11702
+ "snappy": "^7.2.2",
11703
+ "socks": "^2.7.1"
11704
+ },
11705
+ "peerDependenciesMeta": {
11706
+ "@aws-sdk/credential-providers": {
11707
+ "optional": true
11708
+ },
11709
+ "@mongodb-js/zstd": {
11710
+ "optional": true
11711
+ },
11712
+ "gcp-metadata": {
11713
+ "optional": true
11714
+ },
11715
+ "kerberos": {
11716
+ "optional": true
11717
+ },
11718
+ "mongodb-client-encryption": {
11719
+ "optional": true
11720
+ },
11721
+ "snappy": {
11722
+ "optional": true
11723
+ },
11724
+ "socks": {
11725
+ "optional": true
11726
+ }
11727
+ }
11728
+ },
11729
+ "node_modules/mongoose/node_modules/mongodb-connection-string-url": {
11730
+ "version": "3.0.2",
11731
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
11732
+ "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
11733
+ "license": "Apache-2.0",
11734
+ "dependencies": {
11735
+ "@types/whatwg-url": "^11.0.2",
11736
+ "whatwg-url": "^14.1.0 || ^13.0.0"
11737
+ }
11738
+ },
11739
+ "node_modules/mongoose/node_modules/tr46": {
11740
+ "version": "5.1.1",
11741
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
11742
+ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
11743
+ "license": "MIT",
11744
+ "dependencies": {
11745
+ "punycode": "^2.3.1"
11746
+ },
11747
+ "engines": {
11748
+ "node": ">=18"
11749
+ }
11750
+ },
11751
+ "node_modules/mongoose/node_modules/whatwg-url": {
11752
+ "version": "14.2.0",
11753
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
11754
+ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
11755
+ "license": "MIT",
11756
+ "dependencies": {
11757
+ "tr46": "^5.1.0",
11758
+ "webidl-conversions": "^7.0.0"
11759
+ },
11760
+ "engines": {
11761
+ "node": ">=18"
11762
+ }
11763
+ },
11764
+ "node_modules/mpath": {
11765
+ "version": "0.9.0",
11766
+ "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
11767
+ "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
11768
+ "license": "MIT",
11769
+ "engines": {
11770
+ "node": ">=4.0.0"
11771
+ }
11772
+ },
11773
+ "node_modules/mquery": {
11774
+ "version": "5.0.0",
11775
+ "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
11776
+ "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
11777
+ "license": "MIT",
11778
+ "dependencies": {
11779
+ "debug": "4.x"
11780
+ },
11781
+ "engines": {
11782
+ "node": ">=14.0.0"
11783
+ }
11784
+ },
11785
  "node_modules/mri": {
11786
  "version": "1.2.0",
11787
  "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
 
11902
  "node": "^18 || >=20"
11903
  }
11904
  },
11905
+ "node_modules/natural": {
11906
+ "version": "8.1.0",
11907
+ "resolved": "https://registry.npmjs.org/natural/-/natural-8.1.0.tgz",
11908
+ "integrity": "sha512-qHKU+BzPXzEDwToFBzlI+3oI2jeN3xRNP421ifoF2Fw7ej+5zEO3Z5wUKPjz00jhz9/ESerIUGfhPqqkOqlWPA==",
11909
+ "license": "MIT",
11910
+ "dependencies": {
11911
+ "afinn-165": "^1.0.2",
11912
+ "afinn-165-financialmarketnews": "^3.0.0",
11913
+ "apparatus": "^0.0.10",
11914
+ "dotenv": "^16.4.5",
11915
+ "http-server": "^14.1.1",
11916
+ "memjs": "^1.3.2",
11917
+ "mongoose": "^8.2.0",
11918
+ "pg": "^8.11.3",
11919
+ "redis": "^4.6.13",
11920
+ "safe-stable-stringify": "^2.2.0",
11921
+ "stopwords-iso": "^1.1.0",
11922
+ "sylvester": "^0.0.12",
11923
+ "underscore": "^1.9.1",
11924
+ "uuid": "^9.0.1",
11925
+ "wordnet-db": "^3.1.11"
11926
+ },
11927
+ "engines": {
11928
+ "node": ">=0.4.10"
11929
+ }
11930
+ },
11931
  "node_modules/natural-compare": {
11932
  "version": "1.4.0",
11933
  "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
11934
  "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
11935
  "dev": true
11936
  },
11937
+ "node_modules/natural/node_modules/uuid": {
11938
+ "version": "9.0.1",
11939
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
11940
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
11941
+ "funding": [
11942
+ "https://github.com/sponsors/broofa",
11943
+ "https://github.com/sponsors/ctavan"
11944
+ ],
11945
+ "license": "MIT",
11946
+ "bin": {
11947
+ "uuid": "dist/bin/uuid"
11948
+ }
11949
+ },
11950
  "node_modules/negotiator": {
11951
  "version": "0.6.3",
11952
  "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
 
12512
  "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
12513
  "optional": true
12514
  },
12515
+ "node_modules/opener": {
12516
+ "version": "1.5.2",
12517
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
12518
+ "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
12519
+ "license": "(WTFPL OR MIT)",
12520
+ "bin": {
12521
+ "opener": "bin/opener-bin.js"
12522
+ }
12523
+ },
12524
  "node_modules/openid-client": {
12525
  "version": "5.6.5",
12526
  "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz",
 
12921
  "dev": true,
12922
  "license": "MIT"
12923
  },
12924
+ "node_modules/pg": {
12925
+ "version": "8.16.0",
12926
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.0.tgz",
12927
+ "integrity": "sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==",
12928
+ "license": "MIT",
12929
+ "dependencies": {
12930
+ "pg-connection-string": "^2.9.0",
12931
+ "pg-pool": "^3.10.0",
12932
+ "pg-protocol": "^1.10.0",
12933
+ "pg-types": "2.2.0",
12934
+ "pgpass": "1.0.5"
12935
+ },
12936
+ "engines": {
12937
+ "node": ">= 8.0.0"
12938
+ },
12939
+ "optionalDependencies": {
12940
+ "pg-cloudflare": "^1.2.5"
12941
+ },
12942
+ "peerDependencies": {
12943
+ "pg-native": ">=3.0.1"
12944
+ },
12945
+ "peerDependenciesMeta": {
12946
+ "pg-native": {
12947
+ "optional": true
12948
+ }
12949
+ }
12950
+ },
12951
+ "node_modules/pg-cloudflare": {
12952
+ "version": "1.2.5",
12953
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz",
12954
+ "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==",
12955
+ "license": "MIT",
12956
+ "optional": true
12957
+ },
12958
+ "node_modules/pg-connection-string": {
12959
+ "version": "2.9.0",
12960
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz",
12961
+ "integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==",
12962
+ "license": "MIT"
12963
+ },
12964
+ "node_modules/pg-int8": {
12965
+ "version": "1.0.1",
12966
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
12967
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
12968
+ "license": "ISC",
12969
+ "engines": {
12970
+ "node": ">=4.0.0"
12971
+ }
12972
+ },
12973
+ "node_modules/pg-pool": {
12974
+ "version": "3.10.0",
12975
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.0.tgz",
12976
+ "integrity": "sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==",
12977
+ "license": "MIT",
12978
+ "peerDependencies": {
12979
+ "pg": ">=8.0"
12980
+ }
12981
+ },
12982
+ "node_modules/pg-protocol": {
12983
+ "version": "1.10.0",
12984
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.0.tgz",
12985
+ "integrity": "sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==",
12986
+ "license": "MIT"
12987
+ },
12988
+ "node_modules/pg-types": {
12989
+ "version": "2.2.0",
12990
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
12991
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
12992
+ "license": "MIT",
12993
+ "dependencies": {
12994
+ "pg-int8": "1.0.1",
12995
+ "postgres-array": "~2.0.0",
12996
+ "postgres-bytea": "~1.0.0",
12997
+ "postgres-date": "~1.0.4",
12998
+ "postgres-interval": "^1.1.0"
12999
+ },
13000
+ "engines": {
13001
+ "node": ">=4"
13002
+ }
13003
+ },
13004
+ "node_modules/pgpass": {
13005
+ "version": "1.0.5",
13006
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
13007
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
13008
+ "license": "MIT",
13009
+ "dependencies": {
13010
+ "split2": "^4.1.0"
13011
+ }
13012
+ },
13013
  "node_modules/picocolors": {
13014
  "version": "1.1.1",
13015
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
 
13238
  "node": ">=18"
13239
  }
13240
  },
13241
+ "node_modules/portfinder": {
13242
+ "version": "1.0.37",
13243
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.37.tgz",
13244
+ "integrity": "sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==",
13245
+ "license": "MIT",
13246
+ "dependencies": {
13247
+ "async": "^3.2.6",
13248
+ "debug": "^4.3.6"
13249
+ },
13250
+ "engines": {
13251
+ "node": ">= 10.12"
13252
+ }
13253
+ },
13254
  "node_modules/postcss": {
13255
  "version": "8.5.3",
13256
  "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
 
13475
  "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
13476
  }
13477
  },
13478
+ "node_modules/postgres-array": {
13479
+ "version": "2.0.0",
13480
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
13481
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
13482
+ "license": "MIT",
13483
+ "engines": {
13484
+ "node": ">=4"
13485
+ }
13486
+ },
13487
+ "node_modules/postgres-bytea": {
13488
+ "version": "1.0.0",
13489
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
13490
+ "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
13491
+ "license": "MIT",
13492
+ "engines": {
13493
+ "node": ">=0.10.0"
13494
+ }
13495
+ },
13496
+ "node_modules/postgres-date": {
13497
+ "version": "1.0.7",
13498
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
13499
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
13500
+ "license": "MIT",
13501
+ "engines": {
13502
+ "node": ">=0.10.0"
13503
+ }
13504
+ },
13505
+ "node_modules/postgres-interval": {
13506
+ "version": "1.2.0",
13507
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
13508
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
13509
+ "license": "MIT",
13510
+ "dependencies": {
13511
+ "xtend": "^4.0.0"
13512
+ },
13513
+ "engines": {
13514
+ "node": ">=0.10.0"
13515
+ }
13516
+ },
13517
  "node_modules/prelude-ls": {
13518
  "version": "1.2.1",
13519
  "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
 
13820
  "version": "6.11.2",
13821
  "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
13822
  "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
 
13823
  "dependencies": {
13824
  "side-channel": "^1.0.4"
13825
  },
 
13971
  "node": ">= 12.13.0"
13972
  }
13973
  },
13974
+ "node_modules/redis": {
13975
+ "version": "4.7.1",
13976
+ "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz",
13977
+ "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==",
13978
+ "license": "MIT",
13979
+ "workspaces": [
13980
+ "./packages/*"
13981
+ ],
13982
+ "dependencies": {
13983
+ "@redis/bloom": "1.2.0",
13984
+ "@redis/client": "1.6.1",
13985
+ "@redis/graph": "1.1.1",
13986
+ "@redis/json": "1.0.7",
13987
+ "@redis/search": "1.2.0",
13988
+ "@redis/time-series": "1.1.0"
13989
+ }
13990
+ },
13991
  "node_modules/regenerator-runtime": {
13992
  "version": "0.14.1",
13993
  "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
 
14367
  "sanitize-html": "^2.3.2"
14368
  }
14369
  },
14370
+ "node_modules/secure-compare": {
14371
+ "version": "3.0.1",
14372
+ "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz",
14373
+ "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==",
14374
+ "license": "MIT"
14375
+ },
14376
  "node_modules/secure-json-parse": {
14377
  "version": "2.7.0",
14378
  "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
 
14611
  "url": "https://github.com/sponsors/ljharb"
14612
  }
14613
  },
14614
+ "node_modules/sift": {
14615
+ "version": "17.1.3",
14616
+ "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz",
14617
+ "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==",
14618
+ "license": "MIT"
14619
+ },
14620
  "node_modules/siginfo": {
14621
  "version": "2.0.0",
14622
  "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
 
14927
  "url": "https://github.com/sponsors/typicode"
14928
  }
14929
  },
14930
+ "node_modules/stopwords-iso": {
14931
+ "version": "1.1.0",
14932
+ "resolved": "https://registry.npmjs.org/stopwords-iso/-/stopwords-iso-1.1.0.tgz",
14933
+ "integrity": "sha512-I6GPS/E0zyieHehMRPQcqkiBMJKGgLta+1hREixhoLPqEA0AlVFiC43dl8uPpmkkeRdDMzYRWFWk5/l9x7nmNg==",
14934
+ "license": "MIT",
14935
+ "engines": {
14936
+ "node": ">=0.10.0"
14937
+ }
14938
+ },
14939
  "node_modules/streamx": {
14940
  "version": "2.20.1",
14941
  "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz",
 
15321
  "@types/estree": "^1.0.6"
15322
  }
15323
  },
15324
+ "node_modules/sylvester": {
15325
+ "version": "0.0.12",
15326
+ "resolved": "https://registry.npmjs.org/sylvester/-/sylvester-0.0.12.tgz",
15327
+ "integrity": "sha512-SzRP5LQ6Ts2G5NyAa/jg16s8e3R7rfdFjizy1zeoecYWw+nGL+YA1xZvW/+iJmidBGSdLkuvdwTYEyJEb+EiUw==",
15328
+ "engines": {
15329
+ "node": ">=0.2.6"
15330
+ }
15331
+ },
15332
  "node_modules/symbol-tree": {
15333
  "version": "3.2.4",
15334
  "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
 
15936
  "tiny-inflate": "^1.0.0"
15937
  }
15938
  },
15939
+ "node_modules/union": {
15940
+ "version": "0.5.0",
15941
+ "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz",
15942
+ "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==",
15943
+ "dependencies": {
15944
+ "qs": "^6.4.0"
15945
+ },
15946
+ "engines": {
15947
+ "node": ">= 0.8.0"
15948
+ }
15949
+ },
15950
  "node_modules/universal-github-app-jwt": {
15951
  "version": "2.2.2",
15952
  "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz",
 
16081
  "node_modules/url-join": {
16082
  "version": "4.0.1",
16083
  "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
16084
+ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="
 
16085
  },
16086
  "node_modules/url-parse": {
16087
  "version": "1.5.10",
 
16588
  "node": ">=0.10.0"
16589
  }
16590
  },
16591
+ "node_modules/wordnet-db": {
16592
+ "version": "3.1.14",
16593
+ "resolved": "https://registry.npmjs.org/wordnet-db/-/wordnet-db-3.1.14.tgz",
16594
+ "integrity": "sha512-zVyFsvE+mq9MCmwXUWHIcpfbrHHClZWZiVOzKSxNJruIcFn2RbY55zkhiAMMxM8zCVSmtNiViq8FsAZSFpMYag==",
16595
+ "license": "MIT",
16596
+ "engines": {
16597
+ "node": ">=0.6.0"
16598
+ }
16599
+ },
16600
  "node_modules/wordwrap": {
16601
  "version": "1.0.0",
16602
  "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
 
16736
  "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
16737
  "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
16738
  },
16739
+ "node_modules/xtend": {
16740
+ "version": "4.0.2",
16741
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
16742
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
16743
+ "license": "MIT",
16744
+ "engines": {
16745
+ "node": ">=0.4"
16746
+ }
16747
+ },
16748
  "node_modules/y18n": {
16749
  "version": "5.0.8",
16750
  "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
package.json CHANGED
@@ -102,6 +102,7 @@
102
  "marked": "^12.0.1",
103
  "mongodb": "^5.8.0",
104
  "nanoid": "^5.0.9",
 
105
  "openid-client": "^5.4.2",
106
  "parquetjs": "^0.11.2",
107
  "pino": "^9.0.0",
 
102
  "marked": "^12.0.1",
103
  "mongodb": "^5.8.0",
104
  "nanoid": "^5.0.9",
105
+ "natural": "^8.1.0",
106
  "openid-client": "^5.4.2",
107
  "parquetjs": "^0.11.2",
108
  "pino": "^9.0.0",
src/lib/components/InfiniteScroll.svelte CHANGED
@@ -44,7 +44,7 @@
44
  </script>
45
 
46
  <div bind:this={loader} class="flex animate-pulse flex-col gap-4">
47
- <div class="ml-2 h-5 w-4/5 gap-5 rounded bg-gray-200 dark:bg-gray-700"></div>
48
- <div class="ml-2 h-5 w-4/5 gap-5 rounded bg-gray-200 dark:bg-gray-700"></div>
49
- <div class="ml-2 h-5 w-4/5 gap-5 rounded bg-gray-200 dark:bg-gray-700"></div>
50
  </div>
 
44
  </script>
45
 
46
  <div bind:this={loader} class="flex animate-pulse flex-col gap-4">
47
+ <div class="w-5/5 ml-2 h-5 gap-5 rounded bg-gray-300 dark:bg-gray-700"></div>
48
+ <div class="w-5/5 ml-2 h-5 gap-5 rounded bg-gray-300 dark:bg-gray-700"></div>
49
+ <div class="w-5/5 ml-2 h-5 gap-5 rounded bg-gray-300 dark:bg-gray-700"></div>
50
  </div>
src/lib/components/NavConversationItem.svelte CHANGED
@@ -12,9 +12,12 @@
12
  interface Props {
13
  conv: ConvSidebar;
14
  readOnly?: true;
 
 
 
15
  }
16
 
17
- let { conv, readOnly }: Props = $props();
18
 
19
  let confirmDelete = $state(false);
20
 
@@ -30,12 +33,12 @@
30
  confirmDelete = false;
31
  }}
32
  href="{base}/conversation/{conv.id}"
33
- class="group flex h-10 flex-none items-center gap-1.5 rounded-lg pl-2.5 pr-2 text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 sm:h-[2.35rem] {conv.id ===
34
- page.params.id
35
- ? 'bg-gray-100 dark:bg-gray-700'
36
- : ''}"
37
  >
38
- <div class="flex flex-1 items-center truncate">
39
  {#if confirmDelete}
40
  <span class="mr-1 font-semibold"> Delete </span>
41
  {/if}
@@ -54,6 +57,14 @@
54
  {:else}
55
  {conv.title}
56
  {/if}
 
 
 
 
 
 
 
 
57
  </div>
58
 
59
  {#if !readOnly}
 
12
  interface Props {
13
  conv: ConvSidebar;
14
  readOnly?: true;
15
+ showDescription?: boolean;
16
+ description?: string;
17
+ searchInput?: string;
18
  }
19
 
20
+ let { conv, readOnly, showDescription, description, searchInput }: Props = $props();
21
 
22
  let confirmDelete = $state(false);
23
 
 
33
  confirmDelete = false;
34
  }}
35
  href="{base}/conversation/{conv.id}"
36
+ class="group flex h-10 flex-none items-center gap-1.5 rounded-lg pl-2.5 pr-2 text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700
37
+ {conv.id === page.params.id ? 'bg-gray-100 dark:bg-gray-700' : ''}
38
+ {showDescription ? 'sm:h-[3.5rem]' : 'sm:h-[2.35rem]'}
39
+ "
40
  >
41
+ <div class="my-2 flex flex-1 flex-col items-start truncate">
42
  {#if confirmDelete}
43
  <span class="mr-1 font-semibold"> Delete </span>
44
  {/if}
 
57
  {:else}
58
  {conv.title}
59
  {/if}
60
+ {#if showDescription && description && searchInput}
61
+ <p class="ml-7 text-sm text-gray-500">
62
+ {#each description.split(searchInput) as segment, i}{segment}{#if i < description.split(searchInput).length - 1}<strong
63
+ >{searchInput}</strong
64
+ >{/if}
65
+ {/each}
66
+ </p>
67
+ {/if}
68
  </div>
69
 
70
  {#if !readOnly}
src/lib/components/NavMenu.svelte CHANGED
@@ -27,6 +27,7 @@
27
  import { toggleSearch } from "./chat/Search.svelte";
28
  import CarbonSearch from "~icons/carbon/search";
29
  import { closeMobileNav } from "./MobileNav.svelte";
 
30
 
31
  interface Props {
32
  conversations: ConvSidebar[];
@@ -118,15 +119,20 @@
118
  class="scrollbar-custom flex touch-pan-y flex-col gap-1 overflow-y-auto rounded-r-xl from-gray-50 px-3 pb-3 pt-2 text-[.9rem] dark:from-gray-800/30 max-sm:bg-gradient-to-t md:bg-gradient-to-l"
119
  >
120
  <button
121
- class="mx-auto flex w-full flex-row items-center justify-stretch gap-x-2 rounded-xl px-2 py-1 pl-0 align-middle text-gray-600 hover:bg-gray-500/20 dark:text-gray-400"
122
  onclick={() => {
123
  closeMobileNav();
124
  toggleSearch();
125
  }}
126
  >
127
  <CarbonSearch class="text-xs" />
128
- <span class="block">Search chats</span></button
129
- >
 
 
 
 
 
130
  {#await groupedConversations}
131
  {#if $page.data.nConversations > 0}
132
  <div class="overflow-y-hidden">
 
27
  import { toggleSearch } from "./chat/Search.svelte";
28
  import CarbonSearch from "~icons/carbon/search";
29
  import { closeMobileNav } from "./MobileNav.svelte";
30
+ import { isVirtualKeyboard } from "$lib/utils/isVirtualKeyboard";
31
 
32
  interface Props {
33
  conversations: ConvSidebar[];
 
119
  class="scrollbar-custom flex touch-pan-y flex-col gap-1 overflow-y-auto rounded-r-xl from-gray-50 px-3 pb-3 pt-2 text-[.9rem] dark:from-gray-800/30 max-sm:bg-gradient-to-t md:bg-gradient-to-l"
120
  >
121
  <button
122
+ class="group mx-auto flex w-full flex-row items-center justify-stretch gap-x-2 rounded-xl px-2 py-1 align-middle text-gray-600 hover:bg-gray-500/20 dark:text-gray-400"
123
  onclick={() => {
124
  closeMobileNav();
125
  toggleSearch();
126
  }}
127
  >
128
  <CarbonSearch class="text-xs" />
129
+ <span class="block">Search chats</span>
130
+ {#if !isVirtualKeyboard()}
131
+ <span class="invisible ml-auto text-xs text-gray-500 group-hover:visible"
132
+ ><kbd>ctrl</kbd>+<kbd>k</kbd></span
133
+ >
134
+ {/if}
135
+ </button>
136
  {#await groupedConversations}
137
  {#if $page.data.nConversations > 0}
138
  <div class="overflow-y-hidden">
src/lib/components/chat/Search.svelte CHANGED
@@ -20,16 +20,20 @@
20
 
21
  import CarbonClose from "~icons/carbon/close";
22
  import { fly } from "svelte/transition";
 
23
 
24
  let inputElement: HTMLInputElement | undefined = $state(undefined);
25
 
26
  let searchInput: string = $state("");
27
  let debouncedInput: string = $state("");
 
28
 
29
  let pending: boolean = $state(false);
30
 
31
  let conversations: GETSearchEndpointReturn = $state([]);
32
 
 
 
33
  const dateRanges = [
34
  new Date().setDate(new Date().getDate() - 1),
35
  new Date().setDate(new Date().getDate() - 7),
@@ -48,25 +52,42 @@
48
  });
49
 
50
  const update = debounce(async (v: string) => {
 
 
 
 
 
51
  debouncedInput = v;
52
  pending = true;
53
- await fetch(`${base}/api/conversations/search?q=${v}`)
 
 
 
 
 
 
 
 
54
  .then(async (r) => {
55
  if (r.ok) {
56
- conversations = await r.json().then((conversations) =>
57
  conversations.map((conv: GETSearchEndpointReturn[number]) => ({
58
  ...conv,
59
  updatedAt: new Date(conv.updatedAt),
60
  }))
61
  );
62
  } else {
63
- conversations = [];
64
  }
65
  })
66
- .finally(() => {
67
- pending = false;
68
- });
69
- }, 300);
 
 
 
 
70
 
71
  $effect(() => update(searchInput));
72
 
@@ -102,6 +123,7 @@
102
  $effect(() => {
103
  if (!searchOpen) {
104
  searchInput = "";
 
105
  }
106
  });
107
  </script>
@@ -135,7 +157,7 @@
135
  }}
136
  />
137
 
138
- <div class="max-h-[50dvh] overflow-y-scroll">
139
  {#if debouncedInput && debouncedInput.length >= 3}
140
  {#if pending}
141
  {#each Array(5) as _}
@@ -150,14 +172,23 @@
150
  {:else}
151
  {#each Object.entries(groupedConversations) as [group, convs]}
152
  {#if convs.length}
153
- <h4 class="mb-1.5 mt-4 pl-1.5 text-sm text-gray-400 dark:text-gray-500">
154
  {titles[group]}
155
  </h4>
156
  {#each convs as conv}
157
- <NavConversationItem {conv} readOnly={true} />
 
 
 
 
 
 
158
  {/each}
159
  {/if}
160
  {/each}
 
 
 
161
  {/if}
162
  {/if}
163
  </div>
 
20
 
21
  import CarbonClose from "~icons/carbon/close";
22
  import { fly } from "svelte/transition";
23
+ import InfiniteScroll from "../InfiniteScroll.svelte";
24
 
25
  let inputElement: HTMLInputElement | undefined = $state(undefined);
26
 
27
  let searchInput: string = $state("");
28
  let debouncedInput: string = $state("");
29
+ let hasMore = $state(true);
30
 
31
  let pending: boolean = $state(false);
32
 
33
  let conversations: GETSearchEndpointReturn = $state([]);
34
 
35
+ let page: number = $state(0);
36
+
37
  const dateRanges = [
38
  new Date().setDate(new Date().getDate() - 1),
39
  new Date().setDate(new Date().getDate() - 7),
 
52
  });
53
 
54
  const update = debounce(async (v: string) => {
55
+ if (debouncedInput !== v) {
56
+ conversations = [];
57
+ page = 0;
58
+ hasMore = true;
59
+ }
60
  debouncedInput = v;
61
  pending = true;
62
+ try {
63
+ await handleVisible(v);
64
+ } finally {
65
+ pending = false;
66
+ }
67
+ }, 300);
68
+
69
+ async function handleVisible(v: string) {
70
+ const newConvs = await fetch(`${base}/api/conversations/search?q=${v}&p=${page++}`)
71
  .then(async (r) => {
72
  if (r.ok) {
73
+ return await r.json().then((conversations) =>
74
  conversations.map((conv: GETSearchEndpointReturn[number]) => ({
75
  ...conv,
76
  updatedAt: new Date(conv.updatedAt),
77
  }))
78
  );
79
  } else {
80
+ return [];
81
  }
82
  })
83
+ .catch(() => []);
84
+
85
+ if (newConvs.length === 0) {
86
+ hasMore = false;
87
+ }
88
+
89
+ conversations = [...conversations, ...newConvs];
90
+ }
91
 
92
  $effect(() => update(searchInput));
93
 
 
123
  $effect(() => {
124
  if (!searchOpen) {
125
  searchInput = "";
126
+ debouncedInput = ""; // reset debouncedInput on search bar close
127
  }
128
  });
129
  </script>
 
157
  }}
158
  />
159
 
160
+ <div class="scrollbar-custom max-h-[40dvh] overflow-y-scroll">
161
  {#if debouncedInput && debouncedInput.length >= 3}
162
  {#if pending}
163
  {#each Array(5) as _}
 
172
  {:else}
173
  {#each Object.entries(groupedConversations) as [group, convs]}
174
  {#if convs.length}
175
+ <h4 class="mb-1.5 mt-4 pl-1.5 text-sm text-gray-700 dark:text-gray-300">
176
  {titles[group]}
177
  </h4>
178
  {#each convs as conv}
179
+ <NavConversationItem
180
+ {conv}
181
+ readOnly={true}
182
+ showDescription={true}
183
+ description={conv.content}
184
+ searchInput={conv.matchedText}
185
+ />
186
  {/each}
187
  {/if}
188
  {/each}
189
+ {#if hasMore}
190
+ <InfiniteScroll on:visible={() => handleVisible(searchInput)} />
191
+ {/if}
192
  {/if}
193
  {/if}
194
  </div>
src/routes/api/conversations/search/+server.ts CHANGED
@@ -1,12 +1,15 @@
1
- import { CONV_NUM_PER_PAGE } from "$lib/constants/pagination";
2
  import { authCondition } from "$lib/server/auth";
3
  import { collections } from "$lib/server/database";
4
  import { models } from "$lib/server/models";
5
  import type { RequestHandler } from "@sveltejs/kit";
 
 
6
 
7
  export type GETSearchEndpointReturn = Array<{
8
  id: string;
9
  title: string;
 
 
10
  updatedAt: Date;
11
  model: string;
12
  assistantId?: string;
@@ -28,7 +31,9 @@ export const GET: RequestHandler = async ({ locals, url }) => {
28
  ...authCondition(locals),
29
  $text: { $search: searchQuery },
30
  })
31
- .sort({ score: { $meta: "textScore" } })
 
 
32
  .project({
33
  title: 1,
34
  updatedAt: 1,
@@ -37,15 +42,191 @@ export const GET: RequestHandler = async ({ locals, url }) => {
37
  messages: 1,
38
  userId: 1,
39
  })
40
- .skip(p * CONV_NUM_PER_PAGE)
41
- .limit(CONV_NUM_PER_PAGE)
42
  .toArray()
43
  .then((convs) =>
44
  convs.map((conv) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  return {
46
  _id: conv._id,
47
- id: conv._id, // legacy param iOS
48
  title: conv.title,
 
 
49
  updatedAt: conv.updatedAt,
50
  model: conv.model,
51
  assistantId: conv.assistantId,
@@ -53,7 +234,6 @@ export const GET: RequestHandler = async ({ locals, url }) => {
53
  };
54
  })
55
  );
56
-
57
  return Response.json(convs as GETSearchEndpointReturn);
58
  }
59
  return Response.json([]);
 
 
1
  import { authCondition } from "$lib/server/auth";
2
  import { collections } from "$lib/server/database";
3
  import { models } from "$lib/server/models";
4
  import type { RequestHandler } from "@sveltejs/kit";
5
+ import pkg from "natural";
6
+ const { PorterStemmer } = pkg;
7
 
8
  export type GETSearchEndpointReturn = Array<{
9
  id: string;
10
  title: string;
11
+ content: string;
12
+ matchedText: string;
13
  updatedAt: Date;
14
  model: string;
15
  assistantId?: string;
 
31
  ...authCondition(locals),
32
  $text: { $search: searchQuery },
33
  })
34
+ .sort({
35
+ updatedAt: -1, // Sort by date updated in descending order
36
+ })
37
  .project({
38
  title: 1,
39
  updatedAt: 1,
 
42
  messages: 1,
43
  userId: 1,
44
  })
45
+ .skip(p * 5)
46
+ .limit(5)
47
  .toArray()
48
  .then((convs) =>
49
  convs.map((conv) => {
50
+ let matchedContent = "";
51
+ let matchedText = "";
52
+
53
+ // Find the best match using stemming to handle MongoDB's text search behavior
54
+ let bestMatch = null;
55
+ let bestMatchLength = 0;
56
+
57
+ // Simple function to find the best match in content
58
+ const findBestMatch = (
59
+ content: string,
60
+ query: string
61
+ ): { start: number; end: number; text: string } | null => {
62
+ const contentLower = content.toLowerCase();
63
+ const queryLower = query.toLowerCase();
64
+
65
+ // Try exact word boundary match first
66
+ const wordRegex = new RegExp(
67
+ `\\b${queryLower.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`,
68
+ "gi"
69
+ );
70
+ const wordMatch = wordRegex.exec(content);
71
+ if (wordMatch) {
72
+ return {
73
+ start: wordMatch.index,
74
+ end: wordMatch.index + wordMatch[0].length - 1,
75
+ text: wordMatch[0],
76
+ };
77
+ }
78
+
79
+ // Try simple substring match
80
+ const index = contentLower.indexOf(queryLower);
81
+ if (index !== -1) {
82
+ return {
83
+ start: index,
84
+ end: index + queryLower.length - 1,
85
+ text: content.substring(index, index + queryLower.length),
86
+ };
87
+ }
88
+
89
+ return null;
90
+ };
91
+
92
+ // Create search variations
93
+ const searchVariations = [searchQuery.toLowerCase()];
94
+
95
+ // Add stemmed variations
96
+ try {
97
+ const stemmed = PorterStemmer.stem(searchQuery.toLowerCase());
98
+ if (stemmed !== searchQuery.toLowerCase()) {
99
+ searchVariations.push(stemmed);
100
+ }
101
+
102
+ // Find actual words in conversations that stem to the same root
103
+ for (const message of conv.messages) {
104
+ if (message.content) {
105
+ const words = message.content.toLowerCase().match(/\b\w+\b/g) || [];
106
+ words.forEach((word: string) => {
107
+ if (PorterStemmer.stem(word) === stemmed && !searchVariations.includes(word)) {
108
+ searchVariations.push(word);
109
+ }
110
+ });
111
+ }
112
+ }
113
+ } catch (e) {
114
+ console.warn("Stemming failed for:", searchQuery, e);
115
+ }
116
+
117
+ // Add simple variations
118
+ const query = searchQuery.toLowerCase();
119
+ if (query.endsWith("s") && query.length > 3) {
120
+ searchVariations.push(query.slice(0, -1));
121
+ } else if (!query.endsWith("s")) {
122
+ searchVariations.push(query + "s");
123
+ }
124
+
125
+ // Search through all messages for the best match
126
+ for (const message of conv.messages) {
127
+ if (!message.content) continue;
128
+
129
+ // Try each variation in order of preference
130
+ for (const variation of searchVariations) {
131
+ const match = findBestMatch(message.content, variation);
132
+ if (match) {
133
+ const isExactQuery = variation === searchQuery.toLowerCase();
134
+ const priority = isExactQuery ? 1000 : match.text.length;
135
+
136
+ if (priority > bestMatchLength) {
137
+ bestMatch = {
138
+ content: message.content,
139
+ matchStart: match.start,
140
+ matchEnd: match.end,
141
+ matchedText: match.text,
142
+ };
143
+ bestMatchLength = priority;
144
+
145
+ // If we found exact query match, we're done
146
+ if (isExactQuery) break;
147
+ }
148
+ }
149
+ }
150
+
151
+ // Stop if we found an exact match
152
+ if (bestMatchLength >= 1000) break;
153
+ }
154
+
155
+ if (bestMatch) {
156
+ const { content, matchStart, matchEnd } = bestMatch;
157
+ matchedText = bestMatch.matchedText;
158
+
159
+ // Create centered context around the match
160
+ const maxContextLength = 160; // Maximum length of actual content (no padding)
161
+ const matchLength = matchEnd - matchStart + 1;
162
+
163
+ // Calculate context window - don't exceed maxContextLength even if content is longer
164
+ const availableForContext = Math.min(maxContextLength, content.length) - matchLength;
165
+ const contextPerSide = Math.floor(availableForContext / 2);
166
+
167
+ // Calculate snippet boundaries to center the match within maxContextLength
168
+ let snippetStart = Math.max(0, matchStart - contextPerSide);
169
+ let snippetEnd = Math.min(content.length, matchStart + matchLength + contextPerSide);
170
+
171
+ // Ensure we don't exceed maxContextLength
172
+ if (snippetEnd - snippetStart > maxContextLength) {
173
+ if (matchStart - contextPerSide < 0) {
174
+ // Match is near start, extend end but limit to maxContextLength
175
+ snippetEnd = Math.min(content.length, snippetStart + maxContextLength);
176
+ } else {
177
+ // Match is not near start, limit to maxContextLength from match start
178
+ snippetEnd = Math.min(content.length, snippetStart + maxContextLength);
179
+ }
180
+ }
181
+
182
+ // Adjust to word boundaries if possible (but don't move more than 15 chars)
183
+ const originalStart = snippetStart;
184
+ const originalEnd = snippetEnd;
185
+
186
+ while (
187
+ snippetStart > 0 &&
188
+ content[snippetStart] !== " " &&
189
+ content[snippetStart] !== "\n" &&
190
+ originalStart - snippetStart < 15
191
+ ) {
192
+ snippetStart--;
193
+ }
194
+ while (
195
+ snippetEnd < content.length &&
196
+ content[snippetEnd] !== " " &&
197
+ content[snippetEnd] !== "\n" &&
198
+ snippetEnd - originalEnd < 15
199
+ ) {
200
+ snippetEnd++;
201
+ }
202
+
203
+ // Extract the content
204
+ let extractedContent = content.substring(snippetStart, snippetEnd).trim();
205
+ // Add ellipsis indicators only
206
+ if (snippetStart > 0) {
207
+ extractedContent = "..." + extractedContent;
208
+ }
209
+ if (snippetEnd < content.length) {
210
+ extractedContent = extractedContent + "...";
211
+ }
212
+
213
+ matchedContent = extractedContent;
214
+ } else {
215
+ // Fallback: use beginning of the first message if no match found
216
+ const firstMessage = conv.messages[0];
217
+ if (firstMessage?.content) {
218
+ const content = firstMessage.content;
219
+ matchedContent = content.length > 200 ? content.substring(0, 200) + "..." : content;
220
+ matchedText = searchQuery; // Fallback to search query
221
+ }
222
+ }
223
+
224
  return {
225
  _id: conv._id,
226
+ id: conv._id,
227
  title: conv.title,
228
+ content: matchedContent,
229
+ matchedText,
230
  updatedAt: conv.updatedAt,
231
  model: conv.model,
232
  assistantId: conv.assistantId,
 
234
  };
235
  })
236
  );
 
237
  return Response.json(convs as GETSearchEndpointReturn);
238
  }
239
  return Response.json([]);