diff --git a/package.json b/package.json index 1493fc89c..727897346 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,6 @@ "@codemirror/state": "^6.7.1", "@fingerprintjs/fingerprintjs-pro-react": "^2.7.1", "@floating-ui/react": "^0.27.19", - "@kapaai/react-sdk": "^0.9.10", "@lezer/highlight": "^1.2.3", "@modelcontextprotocol/sdk": "^1.29.0", "@octokit/graphql": "^9.0.3", @@ -76,6 +75,7 @@ "@tanstack/ai-client": "^0.31.1", "@tanstack/ai-openai": "^0.22.6", "@tanstack/ai-openrouter": "^0.19.9", + "@tanstack/ai-react": "^0.24.1", "@tanstack/charts": "0.16.1", "@tanstack/create": "^0.70.0", "@tanstack/highlight": "^0.0.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 963956ba1..e2cdf47b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,9 +48,6 @@ importers: '@floating-ui/react': specifier: ^0.27.19 version: 0.27.19(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@kapaai/react-sdk': - specifier: ^0.9.10 - version: 0.9.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@lezer/highlight': specifier: ^1.2.3 version: 1.2.3 @@ -111,6 +108,9 @@ importers: '@tanstack/ai-openrouter': specifier: ^0.19.9 version: 0.19.9(@tanstack/ai@0.54.0(@opentelemetry/api@1.9.1)(zod@4.3.6)) + '@tanstack/ai-react': + specifier: ^0.24.1 + version: 0.24.1(@opentelemetry/api@1.9.1)(@tanstack/ai@0.54.0(@opentelemetry/api@1.9.1)(zod@4.3.6))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.3.6) '@tanstack/charts': specifier: 0.16.1 version: 0.16.1(lit@3.3.2)(octane@0.1.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@8.0.16(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)))(preact@10.29.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.12) @@ -1446,15 +1446,6 @@ packages: peerDependencies: three: ^0.182.0 - '@hcaptcha/loader@2.3.0': - resolution: {integrity: sha512-i4lnNxKBe+COf3R1nFZEWaZoHIoJjvDgWqvcNrdZq8ehoSNMN6KVZ56dcQ02qKie2h3+BkbkwlJA9DOIuLlK/g==} - - '@hcaptcha/react-hcaptcha@1.17.4': - resolution: {integrity: sha512-rIvgesG1N7SS9sAYYHFoWm+nXqRrxq7RcA9z2pKkDWV+S1GdfmrTNYA1aPyVWVe3eowphTCwyDJvl97Swwy0mw==} - peerDependencies: - react: '>= 16.3.0' - react-dom: '>= 16.3.0' - '@hono/node-server@1.19.12': resolution: {integrity: sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==} engines: {node: '>=18.14.1'} @@ -1639,12 +1630,6 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@kapaai/react-sdk@0.9.10': - resolution: {integrity: sha512-osQyFgBJmhNM207MpB0aZbjs2kjNHjEJbNc5YEReJuyFE72Iydn9CoZGxox2GYRgMIlQ7wljHCzgQqkvR8lXqA==} - peerDependencies: - react: '>=17.0.0' - react-dom: '>=17.0.0' - '@lezer/common@1.5.2': resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} @@ -3423,6 +3408,20 @@ packages: peerDependencies: '@tanstack/ai': ^0.54.0 + '@tanstack/ai-react@0.24.1': + resolution: {integrity: sha512-kxxkyunUiacR79HlRpBxOLT/fGKqNP16W/UBOcgM+lqmqaM7TiwqOpHHhwg800opGAEETIFFedUDMyP8ls1Glw==} + peerDependencies: + '@mcp-ui/client': ^7 + '@tanstack/ai': ^0.54.0 + '@types/react': '>=18.0.0' + react: '>=18.0.0' + react-dom: '>=18.0.0' + peerDependenciesMeta: + '@mcp-ui/client': + optional: true + react-dom: + optional: true + '@tanstack/ai-utils@0.4.0': resolution: {integrity: sha512-xqvAgPJkIuGk1m+jZKyb7vBTQIaBmUD9EVzlPkDWWMp80n69uwL0TnBZCVk+OwL9goTQiFy648dnBfLsiJgl6A==} @@ -3938,6 +3937,9 @@ packages: '@types/d3@7.4.3': resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + '@types/dom-speech-recognition@0.0.1': resolution: {integrity: sha512-udCxb8DvjcDKfk1WTBzDsxFbLgYxmQGKrE/ricoMqHRNjSlSUCcamVTA5lIQqzY10mY5qCY0QDwBfFEwhfoDPw==} @@ -3962,6 +3964,12 @@ packages: '@types/hogan.js@3.0.5': resolution: {integrity: sha512-/uRaY3HGPWyLqOyhgvW9Aa43BNnLZrNeQxl2p8wqId4UHMfPKolSB+U7BlZyO1ng7MkLnyEAItsBzCG0SDhqrA==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/mysql@2.15.27': resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} @@ -4017,12 +4025,18 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} '@types/webxr@0.5.24': resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==} + '@ungap/structured-clone@1.4.0': + resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} + '@uploadthing/mime-types@0.3.6': resolution: {integrity: sha512-t3tTzgwFV9+1D7lNDYc7Lr7kBwotHaX0ZsvoCGe7xGnXKo9z0jG2Sjl/msll12FeoLj77nyhsxevXyGpQDBvLg==} @@ -4191,6 +4205,9 @@ packages: babel-dead-code-elimination@1.0.12: resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -4315,6 +4332,9 @@ packages: caniuse-lite@1.0.30001784: resolution: {integrity: sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -4327,6 +4347,18 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -4382,6 +4414,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} @@ -4648,6 +4683,9 @@ packages: supports-color: optional: true + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -4663,6 +4701,10 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-gpu@5.0.70: resolution: {integrity: sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==} @@ -4676,6 +4718,9 @@ packages: devalue@5.9.0: resolution: {integrity: sha512-RWrqdArjvPbsATEhOPUo6Wndc/iWnkWKlhIrdlF3zMMYo/c3CVtoaVAyLtWxz5h8nSlkHzxnzV2uLydPXmtF+A==} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + diff@8.0.4: resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} @@ -4928,6 +4973,10 @@ packages: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -4941,6 +4990,9 @@ packages: '@typescript-eslint/types': optional: true + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -4980,6 +5032,9 @@ packages: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-check@3.23.2: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} @@ -5194,6 +5249,40 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-sanitize@5.0.2: + resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.1: + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + + highlight.js@11.11.2: + resolution: {integrity: sha512-oaXMACAU0kzOMXBjWpNcX+vlwSBCIAiZ9BHa7gA15NOTtT2L/l8OSZDuqS2XppOhZBPJ7hm4o8ep2kyuip2uEQ==} + engines: {node: '>=12.0.0'} + hls.js@1.6.15: resolution: {integrity: sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==} @@ -5211,6 +5300,12 @@ packages: htm@3.1.1: resolution: {integrity: sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@10.1.0: resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} @@ -5259,6 +5354,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + instantsearch-ui-components@0.23.0: resolution: {integrity: sha512-ILqp/RAm0mBrsFuGozTk09tPpQMiODtcLVHQRudXBC23YcaTpt9qTB30nWy5yHODb15dBH5DoUw7gqyUo/PPkw==} @@ -5286,6 +5384,12 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -5321,6 +5425,9 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -5342,6 +5449,9 @@ packages: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -5445,9 +5555,6 @@ packages: jose@6.2.2: resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} - js-cookie@3.0.8: - resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==} - js-tokens@10.0.0: resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} @@ -5689,6 +5796,12 @@ packages: lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + lowlight@3.3.0: + resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==} + lru-cache@11.2.7: resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} engines: {node: 20 || >=22} @@ -5705,6 +5818,9 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + markdown-to-jsx@7.7.17: resolution: {integrity: sha512-7mG/1feQ0TX5I7YyMZVDgCC/y2I3CiEhIRQIhyov9nGBP5eoVrOXXHuL5ZP8GRfxVZKRiXWJgwXkb9It+nQZfQ==} engines: {node: '>= 10'} @@ -5726,6 +5842,51 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + media-typer@1.1.0: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} @@ -5749,6 +5910,90 @@ packages: meshoptimizer@1.0.1: resolution: {integrity: sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.2: + resolution: {integrity: sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + mime-db@1.54.0: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} @@ -5964,6 +6209,9 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-gitignore@2.0.0: resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} engines: {node: '>=14'} @@ -6144,6 +6392,9 @@ packages: promise-worker-transferable@1.0.4: resolution: {integrity: sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==} + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -6196,6 +6447,12 @@ packages: react: '>= 16.8.0 < 20' react-dom: '>= 16.8.0 < 20' + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -6266,6 +6523,27 @@ packages: resolution: {integrity: sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==} engines: {node: '>=8'} + rehype-highlight@7.0.2: + resolution: {integrity: sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-sanitize@6.0.0: + resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + remove-accents@0.5.0: resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} @@ -6496,6 +6774,9 @@ packages: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -6565,6 +6846,9 @@ packages: string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -6584,6 +6868,12 @@ packages: style-mod@4.1.3: resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + stylis@4.3.6: resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} @@ -6673,13 +6963,6 @@ packages: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} - tldts-core@7.4.2: - resolution: {integrity: sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA==} - - tldts@7.4.2: - resolution: {integrity: sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw==} - hasBin: true - toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -6687,6 +6970,9 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + troika-three-text@0.52.4: resolution: {integrity: sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==} peerDependencies: @@ -6700,6 +6986,9 @@ packages: troika-worker-utils@0.52.0: resolution: {integrity: sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==} + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-algebra@2.0.0: resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} @@ -6783,6 +7072,27 @@ packages: resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} engines: {node: '>=20'} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} @@ -6914,6 +7224,15 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-bundle-analyzer@1.3.6: resolution: {integrity: sha512-elFXkF9oGy4CJEeOdP1DSEHRDKWfmaEDfT9/GuF4jmyfmUF6nC//iN+ythqMEVvrtchOEHGKLumZwnu1NjoI0w==} hasBin: true @@ -6992,6 +7311,9 @@ packages: w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + webgl-constants@1.1.1: resolution: {integrity: sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==} @@ -7184,6 +7506,9 @@ packages: use-sync-external-store: optional: true + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@ag-ui/core@0.1.1-canary.beta.0(zod@4.3.6)': @@ -8046,15 +8371,6 @@ snapshots: lit: 3.3.2 three: 0.183.2 - '@hcaptcha/loader@2.3.0': {} - - '@hcaptcha/react-hcaptcha@1.17.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@babel/runtime': 7.29.2 - '@hcaptcha/loader': 2.3.0 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - '@hono/node-server@1.19.12(hono@4.12.9)': dependencies: hono: 4.12.9 @@ -8187,16 +8503,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@kapaai/react-sdk@0.9.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@fingerprintjs/fingerprintjs-pro-react': 2.7.1 - '@fingerprintjs/fingerprintjs-pro-spa': 1.3.3 - '@hcaptcha/react-hcaptcha': 1.17.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - js-cookie: 3.0.8 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - tldts: 7.4.2 - '@lezer/common@1.5.2': {} '@lezer/css@1.3.6': @@ -9753,6 +10059,24 @@ snapshots: '@tanstack/ai': 0.54.0(@opentelemetry/api@1.9.1)(zod@4.3.6) '@tanstack/ai-utils': 0.4.0 + '@tanstack/ai-react@0.24.1(@opentelemetry/api@1.9.1)(@tanstack/ai@0.54.0(@opentelemetry/api@1.9.1)(zod@4.3.6))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.3.6)': + dependencies: + '@tanstack/ai': 0.54.0(@opentelemetry/api@1.9.1)(zod@4.3.6) + '@tanstack/ai-client': 0.31.1(@opentelemetry/api@1.9.1)(zod@4.3.6) + '@types/react': 19.2.14 + react: 19.2.3 + react-markdown: 10.1.0(@types/react@19.2.14)(react@19.2.3) + rehype-highlight: 7.0.2 + rehype-raw: 7.0.0 + rehype-sanitize: 6.0.0 + remark-gfm: 4.0.1 + optionalDependencies: + react-dom: 19.2.3(react@19.2.3) + transitivePeerDependencies: + - '@opentelemetry/api' + - supports-color + - zod + '@tanstack/ai-utils@0.4.0': {} '@tanstack/ai@0.54.0(@opentelemetry/api@1.9.1)(zod@4.3.6)': @@ -10436,6 +10760,10 @@ snapshots: '@types/d3-transition': 3.0.9 '@types/d3-zoom': 3.0.8 + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + '@types/dom-speech-recognition@0.0.1': {} '@types/draco3d@1.4.10': {} @@ -10456,6 +10784,12 @@ snapshots: '@types/hogan.js@3.0.5': {} + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + '@types/mysql@2.15.27': dependencies: '@types/node': 25.5.0 @@ -10524,10 +10858,14 @@ snapshots: '@types/trusted-types@2.0.7': {} + '@types/unist@2.0.11': {} + '@types/unist@3.0.3': {} '@types/webxr@0.5.24': {} + '@ungap/structured-clone@1.4.0': {} + '@uploadthing/mime-types@0.3.6': {} '@uploadthing/react@7.3.3(react@19.2.3)(uploadthing@7.7.4(express@5.2.1)(tailwindcss@4.2.2))': @@ -10696,6 +11034,8 @@ snapshots: transitivePeerDependencies: - supports-color + bail@2.0.2: {} + balanced-match@1.0.2: {} balanced-match@4.0.4: {} @@ -10815,6 +11155,8 @@ snapshots: caniuse-lite@1.0.30001784: {} + ccount@2.0.1: {} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -10828,6 +11170,14 @@ snapshots: chalk@5.6.2: {} + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 @@ -10917,6 +11267,8 @@ snapshots: color-name@1.1.4: {} + comma-separated-tokens@2.0.3: {} + commander@7.2.0: {} commander@8.3.0: {} @@ -11192,6 +11544,10 @@ snapshots: dependencies: ms: 2.1.3 + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -11210,6 +11566,8 @@ snapshots: depd@2.0.0: {} + dequal@2.0.3: {} + detect-gpu@5.0.70: dependencies: webgl-constants: 1.1.1 @@ -11220,6 +11578,10 @@ snapshots: devalue@5.9.0: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + diff@8.0.4: {} discord-interactions@4.4.0: {} @@ -11521,12 +11883,16 @@ snapshots: escape-string-regexp@1.0.5: {} + escape-string-regexp@5.0.0: {} + esprima@4.0.1: {} esrap@2.3.2: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + estree-util-is-identifier-name@3.0.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -11604,6 +11970,8 @@ snapshots: dependencies: is-extendable: 0.1.1 + extend@3.0.2: {} + fast-check@3.23.2: dependencies: pure-rand: 6.1.0 @@ -11800,6 +12168,98 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.2.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.4.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.1 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-sanitize@5.0.2: + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.4.0 + unist-util-position: 5.0.0 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + + highlight.js@11.11.2: {} + hls.js@1.6.15: {} hogan.js@3.0.2: @@ -11813,6 +12273,10 @@ snapshots: htm@3.1.1: {} + html-url-attributes@3.0.1: {} + + html-void-elements@3.0.0: {} + htmlparser2@10.1.0: dependencies: domelementtype: 2.3.0 @@ -11869,6 +12333,8 @@ snapshots: inherits@2.0.4: {} + inline-style-parser@0.2.7: {} + instantsearch-ui-components@0.23.0(react@19.2.3): dependencies: '@swc/helpers': 0.5.18 @@ -11912,6 +12378,13 @@ snapshots: ipaddr.js@1.9.1: {} + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -11954,6 +12427,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-decimal@2.0.1: {} + is-docker@2.2.1: {} is-extendable@0.1.1: {} @@ -11972,6 +12447,8 @@ snapshots: has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 + is-hexadecimal@2.0.1: {} + is-map@2.0.3: {} is-negative-zero@2.0.3: {} @@ -12063,8 +12540,6 @@ snapshots: jose@6.2.2: {} - js-cookie@3.0.8: {} - js-tokens@10.0.0: {} js-tokens@4.0.0: {} @@ -12258,6 +12733,14 @@ snapshots: lodash@4.17.23: {} + longest-streak@3.1.0: {} + + lowlight@3.3.0: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + highlight.js: 11.11.2 + lru-cache@11.2.7: {} lru-cache@5.1.1: @@ -12273,6 +12756,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + markdown-table@3.0.4: {} + markdown-to-jsx@7.7.17(react@19.2.3): optionalDependencies: react: 19.2.3 @@ -12286,6 +12771,159 @@ snapshots: math-intrinsics@1.1.0: {} + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.4.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + media-typer@1.1.0: {} memorystream@0.3.1: {} @@ -12322,6 +12960,197 @@ snapshots: meshoptimizer@1.0.1: {} + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.2: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.2 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + mime-db@1.54.0: {} mime-types@3.0.2: @@ -12594,6 +13423,16 @@ snapshots: pako@1.0.11: {} + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + parse-gitignore@2.0.0: {} parse-json@4.0.0: @@ -12734,6 +13573,8 @@ snapshots: is-promise: 2.2.2 lie: 3.3.0 + property-information@7.2.0: {} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -12794,6 +13635,24 @@ snapshots: react-dom: 19.2.3(react@19.2.3) react-instantsearch-core: 7.29.0(algoliasearch@5.50.0)(react@19.2.3) + react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.3): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.2.14 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 19.2.3 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.3): dependencies: react: 19.2.3 @@ -12871,6 +13730,59 @@ snapshots: regexparam@2.0.2: {} + rehype-highlight@7.0.2: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-text: 4.0.2 + lowlight: 3.3.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-sanitize@6.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-sanitize: 5.0.2 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + remove-accents@0.5.0: {} remove-markdown@0.6.3: {} @@ -13191,6 +14103,8 @@ snapshots: source-map@0.7.6: {} + space-separated-tokens@2.0.2: {} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 @@ -13279,6 +14193,11 @@ snapshots: dependencies: safe-buffer: 5.1.2 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -13291,6 +14210,14 @@ snapshots: style-mod@4.1.3: {} + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + stylis@4.3.6: {} supports-color@10.2.2: {} @@ -13393,16 +14320,12 @@ snapshots: tinypool@2.1.0: {} - tldts-core@7.4.2: {} - - tldts@7.4.2: - dependencies: - tldts-core: 7.4.2 - toidentifier@1.0.1: {} tr46@0.0.3: {} + trim-lines@3.0.1: {} + troika-three-text@0.52.4(three@0.183.2): dependencies: bidi-js: 1.0.3 @@ -13417,6 +14340,8 @@ snapshots: troika-worker-utils@0.52.0: {} + trough@2.2.0: {} + ts-algebra@2.0.0: {} ts-dedent@2.2.0: {} @@ -13509,6 +14434,44 @@ snapshots: unicorn-magic@0.4.0: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + universal-user-agent@7.0.3: {} unpipe@1.0.0: {} @@ -13585,6 +14548,21 @@ snapshots: vary@1.1.2: {} + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + vite-bundle-analyzer@1.3.6: {} vite@8.0.16(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0): @@ -13625,6 +14603,8 @@ snapshots: w3c-keyname@2.2.8: {} + web-namespaces@2.0.1: {} + webgl-constants@1.1.1: {} webgl-sdf-generator@1.1.1: {} @@ -13802,3 +14782,5 @@ snapshots: '@types/react': 19.2.14 react: 19.2.3 use-sync-external-store: 1.6.0(react@19.2.3) + + zwitch@2.0.4: {} diff --git a/src/components/AiDock.tsx b/src/components/AiDock.tsx index 6d3315428..1d51f641c 100644 --- a/src/components/AiDock.tsx +++ b/src/components/AiDock.tsx @@ -29,21 +29,20 @@ import { ArrowsInSimpleIcon, } from '@phosphor-icons/react' import { - DefaultKapaApiService, - KapaProvider, - processStream, + fetchServerSentEvents, useChat, - type StreamSource, -} from '@kapaai/react-sdk' + type UIMessage, +} from '@tanstack/ai-react' import { streamingMarkdownExtension } from '@tanstack/markdown/extensions/streaming' import { Markdown as TanStackMarkdown } from '@tanstack/markdown/react' +import { autolinkBareUrlsExtension } from '~/components/markdown/autolinkBareUrls' +import { LibraryWordmark } from '~/components/LibraryWordmark' import { useSearchContext } from '~/contexts/SearchContext' -import { publicLibraries } from '~/libraries' +import { ai, publicLibraries } from '~/libraries' import { frameworkOptions } from '~/libraries/frameworks' import { capitalize } from '~/utils/utils' import { CodeBlock } from '~/components/markdown/CodeBlock' import { InlineCode } from '~/ui/InlineCode' -import { env } from '~/utils/env' import { isSafeHref } from '~/utils/url-boundary' import { type AlgoliaHit, @@ -67,8 +66,20 @@ const AI_DOCK_MIN_WIDTH = 320 const AI_DOCK_DEFAULT_WIDTH = 360 const AI_DOCK_MAX_WIDTH_RATIO = 0.5 const AI_DOCK_MAXIMIZED_WIDTH = 1200 -const KAPA_RECAPTCHA_READY_TIMEOUT_MS = 8_000 -const KAPA_INTEGRATION_ID = '86e864f7-401e-48c8-ac6f-5fcbc86c5668' + +// Algolia Agent Studio. The API key is search-ACL only and safe to ship to +// browsers; abuse is bounded by Agent Studio's approved domains and +// per-agent/per-IP rate limits. +const AGENT_STUDIO_APP_ID = 'FQ0DQ6MA3C' +const AGENT_STUDIO_SEARCH_API_KEY = '2a257513b7a9e64906b9e0e53a6bc3ce' +const AGENT_STUDIO_AGENT_ID = 'eeaedc22-16c5-4314-974f-38d93631761b' +const AGENT_STUDIO_BASE_URL = `https://${AGENT_STUDIO_APP_ID.toLowerCase()}.algolia.net/agent-studio/1` +const AGENT_STUDIO_COMPLETIONS_URL = `${AGENT_STUDIO_BASE_URL}/agents/${AGENT_STUDIO_AGENT_ID}/completions?compatibilityMode=ag-ui&stream=true` +const AGENT_STUDIO_FEEDBACK_URL = `${AGENT_STUDIO_BASE_URL}/feedback` +const AGENT_STUDIO_AUTH_HEADERS = { + 'X-Algolia-Application-Id': AGENT_STUDIO_APP_ID, + 'X-Algolia-API-Key': AGENT_STUDIO_SEARCH_API_KEY, +} type SearchSurface = 'modal' | 'dock' type AiDockStyle = React.CSSProperties & { @@ -113,72 +124,7 @@ function writeAiDockWidth(width: number) { localStorage.setItem(AI_DOCK_WIDTH_STORAGE_KEY, String(Math.round(width))) } -function waitForKapaRecaptchaReady() { - if (typeof window === 'undefined') { - return Promise.resolve() - } - - return new Promise((resolve) => { - let timeoutId: number | null = null - let intervalId: number | null = null - let isSettled = false - let hasRegisteredReadyCallback = false - - const cleanup = () => { - if (timeoutId !== null) { - window.clearTimeout(timeoutId) - timeoutId = null - } - - if (intervalId !== null) { - window.clearInterval(intervalId) - intervalId = null - } - } - - const finish = () => { - if (isSettled) { - return - } - - isSettled = true - cleanup() - window.setTimeout(resolve, 0) - } - - const checkReady = () => { - if (hasRegisteredReadyCallback) { - return true - } - - const enterprise = window.grecaptcha?.enterprise - - if (!enterprise) { - return false - } - - hasRegisteredReadyCallback = true - - if (intervalId !== null) { - window.clearInterval(intervalId) - intervalId = null - } - - enterprise.ready(finish) - return true - } - - timeoutId = window.setTimeout(finish, KAPA_RECAPTCHA_READY_TIMEOUT_MS) - - if (checkReady()) { - return - } - - intervalId = window.setInterval(checkReady, 50) - }) -} - -function KapaMarkdownLink({ +function AiMarkdownLink({ href, children, ...props @@ -186,7 +132,7 @@ function KapaMarkdownLink({ return ( {children} @@ -194,7 +140,7 @@ function KapaMarkdownLink({ ) } -function KapaMarkdownTable({ +function AiMarkdownTable({ children, className, ...props @@ -214,7 +160,7 @@ function KapaMarkdownTable({ ) } -function KapaMarkdownTableHead({ +function AiMarkdownTableHead({ children, className, ...props @@ -232,7 +178,7 @@ function KapaMarkdownTableHead({ ) } -function KapaMarkdownTableBody({ +function AiMarkdownTableBody({ children, className, ...props @@ -250,7 +196,7 @@ function KapaMarkdownTableBody({ ) } -function KapaMarkdownTableCell({ +function AiMarkdownTableCell({ children, className, ...props @@ -268,7 +214,7 @@ function KapaMarkdownTableCell({ ) } -function KapaMarkdownTableHeaderCell({ +function AiMarkdownTableHeaderCell({ children, className, ...props @@ -286,15 +232,40 @@ function KapaMarkdownTableHeaderCell({ ) } -const kapaMarkdownComponents = { +const aiMarkdownComponents = { pre: CodeBlock, code: InlineCode, - a: KapaMarkdownLink, - table: KapaMarkdownTable, - thead: KapaMarkdownTableHead, - tbody: KapaMarkdownTableBody, - td: KapaMarkdownTableCell, - th: KapaMarkdownTableHeaderCell, + a: AiMarkdownLink, + table: AiMarkdownTable, + thead: AiMarkdownTableHead, + tbody: AiMarkdownTableBody, + td: AiMarkdownTableCell, + th: AiMarkdownTableHeaderCell, + p: ({ children }: { children?: React.ReactNode }) => ( +

