feat: markdown formatting

This commit is contained in:
michaelessiet
2024-12-30 12:00:51 +01:00
parent 1cbdb82bb5
commit 56185d94fb
5 changed files with 397 additions and 58 deletions

View File

@@ -1,39 +1,58 @@
import markdownToHtml from "@/utils/markdownToHTML";
import type { Message } from "ai/react";
import { useMemo } from "react";
export function ChatMessageBubble(props: { message: Message, aiEmoji?: string, sources: any[] }) {
const colorClassName =
props.message.role === "user" ? "bg-sky-600" : "bg-slate-50 text-black";
const alignmentClassName =
props.message.role === "user" ? "ml-auto" : "mr-auto";
const prefix = props.message.role === "user" ? "🧑" : props.aiEmoji;
return (
<div
className={`${alignmentClassName} ${colorClassName} rounded px-4 py-2 max-w-[80%] mb-8 flex`}
>
<div className="mr-2">
{prefix}
</div>
<div className="whitespace-pre-wrap flex flex-col">
<span>{props.message.content}</span>
{props.sources && props.sources.length ? <>
<code className="mt-4 mr-auto bg-slate-600 px-2 py-1 rounded">
<h2>
🔍 Sources:
</h2>
</code>
<code className="mt-1 mr-2 bg-slate-600 px-2 py-1 rounded text-xs">
{props.sources?.map((source, i) => (
<div className="mt-2" key={"source:" + i}>
{i + 1}. &quot;{source.pageContent}&quot;{
source.metadata?.loc?.lines !== undefined
? <div><br/>Lines {source.metadata?.loc?.lines?.from} to {source.metadata?.loc?.lines?.to}</div>
: ""
}
</div>
))}
</code>
</> : ""}
</div>
</div>
);
}
export function ChatMessageBubble(props: {
message: Message;
aiEmoji?: string;
sources: any[];
}) {
const colorClassName =
props.message.role === "user" ? "bg-sky-600" : "bg-slate-50 text-black";
const alignmentClassName =
props.message.role === "user" ? "ml-auto" : "mr-auto";
const prefix = props.message.role === "user" ? "🧑" : props.aiEmoji;
const content = useMemo(() => {
return markdownToHtml(props.message.content);
}, [props.message.content]);
return (
<div
className={`${alignmentClassName} ${colorClassName} rounded px-4 py-2 max-w-[80%] mb-8 flex`}
>
<div className="mr-2">{prefix}</div>
<div className="flex flex-col">
<div
className="prose prose-lg max-w-none"
dangerouslySetInnerHTML={{ __html: content }}
></div>
{props.sources && props.sources.length ? (
<>
<code className="mt-4 mr-auto bg-slate-600 px-2 py-1 rounded">
<h2>🔍 Sources:</h2>
</code>
<code className="mt-1 mr-2 bg-slate-600 px-2 py-1 rounded text-xs">
{props.sources?.map((source, i) => (
<div className="mt-2" key={"source:" + i}>
{i + 1}. &quot;{source.pageContent}&quot;
{source.metadata?.loc?.lines !== undefined ? (
<div>
<br />
Lines {source.metadata?.loc?.lines?.from} to{" "}
{source.metadata?.loc?.lines?.to}
</div>
) : (
""
)}
</div>
))}
</code>
</>
) : (
""
)}
</div>
</div>
);
}

View File

@@ -19,6 +19,7 @@
"@langchain/openai": "^0.3.11",
"@next/bundle-analyzer": "^13.4.19",
"@supabase/supabase-js": "^2.32.0",
"@tailwindcss/typography": "^0.5.15",
"@types/node": "20.12.12",
"@types/react": "18.3.2",
"@types/react-dom": "18.3.0",
@@ -26,7 +27,9 @@
"autoprefixer": "10.4.14",
"eslint": "8.46.0",
"eslint-config-next": "13.4.12",
"isomorphic-dompurify": "^2.19.0",
"langchain": "^0.3.5",
"marked": "^15.0.4",
"next": "^14.2.3",
"postcss": "8.4.27",
"react": "^18.3.1",

View File