{children}

+ ), + // Sized in em so headings scale with the compact (13px) and regular (14px) + // chat bubbles alike. + h1: ({ children }: { children?: React.ReactNode }) => ( +

{children}

+ ), + h2: ({ children }: { children?: React.ReactNode }) => ( +

+ {children} +

+ ), + h3: ({ children }: { children?: React.ReactNode }) => ( +

{children}

+ ), + h4: ({ children }: { children?: React.ReactNode }) => ( +

{children}

+ ), + h5: ({ children }: { children?: React.ReactNode }) => ( +
{children}
+ ), + h6: ({ children }: { children?: React.ReactNode }) => ( +
{children}
+ ), ul: ({ children }: { children?: React.ReactNode }) => ( ), @@ -308,75 +279,342 @@ const kapaMarkdownComponents = { ), } -const kapaMarkdownExtensions = [streamingMarkdownExtension()] +const aiMarkdownExtensions = [ + streamingMarkdownExtension(), + autolinkBareUrlsExtension, +] -function parseSourceGroupIDs(value: string | undefined) { - if (!value) { - return undefined - } +type FeedbackReaction = 'upvote' | 'downvote' - const ids = value - .split(',') - .map((id) => id.trim()) - .filter((id) => id.length > 0) +type AnswerSource = { + title: string + subtitle: string + source_url: string + source_type: string +} - return ids.length > 0 ? ids : undefined +type AnswerToolCall = { + id: string + query: string + isPending: boolean } -type KapaReaction = 'upvote' | 'downvote' +type AnswerSegment = + | { type: 'text'; content: string } + | { type: 'tool-call'; toolCall: AnswerToolCall } -type KapaDisplayQA = { +type DisplayQA = { id: string | null question: string answer: string - sources: Array + sources: Array + // Ordered answer flow (text interleaved with search-tool activity) for the + // live conversation; not persisted to chat history, which falls back to + // the flat `answer` text. + segments?: Array isGenerationAborted: boolean isFeedbackSubmissionEnabled?: boolean - reaction: KapaReaction | null + reaction: FeedbackReaction | null } -type KapaHistoryItem = { +type ChatHistoryItem = { threadId: string title: string createdAt: number updatedAt: number - conversation: Array + conversation: Array } -type KapaSubmitQueryArgs = Parameters[0] -type KapaChatStreamCallbacks = Parameters< - DefaultKapaApiService['submitQuery'] ->[1] -type KapaSubmitFeedbackArgs = Parameters< - DefaultKapaApiService['addFeedback'] ->[0] - -const KAPA_HISTORY_STORAGE_KEY = 'tanstack-kapa-chat-history' -const KAPA_HISTORY_LIMIT = 5 - -class KapaThreadOverrideApiService { - private service = new DefaultKapaApiService(processStream) - - constructor( - private threadIdOverrideRef: React.MutableRefObject, - ) {} - - submitQuery( - args: KapaSubmitQueryArgs, - callbacks: KapaChatStreamCallbacks, - ): Promise { - return this.service.submitQuery( - { ...args, threadId: this.threadIdOverrideRef.current }, - callbacks, - ) +const CHAT_HISTORY_STORAGE_KEY = 'tanstack-ai-chat-history' +const CHAT_HISTORY_LIMIT = 5 + +/** + * Which Agent Studio thread the panel is on, plus the QAs restored from local + * history for that thread. Agent Studio persists conversations server-side + * keyed by `threadId`, so resuming only needs the id; the saved QAs render + * as the prefix of the transcript, ahead of whatever the live client holds. + */ +type ThreadSelection = { + threadId: string + savedConversation: Array +} + +function freshThread(): ThreadSelection { + return { threadId: crypto.randomUUID(), savedConversation: [] } +} + +/** + * Re-selecting the thread that is already live must be a no-op: the client + * keeps its messages when `threadId` is unchanged, so restoring the saved + * transcript as a prefix would duplicate every answer. + */ +function selectThread( + current: ThreadSelection, + item: ChatHistoryItem, +): ThreadSelection { + if (item.threadId === current.threadId) { + return current + } + + return { threadId: item.threadId, savedConversation: item.conversation } +} + +function messageText(message: UIMessage) { + return message.parts + .map((part) => (part.type === 'text' ? part.content : '')) + .join('') +} + +/** + * Pull doc links out of the agent's Algolia search tool results. The tool + * output is provider-shaped JSON, so this walks it defensively and collects + * anything that looks like a hit: a record with a safe URL plus a usable + * title (explicit `title` or the deepest DocSearch `hierarchy` level). + */ +function collectSourcesFromToolOutput( + value: unknown, + into: Map, + depth = 0, +) { + if (depth > 6 || into.size >= 6) { + return + } + + if (Array.isArray(value)) { + for (const item of value) { + collectSourcesFromToolOutput(item, into, depth + 1) + } + return } - addFeedback(args: KapaSubmitFeedbackArgs): Promise { - return this.service.addFeedback(args) + if (!isUnknownRecord(value)) { + return } - abortCurrent(): void { - this.service.abortCurrent() + const url = readString(value.url) || readString(value.source_url) + + if (url && isSafeHref(url) && !into.has(url)) { + const hierarchy = isUnknownRecord(value.hierarchy) ? value.hierarchy : {} + const hierarchyLevels = ['lvl6', 'lvl5', 'lvl4', 'lvl3', 'lvl2', 'lvl1'] + .map((level) => readString(hierarchy[level])) + .filter((level) => level.length > 0) + const title = + readString(value.title) || + hierarchyLevels[0] || + readString(hierarchy.lvl0) + + if (title) { + into.set(url, { + title, + subtitle: readString(hierarchy.lvl0), + source_url: url, + source_type: 'documentation', + }) + return + } + } + + for (const nested of Object.values(value)) { + collectSourcesFromToolOutput(nested, into, depth + 1) + } +} + +/** + * Turn the search tool's input into a human-readable label. The agent's + * Algolia tools take `{ queries: [{ query }], originalQuery, ... }`. + */ +function describeToolCallQuery(input: unknown, rawArguments: string) { + let parsed = input + + if (parsed === undefined && rawArguments) { + try { + parsed = JSON.parse(rawArguments) + } catch { + return '' + } + } + + if (!isUnknownRecord(parsed)) { + return '' + } + + const queries = Array.isArray(parsed.queries) ? parsed.queries : [] + const queryTexts = queries + .map((entry) => (isUnknownRecord(entry) ? readString(entry.query) : '')) + .filter((query) => query.length > 0) + + return queryTexts.join(', ') || readString(parsed.originalQuery) +} + +/** Tool results arrive as JSON strings on the wire; parse them for source extraction. */ +function parseToolResult(content: string | Array): unknown { + if (typeof content !== 'string') { + return content + } + + try { + return JSON.parse(content) + } catch { + return undefined + } +} + +/** + * Fold the AG-UI message list into the question/answer pairs the panel + * renders: each user message opens a QA, and the assistant messages that + * follow contribute answer text, search-tool activity, and tool-derived + * sources. + */ +function messagesToDisplayQAs( + messages: Array, + reactions: Record, +): Array { + const qas: Array = [] + let current: DisplayQA | null = null + + for (const message of messages) { + if (message.role === 'user') { + current = { + id: null, + question: messageText(message), + answer: '', + sources: [], + segments: [], + isGenerationAborted: false, + isFeedbackSubmissionEnabled: false, + reaction: null, + } + qas.push(current) + continue + } + + if (message.role !== 'assistant' || !current) { + continue + } + + const sources = new Map( + current.sources.map((source) => [source.source_url, source]), + ) + const segments = current.segments ?? [] + + // A completed tool call carries its output inline while streaming, but a + // hydrated or replayed thread can represent it as a sibling tool-result + // part instead. Index those so both shapes yield sources. + const toolResultOutputs = new Map() + for (const part of message.parts) { + if (part.type === 'tool-result' && part.error === undefined) { + toolResultOutputs.set(part.toolCallId, parseToolResult(part.content)) + } + } + + for (const part of message.parts) { + if (part.type === 'text' && part.content.trim()) { + segments.push({ type: 'text', content: part.content.trim() }) + } + + if (part.type === 'tool-call') { + const output = part.output ?? toolResultOutputs.get(part.id) + + segments.push({ + type: 'tool-call', + toolCall: { + id: part.id, + query: describeToolCallQuery(part.input, part.arguments), + isPending: output === undefined && part.state !== 'error', + }, + }) + + if (output !== undefined) { + collectSourcesFromToolOutput(output, sources) + } + } + } + + current.id = message.id + current.segments = segments + // Flat text fallback, used for chat history persistence. The agent + // narrates between tool calls, so join the chunks as paragraphs. + current.answer = segments + .flatMap((segment) => (segment.type === 'text' ? [segment.content] : [])) + .join('\n\n') + current.sources = Array.from(sources.values()) + current.isFeedbackSubmissionEnabled = true + current.reaction = reactions[message.id] ?? null + } + + return qas +} + +/** + * Chat state for the AI panel, backed by Algolia Agent Studio's AG-UI + * completions endpoint via TanStack AI. Conversations are keyed by + * `threadId` on the server, so resuming a thread only needs the same id. + */ +function useAgentStudioChat(threadId: string) { + const connection = React.useMemo( + () => + fetchServerSentEvents(AGENT_STUDIO_COMPLETIONS_URL, { + headers: AGENT_STUDIO_AUTH_HEADERS, + }), + [], + ) + const { messages, sendMessage, isLoading, error, stop, clear } = useChat({ + connection, + threadId, + }) + const [reactions, setReactions] = React.useState< + Record + >({}) + + const conversation = React.useMemo( + () => messagesToDisplayQAs(messages, reactions), + [messages, reactions], + ) + + const addFeedback = React.useCallback( + (messageId: string, reaction: FeedbackReaction) => { + // Optimistic: Agent Studio accepts a single vote per message, and the + // UI already locks the buttons after the first reaction. + setReactions((current) => ({ ...current, [messageId]: reaction })) + + fetch(AGENT_STUDIO_FEEDBACK_URL, { + method: 'POST', + headers: { + ...AGENT_STUDIO_AUTH_HEADERS, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + messageId, + agentId: AGENT_STUDIO_AGENT_ID, + vote: reaction === 'upvote' ? 1 : 0, + }), + }) + .then((response) => { + if (!response.ok) { + throw new Error(`feedback rejected: ${response.status}`) + } + }) + .catch(() => { + // Unlock the buttons so the vote can be retried. + setReactions((current) => { + const { [messageId]: _failed, ...rest } = current + return rest + }) + }) + }, + [], + ) + + return { + conversation, + submitQuery: sendMessage, + isBusy: isLoading, + stopGeneration: stop, + // Cancels any in-flight stream and drops messages *and* the error, which + // would otherwise outlive a threadId change (the SDK only resets messages). + resetChat: clear, + addFeedback, + errorMessage: error?.message ?? null, } } @@ -396,7 +634,7 @@ function readNumber(value: unknown) { return typeof value === 'number' && Number.isFinite(value) ? value : 0 } -function parseKapaReaction(value: unknown): KapaReaction | null { +function parseFeedbackReaction(value: unknown): FeedbackReaction | null { if (value === 'upvote' || value === 'downvote') { return value } @@ -404,7 +642,7 @@ function parseKapaReaction(value: unknown): KapaReaction | null { return null } -function parseKapaSource(value: unknown): StreamSource | null { +function parseAnswerSource(value: unknown): AnswerSource | null { if (!isUnknownRecord(value)) { return null } @@ -430,7 +668,7 @@ function parseKapaSource(value: unknown): StreamSource | null { } } -function parseKapaHistoryQA(value: unknown): KapaDisplayQA | null { +function parseChatHistoryQA(value: unknown): DisplayQA | null { if (!isUnknownRecord(value)) { return null } @@ -447,14 +685,14 @@ function parseKapaHistoryQA(value: unknown): KapaDisplayQA | null { id: typeof value.id === 'string' ? value.id : null, question, answer: readString(value.answer), - sources: sourceValues.map(parseKapaSource).filter(isDefined), + sources: sourceValues.map(parseAnswerSource).filter(isDefined), isGenerationAborted: value.isGenerationAborted === true, isFeedbackSubmissionEnabled: value.isFeedbackSubmissionEnabled === true, - reaction: parseKapaReaction(value.reaction), + reaction: parseFeedbackReaction(value.reaction), } } -function parseKapaHistoryItem(value: unknown): KapaHistoryItem | null { +function parseChatHistoryItem(value: unknown): ChatHistoryItem | null { if (!isUnknownRecord(value)) { return null } @@ -464,7 +702,7 @@ function parseKapaHistoryItem(value: unknown): KapaHistoryItem | null { ? value.conversation : [] const conversation = conversationValues - .map(parseKapaHistoryQA) + .map(parseChatHistoryQA) .filter(isDefined) if (!threadId || conversation.length === 0) { @@ -475,20 +713,20 @@ function parseKapaHistoryItem(value: unknown): KapaHistoryItem | null { return { threadId, - title: readString(value.title) || buildKapaHistoryTitle(conversation), + title: readString(value.title) || buildChatHistoryTitle(conversation), createdAt: readNumber(value.createdAt) || updatedAt, updatedAt, conversation, } } -function readKapaHistory() { +function readChatHistory() { if (typeof window === 'undefined') { return [] } try { - const raw = localStorage.getItem(KAPA_HISTORY_STORAGE_KEY) + const raw = localStorage.getItem(CHAT_HISTORY_STORAGE_KEY) if (!raw) { return [] } @@ -499,31 +737,31 @@ function readKapaHistory() { } return parsed - .map(parseKapaHistoryItem) + .map(parseChatHistoryItem) .filter(isDefined) .sort((a, b) => b.updatedAt - a.updatedAt) - .slice(0, KAPA_HISTORY_LIMIT) + .slice(0, CHAT_HISTORY_LIMIT) } catch { return [] } } -function writeKapaHistory(items: Array) { +function writeChatHistory(items: Array) { if (typeof window === 'undefined') { return } try { localStorage.setItem( - KAPA_HISTORY_STORAGE_KEY, - JSON.stringify(items.slice(0, KAPA_HISTORY_LIMIT)), + CHAT_HISTORY_STORAGE_KEY, + JSON.stringify(items.slice(0, CHAT_HISTORY_LIMIT)), ) } catch { // Ignore storage failures so chat keeps working. } } -function buildKapaHistoryTitle(conversation: Array) { +function buildChatHistoryTitle(conversation: Array) { const firstQuestion = conversation .find((qa) => qa.question.trim().length > 0) @@ -535,7 +773,7 @@ function buildKapaHistoryTitle(conversation: Array) { : firstQuestion } -function toStoredKapaQA(qa: KapaDisplayQA): KapaDisplayQA { +function toStoredQA(qa: DisplayQA): DisplayQA { return { id: qa.id, question: qa.question, @@ -552,30 +790,30 @@ function toStoredKapaQA(qa: KapaDisplayQA): KapaDisplayQA { } } -function upsertKapaHistoryItem( +function upsertChatHistoryItem( threadId: string, - conversation: Array, + conversation: Array, ) { - const current = readKapaHistory() + const current = readChatHistory() const existing = current.find((item) => item.threadId === threadId) const now = Date.now() - const nextItem: KapaHistoryItem = { + const nextItem: ChatHistoryItem = { threadId, - title: buildKapaHistoryTitle(conversation), + title: buildChatHistoryTitle(conversation), createdAt: existing?.createdAt ?? now, updatedAt: now, - conversation: conversation.map(toStoredKapaQA), + conversation: conversation.map(toStoredQA), } const next = [ nextItem, ...current.filter((item) => item.threadId !== threadId), - ].slice(0, KAPA_HISTORY_LIMIT) + ].slice(0, CHAT_HISTORY_LIMIT) - writeKapaHistory(next) + writeChatHistory(next) return next } -function formatKapaHistoryTime(updatedAt: number) { +function formatChatHistoryTime(updatedAt: number) { const elapsed = Date.now() - updatedAt if (elapsed < 60_000) { @@ -596,17 +834,17 @@ function formatKapaHistoryTime(updatedAt: number) { }).format(new Date(updatedAt)) } -function useKapaChatHistory() { +function useChatHistory() { const [items, setItems] = - React.useState>(readKapaHistory) + React.useState>(readChatHistory) const save = React.useCallback( - (threadId: string, conversation: Array) => { + (threadId: string, conversation: Array) => { if (conversation.length === 0) { return } - setItems(upsertKapaHistoryItem(threadId, conversation)) + setItems(upsertChatHistoryItem(threadId, conversation)) }, [], ) @@ -726,7 +964,7 @@ function CopyChatButton({ conversation, compact = false, }: { - conversation: Array + conversation: Array compact?: boolean }) { const [copied, setCopied] = React.useState(false) @@ -794,17 +1032,17 @@ function CopyChatButton({ ) } -function KapaHistoryButton({ +function ChatHistoryButton({ items, activeThreadId, isBusy, onSelect, compact = false, }: { - items: Array + items: Array activeThreadId: string | null isBusy: boolean - onSelect: (item: KapaHistoryItem) => void + onSelect: (item: ChatHistoryItem) => void compact?: boolean }) { const { cancelAiDockHoverClose } = useSearchContext() @@ -875,7 +1113,7 @@ function KapaHistoryButton({ {item.title} - {formatKapaHistoryTime(item.updatedAt)} + {formatChatHistoryTime(item.updatedAt)} {isActive ? ( @@ -926,43 +1164,7 @@ function MessageActionButton({ ) } -function AIMessageHeader({ action }: { action?: React.ReactNode }) { - return ( -
-
-
- TanStack - -
-
- - TanStack - - - answers by kapa.ai - -
-
- {action} -
- ) -} - -function getKapaSuggestions(selectedLibrary: string) { +function getAiSuggestions(selectedLibrary: string) { if (selectedLibrary === 'query') { return [ 'How do I invalidate a query?', @@ -1010,7 +1212,7 @@ function getKapaSuggestions(selectedLibrary: string) { ] } -function KapaWelcome({ +function AiWelcome({ selectedLibrary, selectedFramework, onSuggestion, @@ -1023,11 +1225,10 @@ function KapaWelcome({ disabled?: boolean compact?: boolean }) { - const suggestions = getKapaSuggestions(selectedLibrary) + const suggestions = getAiSuggestions(selectedLibrary) return (
-

void @@ -1099,6 +1300,8 @@ function KapaAnswer({ const canSubmitFeedback = qa.id !== null && qa.isFeedbackSubmissionEnabled && qa.reaction === null const hasAnswerError = !isStreaming && !qa.answer && !!error + const segments = qa.segments ?? [] + const hasAnswerContent = segments.length > 0 || !!qa.answer return (

@@ -1120,7 +1323,6 @@ function KapaAnswer({
-
{hasAnswerError ? (

{error}

- ) : qa.answer ? ( + ) : hasAnswerContent ? (
- - {qa.answer} - + {segments.length > 0 ? ( + segments.map((segment, index) => + segment.type === 'tool-call' ? ( +
+ + + {segment.toolCall.isPending && isStreaming + ? segment.toolCall.query + ? `Searching the docs for “${segment.toolCall.query}”…` + : 'Searching the docs…' + : segment.toolCall.query + ? `Searched the docs for “${segment.toolCall.query}”` + : 'Searched the docs'} + +
+ ) : ( + + {segment.content} + + ), + ) + ) : ( + + {qa.answer} + + )}
) : (
@@ -1236,11 +1475,10 @@ function KapaAnswer({ ) } -function KapaChatPanel({ +function AiChatPanel({ onReset, isFullHeight, onToggleFullHeight, - threadIdOverrideRef, newChatRequestId, surface, isDockMaximized = false, @@ -1249,23 +1487,22 @@ function KapaChatPanel({ onReset: () => void isFullHeight: boolean onToggleFullHeight: () => void - threadIdOverrideRef: React.MutableRefObject newChatRequestId: number surface: SearchSurface isDockMaximized?: boolean onToggleDockMaximized?: () => void }) { + const [thread, setThread] = React.useState(freshThread) + const { threadId, savedConversation } = thread const { conversation, - threadId, submitQuery, - isGeneratingAnswer, - isPreparingAnswer, + isBusy, stopGeneration, - resetConversation, + resetChat, addFeedback, - error, - } = useChat() + errorMessage, + } = useAgentStudioChat(threadId) const { aiDockAskRequest, clearAiDockAskRequest, @@ -1275,27 +1512,27 @@ function KapaChatPanel({ const { selectedLibrary, selectedFramework, showSearchResults, searchQuery } = useSearchFilters() const scrollRef = React.useRef(null) - const { items: historyItems, save: saveHistory } = useKapaChatHistory() - const [selectedHistoryItem, setSelectedHistoryItem] = - React.useState(null) - const isBusy = isGeneratingAnswer || isPreparingAnswer + const { items: historyItems, save: saveHistory } = useChatHistory() + // Question waiting for the chat client of a freshly created thread. Changing + // `threadId` recreates the client on the next render, so a "clear then ask" + // flow (e.g. Ask AI from the search modal) must defer the submit. + const [pendingQuestion, setPendingQuestion] = React.useState( + null, + ) const isDock = surface === 'dock' const isSubmittingRef = React.useRef(false) - const pendingSubmitIdRef = React.useRef(0) const lockedToBottom = React.useRef(true) const handledNewChatRequestId = React.useRef(newChatRequestId) const handledAiDockAskRequestId = React.useRef(0) - const displayConversation = React.useMemo>(() => { - const liveConversation = Array.from(conversation) - - if (!selectedHistoryItem) { - return liveConversation - } - - return [...selectedHistoryItem.conversation, ...liveConversation] - }, [conversation, selectedHistoryItem]) + const displayConversation = React.useMemo>( + () => + savedConversation.length === 0 + ? conversation + : [...savedConversation, ...conversation], + [conversation, savedConversation], + ) const hasConversation = displayConversation.length > 0 - const activeThreadId = threadId ?? selectedHistoryItem?.threadId ?? null + const activeThreadId = threadId React.useEffect(() => { if (!isDock) { @@ -1308,13 +1545,7 @@ function KapaChatPanel({ }, [displayConversation.length, isBusy, isDock, searchQuery, setAiDockDirty]) React.useEffect(() => { - if (threadId) { - threadIdOverrideRef.current = threadId - } - }, [threadId, threadIdOverrideRef]) - - React.useEffect(() => { - if (!threadId || isBusy || displayConversation.length === 0) { + if (isBusy || displayConversation.length === 0) { return } @@ -1361,61 +1592,49 @@ function KapaChatPanel({ return } - const submitId = pendingSubmitIdRef.current + 1 - pendingSubmitIdRef.current = submitId isSubmittingRef.current = true - - waitForKapaRecaptchaReady().then(() => { - if (pendingSubmitIdRef.current !== submitId) { - return - } - - submitQuery(trimmed) - }) + void submitQuery(trimmed) }, [isBusy, submitQuery], ) const selectHistoryItem = React.useCallback( - (item: KapaHistoryItem) => { + (item: ChatHistoryItem) => { if (isBusy) { return } - resetConversation() - threadIdOverrideRef.current = item.threadId - setSelectedHistoryItem(item) + setThread((current) => selectThread(current, item)) lockedToBottom.current = true }, - [isBusy, resetConversation, threadIdOverrideRef], + [isBusy], ) const clearActiveChat = React.useCallback(() => { - pendingSubmitIdRef.current += 1 isSubmittingRef.current = false - resetConversation() - threadIdOverrideRef.current = null - setSelectedHistoryItem(null) + resetChat() + setThread(freshThread()) lockedToBottom.current = true onReset() - }, [onReset, resetConversation, threadIdOverrideRef]) + }, [onReset, resetChat]) - const startNewChat = React.useCallback(() => { - if (isBusy) { - stopGeneration() + React.useEffect(() => { + if (handledNewChatRequestId.current === newChatRequestId) { + return } + handledNewChatRequestId.current = newChatRequestId clearActiveChat() - }, [clearActiveChat, isBusy, stopGeneration]) + }, [newChatRequestId, clearActiveChat]) React.useEffect(() => { - if (handledNewChatRequestId.current === newChatRequestId) { + if (pendingQuestion === null || isBusy) { return } - handledNewChatRequestId.current = newChatRequestId - startNewChat() - }, [newChatRequestId, startNewChat]) + setPendingQuestion(null) + ask(pendingQuestion) + }, [ask, isBusy, pendingQuestion]) React.useEffect(() => { if (!isDock || !aiDockAskRequest) { @@ -1433,10 +1652,9 @@ function KapaChatPanel({ handledAiDockAskRequestId.current = aiDockAskRequest.id clearActiveChat() - ask(aiDockAskRequest.question) + setPendingQuestion(aiDockAskRequest.question) clearAiDockAskRequest(aiDockAskRequest.id) }, [ - ask, aiDockAskRequest, clearActiveChat, clearAiDockAskRequest, @@ -1468,10 +1686,39 @@ function KapaChatPanel({ {isDock ? null : }
{isDock ? ( -
-
- TanStack AI -
+
+ + + + + + Algolia + Algolia +
) : ( <> @@ -1512,7 +1759,7 @@ function KapaChatPanel({ onToggle={onToggleDockMaximized} /> ) : null} -
)}
@@ -1644,122 +1887,6 @@ function StickyTopBlur() { ) } -function KapaUnavailablePanel({ - isFullHeight, - onToggleFullHeight, - surface, - isDockMaximized = false, - onToggleDockMaximized, -}: { - isFullHeight: boolean - onToggleFullHeight: () => void - surface: SearchSurface - isDockMaximized?: boolean - onToggleDockMaximized?: () => void -}) { - const { closeSearch, setAiDockDirty } = useSearchContext() - const { selectedLibrary, selectedFramework, showSearchResults, searchQuery } = - useSearchFilters() - const isDock = surface === 'dock' - - React.useEffect(() => { - if (!isDock) { - return - } - - setAiDockDirty(searchQuery.trim().length > 0) - }, [isDock, searchQuery, setAiDockDirty]) - - return ( -
-
- {isDock ? null : } -
- {isDock ? ( -
-
- TanStack AI -
-
- ) : ( - <> - - - - )} -
- {isDock && onToggleDockMaximized ? ( -
- -
- ) : null} -
-
-
- Kapa is not configured. Add{' '} - VITE_KAPA_INTEGRATION_ID to enable - AI answers. -
- {}} - disabled - compact={isDock} - /> -
-
-
- -
- {}} surface={surface} /> -
-
- ) -} - function SearchPanel({ isFullHeight, onToggleFullHeight, @@ -1775,23 +1902,6 @@ function SearchPanel({ isDockMaximized?: boolean onToggleDockMaximized?: () => void }) { - const integrationId = KAPA_INTEGRATION_ID - const threadIdOverrideRef = React.useRef(null) - const apiService = React.useMemo( - () => new KapaThreadOverrideApiService(threadIdOverrideRef), - [], - ) - const sourceGroupIDsInclude = React.useMemo( - () => parseSourceGroupIDs(env.VITE_KAPA_SOURCE_GROUP_IDS), - [], - ) - - React.useEffect(() => { - return () => { - apiService.abortCurrent() - } - }, [apiService]) - return (
- {integrationId ? ( - - {}} - isFullHeight={isFullHeight} - onToggleFullHeight={onToggleFullHeight} - threadIdOverrideRef={threadIdOverrideRef} - newChatRequestId={newChatRequestId} - surface={surface} - isDockMaximized={isDockMaximized} - onToggleDockMaximized={onToggleDockMaximized} - /> - - ) : ( - - )} + {}} + isFullHeight={isFullHeight} + onToggleFullHeight={onToggleFullHeight} + newChatRequestId={newChatRequestId} + surface={surface} + isDockMaximized={isDockMaximized} + onToggleDockMaximized={onToggleDockMaximized} + />
) } diff --git a/src/components/markdown/autolinkBareUrls.ts b/src/components/markdown/autolinkBareUrls.ts new file mode 100644 index 000000000..6b62464ef --- /dev/null +++ b/src/components/markdown/autolinkBareUrls.ts @@ -0,0 +1,72 @@ +import type { InlineNode, MarkdownExtension } from '@tanstack/markdown' + +// The agent often emits bare URLs in prose, and CommonMark only autolinks +// URLs wrapped in angle brackets. Turn bare http(s) URLs inside text nodes +// into links. Code spans and existing links are separate inline node types, +// so this transform can never touch them. +const BARE_URL_PATTERN = /https?:\/\/[^\s<>]+/g +const TRAILING_PUNCTUATION_PATTERN = /[.,;:!?'"]+$/ + +// GFM autolink rule: a trailing `)` is only punctuation when the URL has +// more closing than opening parens, so `/routes/(auth)` stays intact while +// `(see https://x.dev/a)` drops the wrapper. +function trimBareUrl(raw: string) { + let url = raw.replace(TRAILING_PUNCTUATION_PATTERN, '') + + while (url.endsWith(')') && url.split(')').length > url.split('(').length) { + url = url.slice(0, -1).replace(TRAILING_PUNCTUATION_PATTERN, '') + } + + return url +} + +export function autolinkInlineNodes( + nodes: Array, +): Array { + return nodes.flatMap((node): Array => { + if ( + node.type === 'strong' || + node.type === 'emphasis' || + node.type === 'strike' + ) { + return [{ ...node, children: autolinkInlineNodes(node.children) }] + } + + if (node.type !== 'text') { + return [node] + } + + const parts: Array = [] + let cursor = 0 + + for (const match of node.value.matchAll(BARE_URL_PATTERN)) { + const index = match.index ?? 0 + const url = trimBareUrl(match[0]) + + if (index > cursor) { + parts.push({ type: 'text', value: node.value.slice(cursor, index) }) + } + parts.push({ + type: 'link', + href: url, + children: [{ type: 'text', value: url }], + }) + cursor = index + url.length + } + + if (parts.length === 0) { + return [node] + } + + if (cursor < node.value.length) { + parts.push({ type: 'text', value: node.value.slice(cursor) }) + } + + return parts + }) +} + +export const autolinkBareUrlsExtension: MarkdownExtension = { + name: 'autolink-bare-urls', + transformInline: autolinkInlineNodes, +} diff --git a/src/styles/app.css b/src/styles/app.css index 7cb4d5e94..ed5130e56 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -3005,12 +3005,6 @@ html:not(.dark) .library-landing-graphic [style*='drop-shadow'] { @apply my-4; } -/* Kapa injects invisible reCAPTCHA; keep it from occupying the UI corner. */ -.grecaptcha-badge { - display: none !important; - pointer-events: none !important; -} - button[aria-label='Open TanStack Devtools'] { display: none !important; } diff --git a/src/utils/env.ts b/src/utils/env.ts index c5855418d..c6c4e7c14 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -23,8 +23,6 @@ const serverEnvSchema = v.object({ }) const clientEnvSchema = v.object({ - VITE_KAPA_INTEGRATION_ID: v.optional(v.string()), - VITE_KAPA_SOURCE_GROUP_IDS: v.optional(v.string()), VITE_WEBCONTAINER_API_KEY: v.optional(v.string()), }) diff --git a/tests/ai-dock-autolink.test.ts b/tests/ai-dock-autolink.test.ts new file mode 100644 index 000000000..46d8ec71a --- /dev/null +++ b/tests/ai-dock-autolink.test.ts @@ -0,0 +1,50 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import type { InlineNode } from '@tanstack/markdown' +import { autolinkInlineNodes } from '../src/components/markdown/autolinkBareUrls' + +function autolink(value: string) { + return autolinkInlineNodes([{ type: 'text', value }]) +} + +function hrefs(nodes: Array) { + return nodes.flatMap((node) => (node.type === 'link' ? [node.href] : [])) +} + +test('bare URLs in prose become links', () => { + const nodes = autolink('See https://tanstack.com/router for details.') + + assert.deepEqual(hrefs(nodes), ['https://tanstack.com/router']) + assert.deepEqual(nodes[0], { type: 'text', value: 'See ' }) + assert.deepEqual(nodes[2], { type: 'text', value: ' for details.' }) +}) + +test('balanced parentheses stay part of the URL', () => { + assert.deepEqual( + hrefs(autolink('https://github.com/x/y/blob/main/routes/(auth)')), + ['https://github.com/x/y/blob/main/routes/(auth)'], + ) +}) + +test('a wrapping parenthesis is not part of the URL', () => { + assert.deepEqual(hrefs(autolink('(see https://tanstack.com/start).')), [ + 'https://tanstack.com/start', + ]) + assert.deepEqual(hrefs(autolink('(https://tanstack.com/routes/(auth))')), [ + 'https://tanstack.com/routes/(auth)', + ]) +}) + +test('non-text inline nodes are left alone', () => { + const code: InlineNode = { + type: 'inlineCode', + value: 'https://tanstack.com', + } + const link: InlineNode = { + type: 'link', + href: 'https://tanstack.com', + children: [{ type: 'text', value: 'https://tanstack.com' }], + } + + assert.deepEqual(autolinkInlineNodes([code, link]), [code, link]) +}) diff --git a/vite.config.ts b/vite.config.ts index 8ae2ecb23..5e79a2027 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -161,7 +161,7 @@ const useSyncExternalStoreShimIndexAlias = { const serverBundledClientPackages = [ ...(shouldUseRedact ? ['@tanstack/redact'] : []), /^@radix-ui\//, - '@kapaai/react-sdk', + /^@tanstack\/ai(?:-|$)/, '@tanstack/highlight', '@tanstack/markdown', '@tanstack/react-hotkeys',