@@ -10,7 +10,7 @@ importers:
dependencies:
'@langchain/community':
specifier: ^0.3.11
version: 0.3.20(@browserbasehq/sdk@2.0.0)(@browserbasehq/stagehand@1.8.0(@playwright/test@1.49.1)(bufferutil@4.0.8)(deepmerge@4.3.1)(dotenv@16.4.7)(openai@4.77.0(zod@3.24.1))(utf-8-validate@5.0.10)(zod@3.24.1))(@ibm-cloud/watsonx-ai@1.3.0)(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1)))(@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1))))(@supabase/supabase-js@2.47.10(bufferutil@4.0.8)(utf-8-validate@5.0.10))(axios@1.7.4)(ibm-cloud-sdk-core@5.1.0)(ignore@5.3.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.77.0(zod@3.24.1))(playwright@1.49.1)(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))
version: 0.3.20(@browserbasehq/sdk@2.0.0)(@browserbasehq/stagehand@1.8.0(@playwright/test@1.49.1)(bufferutil@4.0.8)(deepmerge@4.3.1)(dotenv@16.4.7)(openai@4.77.0(zod@3.24.1))(utf-8-validate@5.0.10)(zod@3.24.1))(@ibm-cloud/watsonx-ai@1.3.0)(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1)))(@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1))))(@supabase/supabase-js@2.47.10(bufferutil@4.0.8)(utf-8-validate@5.0.10))(axios@1.7.4)(ibm-cloud-sdk-core@5.1.0)(ignore@5.3.2)(jsdom@25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.77.0(zod@3.24.1))(playwright@1.49.1)(ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10))
'@langchain/core':
specifier: ^0.3.17
version: 0.3.26(openai@4.77.0(zod@3.24.1))
@@ -26,6 +26,9 @@ importers:
'@supabase/supabase-js':
specifier: ^2.32.0
version: 2.47.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)
'@tailwindcss/typography':
specifier: ^0.5.15
version: 0.5.15(tailwindcss@3.3.3)
'@types/node':
specifier: 20.12.12
version: 20.12.12
@@ -47,9 +50,15 @@ importers:
eslint-config-next:
specifier: 13.4.12
version: 13.4.12(eslint@8.46.0)(typescript@5.1.6)
isomorphic-dompurify:
specifier: ^2.19.0
version: 2.19.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
langchain:
specifier: ^0.3.5
version: 0.3.8(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1)))(@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1))))(axios@1.7.4)(openai@4.77.0(zod@3.24.1))
marked:
specifier: ^15.0.4
version: 15.0.4
next:
specifier: ^14.2.3
version: 14.2.21(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -1269,6 +1278,11 @@ packages:
'@swc/helpers@0.5.5':
resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
'@tailwindcss/typography@0.5.15':
resolution: {integrity: sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==}
peerDependencies:
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20'
'@tokenizer/token@0.3.0':
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
@@ -1332,6 +1346,9 @@ packages:
'@types/tough-cookie@4.0.5':
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
@@ -1437,6 +1454,10 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
agent-base@7.1.3:
resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
engines: {node: '>= 14'}
agentkeepalive@4.5.0:
resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==}
engines: {node: '>= 8.0.0'}
@@ -1791,12 +1812,20 @@ packages:
engines: {node: '>=4'}
hasBin: true
cssstyle@4.1.0:
resolution: {integrity: sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==}
engines: {node: '>=18'}
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
data-urls@5.0.0:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'}
data-view-buffer@1.0.2:
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
engines: {node: '>= 0.4'}
@@ -1898,6 +1927,9 @@ packages:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
dompurify@3.2.3:
resolution: {integrity: sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==}
dot-case@3.0.4:
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
@@ -2332,9 +2364,21 @@ packages:
hast-util-whitespace@3.0.0:
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
html-encoding-sniffer@4.0.0:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
@@ -2342,6 +2386,10 @@ packages:
resolution: {integrity: sha512-KJCbPz3tiXB1NGAD7cL4JtwpWV8yd/C7jsaHsxvedMo2ZblNG8emMyvSpGhiKAQVZmi3c0ujz6eJdy22NHuUWQ==}
engines: {node: '>=18'}
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -2458,6 +2506,9 @@ packages:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'}
is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
is-reference@3.0.3:
resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
@@ -2507,6 +2558,10 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
isomorphic-dompurify@2.19.0:
resolution: {integrity: sha512-ppcgeRlEwOQ+v/JDctcjnOsBwEoJlAWVDH5+LisLHphQFeWCrBiVvK6XF4wF0MJM5tJA6RxJSlpbmthnmonxOQ==}
engines: {node: '>=18'}
isomorphic-ws@4.0.1:
resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==}
peerDependencies:
@@ -2544,6 +2599,15 @@ packages:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
jsdom@25.0.1:
resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==}
engines: {node: '>=18'}
peerDependencies:
canvas: ^2.11.2
peerDependenciesMeta:
canvas:
optional: true
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
@@ -2687,6 +2751,9 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
lodash.castarray@4.4.0:
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
lodash.includes@4.3.0:
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
@@ -2737,6 +2804,11 @@ packages:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
marked@15.0.4:
resolution: {integrity: sha512-TCHvDqmb3ZJ4PWG7VEGVgtefA5/euFmsIhxtD0XsBxI39gUSKL81mIRFdt0AiNQozUahd4ke98ZdirExd/vSEw==}
engines: {node: '>= 18'}
hasBin: true
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
@@ -2866,6 +2938,9 @@ packages:
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
engines: {node: '>=0.10.0'}
nwsapi@2.2.16:
resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -2969,6 +3044,9 @@ packages:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
parse5@7.2.1:
resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==}
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -3059,6 +3137,10 @@ packages:
peerDependencies:
postcss: ^8.2.14
postcss-selector-parser@6.0.10:
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
engines: {node: '>=4'}
postcss-selector-parser@6.1.2:
resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
engines: {node: '>=4'}
@@ -3207,6 +3289,9 @@ packages:
rpc-websockets@9.0.4:
resolution: {integrity: sha512-yWZWN0M+bivtoNLnaDbtny4XchdAIF5Q4g/ZsC5UC61Ckbp0QczwO8fg44rV3uYmY4WHd+EZQbn90W1d8ojzqQ==}
rrweb-cssom@0.7.1:
resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==}
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -3221,6 +3306,13 @@ packages:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
@@ -3423,6 +3515,9 @@ packages:
peerDependencies:
vue: '>=3.2.26 < 4'
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
tailwindcss@3.3.3:
resolution: {integrity: sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==}
engines: {node: '>=14.0.0'}
@@ -3458,6 +3553,13 @@ packages:
tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
tldts-core@6.1.70:
resolution: {integrity: sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==}
tldts@6.1.70:
resolution: {integrity: sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==}
hasBin: true
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@@ -3480,9 +3582,17 @@ packages:
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
engines: {node: '>=6'}
tough-cookie@5.0.0:
resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==}
engines: {node: '>=16'}
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
tr46@5.0.0:
resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
engines: {node: '>=18'}
trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
@@ -3639,6 +3749,10 @@ packages:
typescript:
optional: true
w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
web-streams-polyfill@3.3.3:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
@@ -3650,11 +3764,27 @@ packages:
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
webpack-bundle-analyzer@4.7.0:
resolution: {integrity: sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg==}
engines: {node: '>= 10.13.0'}
hasBin: true
whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'}
whatwg-url@14.1.0:
resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==}
engines: {node: '>=18'}
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
@@ -3718,6 +3848,13 @@ packages:
utf-8-validate:
optional: true
xml-name-validator@5.0.0:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
yaml@2.6.1:
resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==}
engines: {node: '>= 14'}
@@ -4084,7 +4221,7 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@langchain/community@0.3.20(@browserbasehq/sdk@2.0.0)(@browserbasehq/stagehand@1.8.0(@playwright/test@1.49.1)(bufferutil@4.0.8)(deepmerge@4.3.1)(dotenv@16.4.7)(openai@4.77.0(zod@3.24.1))(utf-8-validate@5.0.10)(zod@3.24.1))(@ibm-cloud/watsonx-ai@1.3.0)(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1)))(@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1))))(@supabase/supabase-js@2.47.10(bufferutil@4.0.8)(utf-8-validate@5.0.10))(axios@1.7.4)(ibm-cloud-sdk-core@5.1.0)(ignore@5.3.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.77.0(zod@3.24.1))(playwright@1.49.1)(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))':
'@langchain/community@0.3.20(@browserbasehq/sdk@2.0.0)(@browserbasehq/stagehand@1.8.0(@playwright/test@1.49.1)(bufferutil@4.0.8)(deepmerge@4.3.1)(dotenv@16.4.7)(openai@4.77.0(zod@3.24.1))(utf-8-validate@5.0.10)(zod@3.24.1))(@ibm-cloud/watsonx-ai@1.3.0)(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1)))(@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1))))(@supabase/supabase-js@2.47.10(bufferutil@4.0.8)(utf-8-validate@5.0.10))(axios@1.7.4)(ibm-cloud-sdk-core@5.1.0)(ignore@5.3.2)(jsdom@25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.77.0(zod@3.24.1))(playwright@1.49.1)(ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10))':
dependencies:
'@browserbasehq/stagehand': 1.8.0(@playwright/test@1.49.1)(bufferutil@4.0.8)(deepmerge@4.3.1)(dotenv@16.4.7)(openai@4.77.0(zod@3.24.1))(utf-8-validate@5.0.10)(zod@3.24.1)
'@ibm-cloud/watsonx-ai': 1.3.0
@@ -4105,10 +4242,11 @@ snapshots:
'@browserbasehq/sdk': 2.0.0
'@supabase/supabase-js': 2.47.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)
ignore: 5.3.2
jsdom: 25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
jsonwebtoken: 9.0.2
lodash: 4.17.21
playwright: 1.49.1
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)
transitivePeerDependencies:
- '@langchain/anthropic'
- '@langchain/aws'
@@ -4874,6 +5012,14 @@ snapshots:
'@swc/counter': 0.1.3
tslib: 2.8.1
'@tailwindcss/typography@0.5.15(tailwindcss@3.3.3)':
dependencies:
lodash.castarray: 4.4.0
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
postcss-selector-parser: 6.0.10
tailwindcss: 3.3.3
'@tokenizer/token@0.3.0': {}
'@types/connect@3.4.38':
@@ -4936,6 +5082,9 @@ snapshots:
'@types/tough-cookie@4.0.5': {}
'@types/trusted-types@2.0.7':
optional: true
'@types/unist@3.0.3': {}
'@types/uuid@10.0.0': {}
@@ -5067,6 +5216,8 @@ snapshots:
acorn@8.14.0: {}
agent-base@7.1.3: {}
agentkeepalive@4.5.0:
dependencies:
humanize-ms: 1.2.1
@@ -5445,10 +5596,19 @@ snapshots:
cssesc@3.0.0: {}
cssstyle@4.1.0:
dependencies:
rrweb-cssom: 0.7.1
csstype@3.1.3: {}
damerau-levenshtein@1.0.8: {}
data-urls@5.0.0:
dependencies:
whatwg-mimetype: 4.0.0
whatwg-url: 14.1.0
data-view-buffer@1.0.2:
dependencies:
call-bound: 1.0.3
@@ -5531,6 +5691,10 @@ snapshots:
dependencies:
esutils: 2.0.3
dompurify@3.2.3:
optionalDependencies:
'@types/trusted-types': 2.0.7
dot-case@3.0.4:
dependencies:
no-case: 3.0.4
@@ -6144,8 +6308,26 @@ snapshots:
dependencies:
'@types/hast': 3.0.4
html-encoding-sniffer@4.0.0:
dependencies:
whatwg-encoding: 3.1.1
html-void-elements@3.0.0: {}
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.3
debug: 4.4.0
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.3
debug: 4.4.0
transitivePeerDependencies:
- supports-color
humanize-ms@1.2.1:
dependencies:
ms: 2.1.3
@@ -6165,11 +6347,15 @@ snapshots:
isstream: 0.1.2
jsonwebtoken: 9.0.2
mime-types: 2.1.35
retry-axios: 2.6.0(axios@1.7.4)
retry-axios: 2.6.0(axios@1.7.4(debug@4.4.0))
tough-cookie: 4.1.4
transitivePeerDependencies:
- supports-color
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
ieee754@1.2.1: {}
ignore@5.3.2: {}
@@ -6279,6 +6465,8 @@ snapshots:
is-path-inside@3.0.3: {}
is-potential-custom-element-name@1.0.1: {}
is-reference@3.0.3:
dependencies:
'@types/estree': 1.0.6
@@ -6328,6 +6516,16 @@ snapshots:
isexe@2.0.0: {}
isomorphic-dompurify@2.19.0(bufferutil@4.0.8)(utf-8-validate@5.0.10):
dependencies:
dompurify: 3.2.3
jsdom: 25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
transitivePeerDependencies:
- bufferutil
- canvas
- supports-color
- utf-8-validate
isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)):
dependencies:
ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)
@@ -6385,6 +6583,34 @@ snapshots:
dependencies:
argparse: 2.0.1
jsdom@25.0.1(bufferutil@4.0.8)(utf-8-validate@5.0.10):
dependencies:
cssstyle: 4.1.0
data-urls: 5.0.0
decimal.js: 10.4.3
form-data: 4.0.1
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.16
parse5: 7.2.1
rrweb-cssom: 0.7.1
saxes: 6.0.0
symbol-tree: 3.2.4
tough-cookie: 5.0.0
w3c-xmlserializer: 5.0.0
webidl-conversions: 7.0.0
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 14.1.0
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
xml-name-validator: 5.0.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
json-buffer@3.0.1: {}
json-schema-traverse@0.4.1: {}
@@ -6512,6 +6738,8 @@ snapshots:
dependencies:
p-locate: 5.0.0
lodash.castarray@4.4.0: {}
lodash.includes@4.3.0: {}
lodash.isboolean@3.0.3: {}
@@ -6557,6 +6785,8 @@ snapshots:
punycode.js: 2.3.1
uc.micro: 2.1.0
marked@15.0.4: {}
math-intrinsics@1.1.0: {}
mdast-util-to-hast@13.2.0:
@@ -6680,6 +6910,8 @@ snapshots:
normalize-range@0.1.2: {}
nwsapi@2.2.16: {}
object-assign@4.1.1: {}
object-hash@3.0.0: {}
@@ -6799,6 +7031,10 @@ snapshots:
dependencies:
callsites: 3.1.0
parse5@7.2.1:
dependencies:
entities: 4.5.0
path-exists@4.0.0: {}
path-is-absolute@1.0.1: {}
@@ -6860,6 +7096,11 @@ snapshots:
postcss: 8.4.27
postcss-selector-parser: 6.1.2
postcss-selector-parser@6.0.10:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
postcss-selector-parser@6.1.2:
dependencies:
cssesc: 3.0.0
@@ -6995,7 +7236,7 @@ snapshots:
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
retry-axios@2.6.0(axios@1.7.4):
retry-axios@2.6.0(axios@1.7.4(debug@4.4.0)):
dependencies:
axios: 1.7.4(debug@4.4.0)
@@ -7020,6 +7261,8 @@ snapshots:
bufferutil: 4.0.8
utf-8-validate: 5.0.10
rrweb-cssom@0.7.1: {}
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
@@ -7040,6 +7283,12 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.2.1
safer-buffer@2.1.2: {}
saxes@6.0.0:
dependencies:
xmlchars: 2.2.0
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
@@ -7366,6 +7615,8 @@ snapshots:
dependencies:
vue: 3.5.13(typescript@5.1.6)
symbol-tree@3.2.4: {}
tailwindcss@3.3.3:
dependencies:
'@alloc/quick-lru': 5.2.0
@@ -7415,6 +7666,12 @@ snapshots:
tiny-invariant@1.3.3: {}
tldts-core@6.1.70: {}
tldts@6.1.70:
dependencies:
tldts-core: 6.1.70
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@@ -7437,8 +7694,16 @@ snapshots:
universalify: 0.2.0
url-parse: 1.5.10
tough-cookie@5.0.0:
dependencies:
tldts: 6.1.70
tr46@0.0.3: {}
tr46@5.0.0:
dependencies:
punycode: 2.3.1
trim-lines@3.0.1: {}
ts-interface-checker@0.1.13: {}
@@ -7622,12 +7887,18 @@ snapshots:
optionalDependencies:
typescript: 5.1.6
w3c-xmlserializer@5.0.0:
dependencies:
xml-name-validator: 5.0.0
web-streams-polyfill@3.3.3: {}
web-streams-polyfill@4.0.0-beta.3: {}
webidl-conversions@3.0.1: {}
webidl-conversions@7.0.0: {}
webpack-bundle-analyzer@4.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10):
dependencies:
acorn: 8.14.0
@@ -7643,6 +7914,17 @@ snapshots:
- bufferutil
- utf-8-validate
whatwg-encoding@3.1.1:
dependencies:
iconv-lite: 0.6.3
whatwg-mimetype@4.0.0: {}
whatwg-url@14.1.0:
dependencies:
tr46: 5.0.0
webidl-conversions: 7.0.0
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
@@ -7718,6 +8000,10 @@ snapshots:
bufferutil: 4.0.8
utf-8-validate: 5.0.10
xml-name-validator@5.0.0: {}
xmlchars@2.2.0: {}
yaml@2.6.1: {}
yocto-queue@0.1.0: {}

View File

@@ -1,18 +1,18 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
},
},
plugins: [],
}
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [require("@tailwindcss/typography")],
};

View File

@@ -0,0 +1,31 @@
import { marked } from "marked";
import DOMPurify from "isomorphic-dompurify";
interface MarkedOptions {
gfm: boolean;
breaks: boolean;
headerIds: boolean;
mangle: false;
highlight?: (code: string, lang: string) => string;
}
// Configure marked options
const markedOptions: MarkedOptions = {
gfm: true, // GitHub Flavored Markdown
breaks: true, // Convert \n to <br>
headerIds: true, // Add ids to headers
mangle: false, // Don't escape HTML
highlight: function (code: string, lang: string): string {
// You can add syntax highlighting here if needed
return code;
},
};
marked.setOptions(markedOptions);
// Basic markdown to HTML conversion with sanitization
export default function markdownToHtml(markdown: string) {
const rawHtml = marked.parse(markdown);
console.log(rawHtml);
return DOMPurify.sanitize(rawHtml);
}