From 8021c848ddb44ca23cade8059bf1182755813c22 Mon Sep 17 00:00:00 2001 From: BRama10 Date: Wed, 9 Oct 2024 22:01:59 -0400 Subject: [PATCH 1/8] feat: add mantine provider --- agenthub/app/agentchat/page.tsx | 0 agenthub/app/layout.tsx | 28 +- agenthub/package-lock.json | 991 ++++++++++++++++++++++++++++++-- agenthub/package.json | 26 +- agenthub/postcss.config.mjs | 10 + 5 files changed, 1009 insertions(+), 46 deletions(-) create mode 100644 agenthub/app/agentchat/page.tsx diff --git a/agenthub/app/agentchat/page.tsx b/agenthub/app/agentchat/page.tsx new file mode 100644 index 00000000..e69de29b diff --git a/agenthub/app/layout.tsx b/agenthub/app/layout.tsx index 4b435eb5..c70aa731 100644 --- a/agenthub/app/layout.tsx +++ b/agenthub/app/layout.tsx @@ -45,6 +45,9 @@ import '@/styles/google-font-Source-Sans-Pro.css' import '@/styles/google-font-IBM-Plex-Mono.css' import "./globals.css"; import "./ts.css" +import '@mantine/core/styles.css'; + +import { ColorSchemeScript, MantineProvider } from '@mantine/core'; export const metadata: Metadata = { title: 'AIOS – The future of AI Agents', @@ -72,18 +75,23 @@ export default function RootLayout({ children }: { children: React.ReactNode }) */} + + + -
-
- - {/*
*/} - {children} -
-
+ +
+
+ + {/*
*/} + {children} +
+
+
diff --git a/agenthub/package-lock.json b/agenthub/package-lock.json index 88b80389..3c720ab4 100644 --- a/agenthub/package-lock.json +++ b/agenthub/package-lock.json @@ -9,6 +9,19 @@ "version": "0.1.0", "hasInstallScript": true, "dependencies": { + "@mantine/carousel": "^7.13.2", + "@mantine/charts": "^7.13.2", + "@mantine/code-highlight": "^7.13.2", + "@mantine/core": "^7.13.2", + "@mantine/dates": "^7.13.2", + "@mantine/dropzone": "^7.13.2", + "@mantine/form": "^7.13.2", + "@mantine/hooks": "^7.13.2", + "@mantine/modals": "^7.13.2", + "@mantine/notifications": "^7.13.2", + "@mantine/nprogress": "^7.13.2", + "@mantine/spotlight": "^7.13.2", + "@mantine/tiptap": "^7.13.2", "@nextui-org/react": "^2.4.8", "@prisma/client": "^5.19.1", "@radix-ui/react-avatar": "^1.1.0", @@ -16,18 +29,22 @@ "@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-scroll-area": "^1.1.0", "@tiptap/extension-document": "^2.7.2", + "@tiptap/extension-link": "^2.8.0", "@tiptap/extension-mention": "^2.7.2", "@tiptap/extension-paragraph": "^2.7.2", "@tiptap/extension-placeholder": "^2.7.4", "@tiptap/extension-text": "^2.7.2", - "@tiptap/pm": "^2.2.4", - "@tiptap/react": "^2.7.2", + "@tiptap/pm": "^2.8.0", + "@tiptap/react": "^2.8.0", + "@tiptap/starter-kit": "^2.8.0", "@tiptap/suggestion": "^2.7.2", "axios": "^1.7.7", "class-variance-authority": "^0.7.0", "classnames": "^2.5.1", "common": "^0.2.5", "copy-to-clipboard": "^3.3.3", + "dayjs": "^1.11.13", + "embla-carousel-react": "^7.1.0", "framer-motion": "^11.5.5", "install": "^0.13.0", "lodash": "^4.17.21", @@ -44,6 +61,7 @@ "react-quill-patched": "^3.0.4", "react-syntax-highlighter": "^15.5.0", "react-tooltip": "^5.28.0", + "recharts": "^2.12.7", "sonner": "^1.5.0", "supabase": "^1.192.5", "ui": "*" @@ -55,7 +73,9 @@ "@types/react-syntax-highlighter": "^15.5.13", "eslint": "^8", "eslint-config-next": "14.2.8", - "postcss": "^8", + "postcss": "^8.4.47", + "postcss-preset-mantine": "^1.17.0", + "postcss-simple-vars": "^7.0.1", "tailwindcss": "^3.4.1", "typescript": "^5" } @@ -155,6 +175,20 @@ "@floating-ui/utils": "^0.2.8" } }, + "node_modules/@floating-ui/react": { + "version": "0.26.24", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.24.tgz", + "integrity": "sha512-2ly0pCkZIGEQUq5H8bBK0XJmc1xIK/RM3tvVzY3GBER7IOD1UgmC2Y2tjj4AuS+TC+vTE1KJv2053290jua0Sw==", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@floating-ui/react-dom": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", @@ -379,6 +413,230 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mantine/carousel": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/carousel/-/carousel-7.13.2.tgz", + "integrity": "sha512-qFIM32uWiiQQPkdAOeymVstwOGiPR/0jxvniHO3llPB193ZFWsKKq3IBIch6zRG2bSPOV18yZsWR6VVkXYXb+A==", + "peerDependencies": { + "@mantine/core": "7.13.2", + "@mantine/hooks": "7.13.2", + "embla-carousel-react": ">=7.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/charts": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/charts/-/charts-7.13.2.tgz", + "integrity": "sha512-rfaf45F58Cx5k7X9qN5yV2ZhVFAIB8i+1S/DvNE3uUflGmrevHuuNbNDDbYtQOw8ObL959rtSWOyL1SmC11D0A==", + "peerDependencies": { + "@mantine/core": "7.13.2", + "@mantine/hooks": "7.13.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "recharts": "^2.10.3" + } + }, + "node_modules/@mantine/code-highlight": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/code-highlight/-/code-highlight-7.13.2.tgz", + "integrity": "sha512-gBZvsECENELXsAeCuk86/vpOnxqdOXx9e6iNbQKyiGxcmcHli0+OgnZLL4WP4xt0pd6jTni+B+kJP2g9WfJQtA==", + "dependencies": { + "clsx": "^2.1.1", + "highlight.js": "^11.9.0" + }, + "peerDependencies": { + "@mantine/core": "7.13.2", + "@mantine/hooks": "7.13.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/code-highlight/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mantine/code-highlight/node_modules/highlight.js": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.10.0.tgz", + "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@mantine/core": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-7.13.2.tgz", + "integrity": "sha512-nD8oKIal+KdthqF074+ZG21035QBEAKso2zK9D6zWxRTLVCjLCNSc5JSXrXLrdBTnvPQGY26yunX4+MEPlmrHg==", + "dependencies": { + "@floating-ui/react": "^0.26.9", + "clsx": "^2.1.1", + "react-number-format": "^5.3.1", + "react-remove-scroll": "^2.5.7", + "react-textarea-autosize": "8.5.3", + "type-fest": "^4.12.0" + }, + "peerDependencies": { + "@mantine/hooks": "7.13.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/core/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mantine/core/node_modules/type-fest": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mantine/dates": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-7.13.2.tgz", + "integrity": "sha512-FSLGTM5s47mmHnIudRxrMXjE1EO56Qp01nATa9OwqVgVYVxxJ5xvS1ys5yxSGSE1jQk+3kyYQXHyLFcqbFhIVA==", + "dependencies": { + "clsx": "^2.1.1" + }, + "peerDependencies": { + "@mantine/core": "7.13.2", + "@mantine/hooks": "7.13.2", + "dayjs": ">=1.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/dates/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mantine/dropzone": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-7.13.2.tgz", + "integrity": "sha512-Sce2mkZ2Y2LNAIrHy6P2woRHlUiUx1tIN7yhtHt3Iils8sGH+cDERc+C53ZxKcVlEge6AlhfX0PhtancYxzDaQ==", + "dependencies": { + "react-dropzone-esm": "15.0.1" + }, + "peerDependencies": { + "@mantine/core": "7.13.2", + "@mantine/hooks": "7.13.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/form": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/form/-/form-7.13.2.tgz", + "integrity": "sha512-jx7st64CzzzwdKwhRw/rHqQ/ReGa5tW9PnId5sdE5fhf9QJjjiNWfQjGUFnA7WSEGlOTbwrznKA45ro5lFY6CA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "klona": "^2.0.6" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/@mantine/hooks": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-7.13.2.tgz", + "integrity": "sha512-NKfGl2sKZw6zF//AfAFJrVDftjg7DKCn0h8rwJBIZCKi9axhwlV0Mvlqe2dep8QuM7O/uLLJSymSKIv1gaxIJg==", + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/@mantine/modals": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-7.13.2.tgz", + "integrity": "sha512-rKkXss1ZYAbkSi9ZcUsBY/HyGjgKk+bT8TXzLoClBRgg6uyto+/2lT9M5e4Nao+2PsjsRnWI/ZgddNZKiSaNgQ==", + "peerDependencies": { + "@mantine/core": "7.13.2", + "@mantine/hooks": "7.13.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/notifications": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-7.13.2.tgz", + "integrity": "sha512-14vFJtR0wjO8Won96UMLxIGmKetR0ocBxcghtuGh6+wnXt6r/ezfQKsdGkkNj6w91I+0Nl9jspcxEekE5q2tBQ==", + "dependencies": { + "@mantine/store": "7.13.2", + "react-transition-group": "4.4.5" + }, + "peerDependencies": { + "@mantine/core": "7.13.2", + "@mantine/hooks": "7.13.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/nprogress": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/nprogress/-/nprogress-7.13.2.tgz", + "integrity": "sha512-GiseMDc1K42vZa2nHS19rvvtOZX+o2vmwhVhOFRZJ8NxIaR/pk2DcRjeq2cbRFgz9/KtUZNoehOwDqHY+ZYw/A==", + "dependencies": { + "@mantine/store": "7.13.2" + }, + "peerDependencies": { + "@mantine/core": "7.13.2", + "@mantine/hooks": "7.13.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/spotlight": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/spotlight/-/spotlight-7.13.2.tgz", + "integrity": "sha512-n8MapJHAJOCjrny4Yiy/SwAZOsa27X/JCGZ4rNwugvduliTa0WLT1KxjSg6Ne7Ys5iu4BlE5KEL6t3/LYDMI6g==", + "dependencies": { + "@mantine/store": "7.13.2" + }, + "peerDependencies": { + "@mantine/core": "7.13.2", + "@mantine/hooks": "7.13.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/store": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/store/-/store-7.13.2.tgz", + "integrity": "sha512-JcBGOqRynYiRWzw1rYdmViB/lfeYSec2EXVdSt4eJv+RPICsjjuqrIc3sNzfqJEGxcN4hGSlaeBriSh05K+vNQ==", + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/@mantine/tiptap": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@mantine/tiptap/-/tiptap-7.13.2.tgz", + "integrity": "sha512-7trWaaGoyLdYdxmiunuAANL/euYjZAv5ZfVq7T/T+5SrvLObrEJicdT62nTmEoI5a7bG4R0lE7v22n4FNydohA==", + "peerDependencies": { + "@mantine/core": "7.13.2", + "@mantine/hooks": "7.13.2", + "@tiptap/extension-link": ">=2.1.12", + "@tiptap/react": ">=2.1.12", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, "node_modules/@next/env": { "version": "14.2.13", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.13.tgz", @@ -4245,10 +4503,9 @@ } }, "node_modules/@tiptap/core": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.7.2.tgz", - "integrity": "sha512-rGAH90LPMR5OIG7vuTDRw8WxDYxPXSxuGtu++mxPF+Bv7V2ijPOy3P1oyV1G3KGoS0pPiNugLh+tVLsElcx/9Q==", - "peer": true, + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.8.0.tgz", + "integrity": "sha512-xsqDI4BNzYRWRtBq7+/38ThhqEr7uG9Njip1x+9/wgR3vWPBFnBkYJTz6jSxS35NRE6BSnERm4/B/vrLuY1Hdw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4257,10 +4514,34 @@ "@tiptap/pm": "^2.7.0" } }, + "node_modules/@tiptap/extension-blockquote": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.8.0.tgz", + "integrity": "sha512-m3CKrOIvV7fY1Ak2gYf5LkKiz6AHxHpg6wxfVaJvdBqXgLyVtHo552N+A4oSHOSRbB4AG9EBQ2NeBM8cdEQ4MA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.8.0.tgz", + "integrity": "sha512-U1YkZBxDkSLNvPNiqxB5g42IeJHr27C7zDb/yGQN2xL4UBeg4O9xVhCFfe32f6tLwivSL0dar4ScElpaCJuqow==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, "node_modules/@tiptap/extension-bubble-menu": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.7.2.tgz", - "integrity": "sha512-U4LjkVDrJZEfWeZai8AYT7GaI6d7gzLm7Z7bSzZ0sH5fOry2qkwLycRDI9YZlM95yaVIVB5GE93lrgDS5mOP3A==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.8.0.tgz", + "integrity": "sha512-swg+myJPN60LduQvLMF4hVBqP5LOIN01INZBzBI8egz8QufqtSyRCgXl7Xcma0RT5xIXnZSG9XOqNFf2rtkjKA==", "dependencies": { "tippy.js": "^6.3.7" }, @@ -4273,10 +4554,49 @@ "@tiptap/pm": "^2.7.0" } }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.8.0.tgz", + "integrity": "sha512-H4O2X0ozbc/ce9/XF1H98sqWVUdtt7jzy7hMBunwmY8ZxI4dHtcRkeg81CZbpKTqOqRrMCLWjE3M2tgiDXrDkA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/extension-list-item": "^2.7.0", + "@tiptap/extension-text-style": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.8.0.tgz", + "integrity": "sha512-VSFn3sFF6qPpOGkXFhik8oYRH5iByVJpFEFd/duIEftmS0MdPzkbSItOpN3mc9xsJ5dCX80LYaResSj5hr5zkA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.8.0.tgz", + "integrity": "sha512-POuA5Igx+Dto0DTazoBFAQTj/M/FCdkqRVD9Uhsxhv49swPyANTJRr05vgbgtHB+NDDsZfCawVh7pI0IAD/O0w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, "node_modules/@tiptap/extension-document": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.7.2.tgz", - "integrity": "sha512-WyMGytHhb3MbNhJ8kUXTx/jHZ9XPaaPRJu1TYdVZNQ4pg7K47qLJ2KMOyLEFy7e5HcJUkYfhRHpyQGHkiu3brg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.8.0.tgz", + "integrity": "sha512-mp7Isx1sVc/ifeW4uW/PexGQ9exN3NRUOebSpnLfqXeWYk4y1RS1PA/3+IHkOPVetbnapgPjFx/DswlCP3XLjA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4285,10 +4605,23 @@ "@tiptap/core": "^2.7.0" } }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.8.0.tgz", + "integrity": "sha512-rAFvx44YuT6dtS1c+ALw0ROAGI16l5L1HxquL4hR1gtxDcTieST5xhw5bkshXlmrlfotZXPrhokzqA7qjhZtJw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, "node_modules/@tiptap/extension-floating-menu": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.7.2.tgz", - "integrity": "sha512-1z/kluQUQP3JmWb7vrC/ERi79lNuQegU6WRptRgSSQhFxeDPQX9CBK3I7HYy9bvSxnIa1/3GrKzL6gfaKU8WDA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.8.0.tgz", + "integrity": "sha512-H4QT61CrkLqisnGGC7zgiYmsl2jXPHl89yQCbdlkQN7aw11H7PltcJS2PJguL0OrRVJS/Mv/VTTUiMslmsEV5g==", "dependencies": { "tippy.js": "^6.3.7" }, @@ -4301,6 +4634,109 @@ "@tiptap/pm": "^2.7.0" } }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.8.0.tgz", + "integrity": "sha512-Be1LWCmvteQInOnNVN+HTqc1XWsj1bCl+Q7et8qqNjtGtTaCbdCp8ppcH1SKJxNTM/RLUtPyJ8FDgOTj51ixCA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.8.0.tgz", + "integrity": "sha512-vqiIfviNiCmy/pJTHuDSCAGL2O4QDEdDmAvGJu8oRmElUrnlg8DbJUfKvn6DWQHNSQwRb+LDrwWlzAYj1K9u6A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.8.0.tgz", + "integrity": "sha512-4inWgrTPiqlivPmEHFOM5ck2UsmOsbKKPtqga6bALvWPmCv24S6/EBwFp8Jz4YABabXDnkviihmGu0LpP9D69w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-history": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.8.0.tgz", + "integrity": "sha512-u5YS0J5Egsxt8TUWMMAC3QhPZaak+IzQeyHch4gtqxftx96tprItY7AD/A3pGDF2uCSnN+SZrk6yVexm6EncDw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.8.0.tgz", + "integrity": "sha512-Sn/MI8WVFBoIYSIHA9NJryJIyCEzZdRysau8pC5TFnfifre0QV1ksPz2bgF+DyCD69ozQiRdBBHDEwKe47ZbfQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.8.0.tgz", + "integrity": "sha512-PwwSE2LTYiHI47NJnsfhBmPiLE8IXZYqaSoNPU6flPrk1KxEzqvRI1joKZBmD9wuqzmHJ93VFIeZcC+kfwi8ZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.8.0.tgz", + "integrity": "sha512-p67hCG/pYCiOK/oCTPZnlkw9Ei7KJ7kCKFaluTcAmr5j8IBdYfDqSMDNCT4vGXBvKFh4X6xD7S7QvOqcH0Gn9A==", + "dependencies": { + "linkifyjs": "^4.1.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.8.0.tgz", + "integrity": "sha512-o7OGymGxB0B9x3x2prp3KBDYFuBYGc5sW69O672jk8G52DqhzzndgPnkk0qUn8nXAUKuDGbJmpmHVA2kagqnRg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, "node_modules/@tiptap/extension-mention": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/@tiptap/extension-mention/-/extension-mention-2.7.2.tgz", @@ -4315,10 +4751,24 @@ "@tiptap/suggestion": "^2.7.0" } }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.8.0.tgz", + "integrity": "sha512-sCvNbcTS1+5QTTXwUPFa10vf5I1pr8sGcOTIh0G+a5ZkS5+6FxT12k7VLzPt39QyNbOi+77U2o4Xr4XyaEkfSg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/extension-list-item": "^2.7.0", + "@tiptap/extension-text-style": "^2.7.0" + } + }, "node_modules/@tiptap/extension-paragraph": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.7.2.tgz", - "integrity": "sha512-yMzUGNojNv0lLEE+38GOpgRI327EyEZK/uEHlyzbjAWRvqE6aZ+oEB4JUuoJXX2Ad9gwN16dGHnxL//ieTxrkQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.8.0.tgz", + "integrity": "sha512-XgxxNNbuBF48rAGwv7/s6as92/xjm/lTZIGTq9aG13ClUKFtgdel7C33SpUCcxg3cO2WkEyllXVyKUiauFZw/A==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4340,10 +4790,35 @@ "@tiptap/pm": "^2.7.0" } }, + "node_modules/@tiptap/extension-strike": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.8.0.tgz", + "integrity": "sha512-ezkDiXxQ3ME/dDMMM7tAMkKRi6UWw7tIu+Mx7Os0z8HCGpVBk1gFhLlhEd8I5rJaPZr4tK1wtSehMA9bscFGQw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, "node_modules/@tiptap/extension-text": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.7.2.tgz", - "integrity": "sha512-VjzG7W53Lx2q8XV0rUHetVTQWDK28XTCTW3IzxYxHp2joB/k9q3xgE/5Vs+7DOLSHIKq2BmwQNyaE+XjUF5iYQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.8.0.tgz", + "integrity": "sha512-EDAdFFzWOvQfVy7j3qkKhBpOeE5thkJaBemSWfXI93/gMVc0ZCdLi24mDvNNgUHlT+RjlIoQq908jZaaxLKN2A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-text-style": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.8.0.tgz", + "integrity": "sha512-jJp0vcZ2Ty7RvIL0VU6dm1y+fTfXq1lN2GwtYzYM0ueFuESa+Qo8ticYOImyWZ3wGJGVrjn7OV9r0ReW0/NYkQ==", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4353,9 +4828,9 @@ } }, "node_modules/@tiptap/pm": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.7.2.tgz", - "integrity": "sha512-RiRPlwpuE6IHDJytE0tglbFlWELOaqeyGRGv25wBTjzV1plnqC5B3U65XY/8kKuuLjdd3NpRfR68DXBafusSBg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.8.0.tgz", + "integrity": "sha512-eMGpRooUMvKz/vOpnKKppApMSoNM325HxTdAJvTlVAmuHp5bOY5kyY1kfUlePRiVx1t1UlFcXs3kecFwkkBD3Q==", "dependencies": { "prosemirror-changeset": "^2.2.1", "prosemirror-collab": "^1.3.1", @@ -4382,12 +4857,12 @@ } }, "node_modules/@tiptap/react": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.7.2.tgz", - "integrity": "sha512-4d1TQMRHtqagCarty1iQIJMLYZ2YzBHBgAt4HlPeoDUI4MHZutOIdkl1UXmxZwh/d5p6a+tZGTVOV4aKXMW2Iw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.8.0.tgz", + "integrity": "sha512-o/aSCjO5Nu4MsNpTF+N1SzYzVQvvBiclmTOZX2E6usZ8jre5zmKfXHDSZnjGSRTK6z6kw5KW8wpjRQha03f9mg==", "dependencies": { - "@tiptap/extension-bubble-menu": "^2.7.2", - "@tiptap/extension-floating-menu": "^2.7.2", + "@tiptap/extension-bubble-menu": "^2.8.0", + "@tiptap/extension-floating-menu": "^2.8.0", "@types/use-sync-external-store": "^0.0.6", "fast-deep-equal": "^3", "use-sync-external-store": "^1.2.2" @@ -4403,6 +4878,37 @@ "react-dom": "^17.0.0 || ^18.0.0" } }, + "node_modules/@tiptap/starter-kit": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.8.0.tgz", + "integrity": "sha512-r7UwaTrECkQoheWVZKFDqtL5tBx07x7IFT+prfgnsVlYFutGWskVVqzCDvD3BDmrg5PzeCWYZrQGlPaLib7tjg==", + "dependencies": { + "@tiptap/core": "^2.8.0", + "@tiptap/extension-blockquote": "^2.8.0", + "@tiptap/extension-bold": "^2.8.0", + "@tiptap/extension-bullet-list": "^2.8.0", + "@tiptap/extension-code": "^2.8.0", + "@tiptap/extension-code-block": "^2.8.0", + "@tiptap/extension-document": "^2.8.0", + "@tiptap/extension-dropcursor": "^2.8.0", + "@tiptap/extension-gapcursor": "^2.8.0", + "@tiptap/extension-hard-break": "^2.8.0", + "@tiptap/extension-heading": "^2.8.0", + "@tiptap/extension-history": "^2.8.0", + "@tiptap/extension-horizontal-rule": "^2.8.0", + "@tiptap/extension-italic": "^2.8.0", + "@tiptap/extension-list-item": "^2.8.0", + "@tiptap/extension-ordered-list": "^2.8.0", + "@tiptap/extension-paragraph": "^2.8.0", + "@tiptap/extension-strike": "^2.8.0", + "@tiptap/extension-text": "^2.8.0", + "@tiptap/pm": "^2.8.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, "node_modules/@tiptap/suggestion": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/@tiptap/suggestion/-/suggestion-2.7.2.tgz", @@ -4416,6 +4922,60 @@ "@tiptap/pm": "^2.7.0" } }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -5491,6 +6051,116 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -5556,6 +6226,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -5572,6 +6247,11 @@ } } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "node_modules/decode-named-character-reference": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", @@ -5731,11 +6411,36 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/embla-carousel": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-7.1.0.tgz", + "integrity": "sha512-Bh8Pa8NWzgugLkf8sAGexQlBCNDFaej5BXiKgQdRJ1mUC9NWBrw9Z23YVPVGkguWoz5LMjZXXFVGCobl3UPt/Q==" + }, + "node_modules/embla-carousel-react": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-7.1.0.tgz", + "integrity": "sha512-tbYRPRZSDNd2QLNqYDcArAakGIxtUbhS7tkP0dGXktXHGgcX+3ji3VrOUTOftBiujZrMV8kRxtrRUe/1soloIQ==", + "dependencies": { + "embla-carousel": "7.1.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.1 || ^18.0.0" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -6407,6 +7112,14 @@ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==" }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -7183,6 +7896,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/intl-messageformat": { "version": "10.5.14", "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz", @@ -7767,6 +8488,14 @@ "json-buffer": "3.0.1" } }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "engines": { + "node": ">= 8" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -7819,6 +8548,11 @@ "uc.micro": "^2.0.0" } }, + "node_modules/linkifyjs": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.3.tgz", + "integrity": "sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -11575,6 +12309,28 @@ "url": "https://github.com/sponsors/antonk52" } }, + "node_modules/postcss-mixins": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/postcss-mixins/-/postcss-mixins-9.0.4.tgz", + "integrity": "sha512-XVq5jwQJDRu5M1XGkdpgASqLk37OqkH4JCFDXl/Dn7janOJjCTEKL+36cnRVy7bMtoBzALfO7bV7nTIsFnUWLA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "postcss-js": "^4.0.0", + "postcss-simple-vars": "^7.0.0", + "sugarss": "^4.0.1" + }, + "engines": { + "node": ">=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, "node_modules/postcss-nested": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", @@ -11599,6 +12355,19 @@ "postcss": "^8.2.14" } }, + "node_modules/postcss-preset-mantine": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/postcss-preset-mantine/-/postcss-preset-mantine-1.17.0.tgz", + "integrity": "sha512-ji1PMDBUf2Vsx/HE5faMSs1+ff6qE6YRulTr4Ja+6HD3gop8rSMTCYdpN7KrdsEg079kfBKkO/PaKhG9uR0zwQ==", + "dev": true, + "dependencies": { + "postcss-mixins": "^9.0.4", + "postcss-nested": "^6.0.1" + }, + "peerDependencies": { + "postcss": ">=8.0.0" + } + }, "node_modules/postcss-selector-parser": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", @@ -11611,6 +12380,22 @@ "node": ">=4" } }, + "node_modules/postcss-simple-vars": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz", + "integrity": "sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==", + "dev": true, + "engines": { + "node": ">=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.1" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -11655,7 +12440,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -11946,6 +12730,20 @@ "react": "^18.3.1" } }, + "node_modules/react-dropzone-esm": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/react-dropzone-esm/-/react-dropzone-esm-15.0.1.tgz", + "integrity": "sha512-RdeGpqwHnoV/IlDFpQji7t7pTtlC2O1i/Br0LWkRZ9hYtLyce814S71h5NolnCZXsIN5wrZId6+8eQj2EBnEzg==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, "node_modules/react-fast-compare": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", @@ -11954,8 +12752,7 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-markdown": { "version": "9.0.1", @@ -11982,6 +12779,15 @@ "react": ">=18" } }, + "node_modules/react-number-format": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.2.tgz", + "integrity": "sha512-cg//jVdS49PYDgmcYoBnMMHl4XNTMuV723ZnHD2aXYtWWWqbVF3hjQ8iB+UZEuXapLbeA8P8H+1o6ZB1lcw3vg==", + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-popper": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", @@ -12055,6 +12861,20 @@ } } }, + "node_modules/react-smooth": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", @@ -12121,6 +12941,21 @@ "react-dom": ">=16.14.0" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -12148,6 +12983,49 @@ "node": ">=8.10.0" } }, + "node_modules/recharts": { + "version": "2.12.7", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.7.tgz", + "integrity": "sha512-hlLJMhPQfv4/3NBSAyq3gzGg4h2v69RJh6KU7b3pXYNNAELs9kEoXOjbkxdXpALqKBoVmVptGfLpxdaVYqjmXQ==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^16.10.2", + "react-smooth": "^4.0.0", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/recharts/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -12913,6 +13791,22 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/sugarss": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-4.0.1.tgz", + "integrity": "sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, "node_modules/supabase": { "version": "1.192.5", "resolved": "https://registry.npmjs.org/supabase/-/supabase-1.192.5.tgz", @@ -12954,6 +13848,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/tailwind-merge": { "version": "1.14.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz", @@ -13064,6 +13963,11 @@ "node": ">=0.8" } }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, "node_modules/tippy.js": { "version": "6.3.7", "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", @@ -13494,6 +14398,27 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", diff --git a/agenthub/package.json b/agenthub/package.json index ca60ae62..627f55b5 100644 --- a/agenthub/package.json +++ b/agenthub/package.json @@ -10,6 +10,19 @@ "postinstall": "prisma generate" }, "dependencies": { + "@mantine/carousel": "^7.13.2", + "@mantine/charts": "^7.13.2", + "@mantine/code-highlight": "^7.13.2", + "@mantine/core": "^7.13.2", + "@mantine/dates": "^7.13.2", + "@mantine/dropzone": "^7.13.2", + "@mantine/form": "^7.13.2", + "@mantine/hooks": "^7.13.2", + "@mantine/modals": "^7.13.2", + "@mantine/notifications": "^7.13.2", + "@mantine/nprogress": "^7.13.2", + "@mantine/spotlight": "^7.13.2", + "@mantine/tiptap": "^7.13.2", "@nextui-org/react": "^2.4.8", "@prisma/client": "^5.19.1", "@radix-ui/react-avatar": "^1.1.0", @@ -17,18 +30,22 @@ "@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-scroll-area": "^1.1.0", "@tiptap/extension-document": "^2.7.2", + "@tiptap/extension-link": "^2.8.0", "@tiptap/extension-mention": "^2.7.2", "@tiptap/extension-paragraph": "^2.7.2", "@tiptap/extension-placeholder": "^2.7.4", "@tiptap/extension-text": "^2.7.2", - "@tiptap/pm": "^2.2.4", - "@tiptap/react": "^2.7.2", + "@tiptap/pm": "^2.8.0", + "@tiptap/react": "^2.8.0", + "@tiptap/starter-kit": "^2.8.0", "@tiptap/suggestion": "^2.7.2", "axios": "^1.7.7", "class-variance-authority": "^0.7.0", "classnames": "^2.5.1", "common": "^0.2.5", "copy-to-clipboard": "^3.3.3", + "dayjs": "^1.11.13", + "embla-carousel-react": "^7.1.0", "framer-motion": "^11.5.5", "install": "^0.13.0", "lodash": "^4.17.21", @@ -45,6 +62,7 @@ "react-quill-patched": "^3.0.4", "react-syntax-highlighter": "^15.5.0", "react-tooltip": "^5.28.0", + "recharts": "^2.12.7", "sonner": "^1.5.0", "supabase": "^1.192.5", "ui": "*" @@ -56,7 +74,9 @@ "@types/react-syntax-highlighter": "^15.5.13", "eslint": "^8", "eslint-config-next": "14.2.8", - "postcss": "^8", + "postcss": "^8.4.47", + "postcss-preset-mantine": "^1.17.0", + "postcss-simple-vars": "^7.0.1", "tailwindcss": "^3.4.1", "typescript": "^5" } diff --git a/agenthub/postcss.config.mjs b/agenthub/postcss.config.mjs index 1a69fd2a..af9c9825 100644 --- a/agenthub/postcss.config.mjs +++ b/agenthub/postcss.config.mjs @@ -3,6 +3,16 @@ const config = { plugins: { tailwindcss: {}, }, + 'postcss-preset-mantine': {}, + 'postcss-simple-vars': { + variables: { + 'mantine-breakpoint-xs': '36em', + 'mantine-breakpoint-sm': '48em', + 'mantine-breakpoint-md': '62em', + 'mantine-breakpoint-lg': '75em', + 'mantine-breakpoint-xl': '88em', + }, + }, }; export default config; From d439b369bcbd032b661db0ae5362b4255b889cda Mon Sep 17 00:00:00 2001 From: Balaji R Date: Sat, 12 Oct 2024 09:47:58 -0400 Subject: [PATCH 2/8] feat: better chat --- agenthub/app/agentchat/page.tsx | 238 ++++++++++++++++++++++++++++++++ agenthub/app/layout.tsx | 2 +- agenthub/tailwind.config.ts | 10 ++ 3 files changed, 249 insertions(+), 1 deletion(-) diff --git a/agenthub/app/agentchat/page.tsx b/agenthub/app/agentchat/page.tsx index e69de29b..3de9d7c4 100644 --- a/agenthub/app/agentchat/page.tsx +++ b/agenthub/app/agentchat/page.tsx @@ -0,0 +1,238 @@ +'use client' + +import React, { useState, useRef, useEffect } from 'react'; +import { TextInput, ActionIcon, Switch, useMantineTheme, Button } from '@mantine/core'; +import { Send, Sun, Moon, Plus, Hash, MessageCircle } from 'lucide-react'; + +interface Message { + id: number; + text: string; + sender: 'user' | 'bot'; + timestamp: Date; +} + +interface HeaderProps { + darkMode: boolean; + setDarkMode: React.Dispatch>; +} + +interface MessageBubbleProps { + message: Message; + darkMode: boolean; + index: number; +} + +interface MessageListProps { + messages: Message[]; + darkMode: boolean; +} + +interface InputAreaProps { + input: string; + setInput: React.Dispatch>; + handleSend: () => void; + darkMode: boolean; +} + + +interface Chat { + id: number; + name: string; +} + +interface SidebarProps { + chats: Chat[]; + activeChat: number; + setActiveChat: (id: number) => void; + addChat: () => void; + darkMode: boolean; +} + +const Sidebar: React.FC = ({ chats, activeChat, setActiveChat, addChat, darkMode }) => ( +
+ +
+ {chats.map((chat) => ( + + ))} +
+
+); + +const Header: React.FC = ({ darkMode, setDarkMode }) => { + const theme = useMantineTheme(); + return ( +
+
+ +

General

+
+
+ + setDarkMode(event.currentTarget.checked)} + size="md" + color={theme.primaryColor} + /> + +
+
+ ); +}; + +const MessageBubble: React.FC = ({ message, darkMode, index }) => ( +
+
+ {message.sender === 'user' ? 'U' : 'B'} +
+
+
+ + {message.sender === 'user' ? 'User' : 'Bot'} + + + {message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + +
+

{message.text}

+
+
+); + +const MessageList: React.FC = ({ messages, darkMode }) => ( +
+
+ {messages.map((message, index) => ( + + ))} +
+
+); + +const InputArea: React.FC = ({ input, setInput, handleSend, darkMode }) => { + const theme = useMantineTheme(); + return ( +
+
+ + + + setInput(event.currentTarget.value)} + onKeyPress={(event) => event.key === 'Enter' && handleSend()} + className="flex-grow" + styles={(theme) => ({ + input: { + backgroundColor: darkMode ? theme.colors.gray[7] : theme.white, + color: darkMode ? theme.white : theme.black, + border: 'none', + borderRadius: '9999px', + padding: '10px 16px', + '&::placeholder': { + color: darkMode ? theme.colors.gray[5] : theme.colors.gray[6], + }, + }, + rightSection: { + width: '40px', + }, + })} + rightSection={ + + + + } + rightSectionWidth={40} + /> +
+
+ ); +}; + +const ChatInterface: React.FC = () => { + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(''); + const [darkMode, setDarkMode] = useState(false); + const [chats, setChats] = useState([{ id: 1, name: 'General' }]); + const [activeChat, setActiveChat] = useState(1); + const messagesEndRef = useRef(null); + + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [messages]); + + const handleSend = () => { + if (input.trim()) { + const newMessage: Message = { id: Date.now(), text: input, sender: 'user', timestamp: new Date() }; + setMessages([...messages, newMessage]); + setInput(''); + setTimeout(() => { + const botMessage: Message = { id: Date.now(), text: 'This is a bot response.', sender: 'bot', timestamp: new Date() }; + setMessages(prevMessages => [...prevMessages, botMessage]); + }, 1000); + } + }; + + const addChat = () => { + const newChat: Chat = { id: Date.now(), name: `Chat ${chats.length + 1}` }; + setChats([...chats, newChat]); + setActiveChat(newChat.id); + }; + + return ( +
+ +
+
+ + +
+
+ ); +}; + +export default ChatInterface; \ No newline at end of file diff --git a/agenthub/app/layout.tsx b/agenthub/app/layout.tsx index c70aa731..e8e8a5d3 100644 --- a/agenthub/app/layout.tsx +++ b/agenthub/app/layout.tsx @@ -86,7 +86,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) style={{ fontSize: 16 }} >
- + {/* */} {/*
*/} {children}
diff --git a/agenthub/tailwind.config.ts b/agenthub/tailwind.config.ts index 4779b445..1df05ffb 100644 --- a/agenthub/tailwind.config.ts +++ b/agenthub/tailwind.config.ts @@ -14,7 +14,17 @@ const config: Config = { background: "var(--background)", foreground: "var(--foreground)", }, + keyframes: { + slideIn: { + '0%': { transform: 'translateY(20px)', opacity: '0' }, + '100%': { transform: 'translateY(0)', opacity: '1' }, + } + }, + animation: { + slideIn: 'slideIn 0.3s ease-out forwards', + } }, + }, darkMode: "class", plugins: [nextui()], From 839bd2ee401adb4fea3c849bb9b3f0c8ef0a818c Mon Sep 17 00:00:00 2001 From: BRama10 Date: Sat, 12 Oct 2024 17:41:10 -0400 Subject: [PATCH 3/8] file uploads --- agenthub/app/agentchat/page.tsx | 84 +++--- agenthub/components/chat/editor/Editor.tsx | 293 ++++++++++++++------- agenthub/interfaces/agentchat/index.ts | 42 +++ 3 files changed, 277 insertions(+), 142 deletions(-) create mode 100644 agenthub/interfaces/agentchat/index.ts diff --git a/agenthub/app/agentchat/page.tsx b/agenthub/app/agentchat/page.tsx index 3de9d7c4..b232eece 100644 --- a/agenthub/app/agentchat/page.tsx +++ b/agenthub/app/agentchat/page.tsx @@ -3,50 +3,10 @@ import React, { useState, useRef, useEffect } from 'react'; import { TextInput, ActionIcon, Switch, useMantineTheme, Button } from '@mantine/core'; import { Send, Sun, Moon, Plus, Hash, MessageCircle } from 'lucide-react'; +import { SidebarProps, HeaderProps, MessageBubbleProps, MessageListProps, InputAreaProps, Message, Chat } from '@/interfaces/agentchat'; +import { ChatEditor } from '@/components/chat/editor/Editor'; +import { useMounted } from '@/lib/mounted'; -interface Message { - id: number; - text: string; - sender: 'user' | 'bot'; - timestamp: Date; -} - -interface HeaderProps { - darkMode: boolean; - setDarkMode: React.Dispatch>; -} - -interface MessageBubbleProps { - message: Message; - darkMode: boolean; - index: number; -} - -interface MessageListProps { - messages: Message[]; - darkMode: boolean; -} - -interface InputAreaProps { - input: string; - setInput: React.Dispatch>; - handleSend: () => void; - darkMode: boolean; -} - - -interface Chat { - id: number; - name: string; -} - -interface SidebarProps { - chats: Chat[]; - activeChat: number; - setActiveChat: (id: number) => void; - addChat: () => void; - darkMode: boolean; -} const Sidebar: React.FC = ({ chats, activeChat, setActiveChat, addChat, darkMode }) => (
@@ -199,17 +159,30 @@ const ChatInterface: React.FC = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); - const handleSend = () => { - if (input.trim()) { - const newMessage: Message = { id: Date.now(), text: input, sender: 'user', timestamp: new Date() }; - setMessages([...messages, newMessage]); - setInput(''); - setTimeout(() => { - const botMessage: Message = { id: Date.now(), text: 'This is a bot response.', sender: 'bot', timestamp: new Date() }; - setMessages(prevMessages => [...prevMessages, botMessage]); - }, 1000); + const handleSend = (content: string, attachments: File[]) => { + if (content.trim() || attachments.length > 0) { + const newMessage: Message = { + id: Date.now(), + text: content, + sender: 'user', + timestamp: new Date(), + attachments: attachments.map(file => file.name), // Store file names or URLs + }; + setMessages([...messages, newMessage]); + + // Handle file uploads here (e.g., to a server) + + setTimeout(() => { + const botMessage: Message = { + id: Date.now(), + text: 'This is a bot response.', + sender: 'bot', + timestamp: new Date(), + }; + setMessages(prevMessages => [...prevMessages, botMessage]); + }, 1000); } - }; + }; const addChat = () => { const newChat: Chat = { id: Date.now(), name: `Chat ${chats.length + 1}` }; @@ -217,6 +190,8 @@ const ChatInterface: React.FC = () => { setActiveChat(newChat.id); }; + const mounted = useMounted(); + return (
{
- + {/* */} + {mounted && }
); diff --git a/agenthub/components/chat/editor/Editor.tsx b/agenthub/components/chat/editor/Editor.tsx index f5643294..249b824d 100644 --- a/agenthub/components/chat/editor/Editor.tsx +++ b/agenthub/components/chat/editor/Editor.tsx @@ -1,102 +1,219 @@ 'use client' -import Document from "@tiptap/extension-document"; -import Mention from "@tiptap/extension-mention"; -import Paragraph from "@tiptap/extension-paragraph"; -import Placeholder from "@tiptap/extension-placeholder" -import Text from "@tiptap/extension-text"; -import { EditorContent, ReactRenderer, useEditor } from "@tiptap/react"; -import React, { useEffect, useState } from "react"; -import { MentionList } from "./MentionList"; -// import StarterKit from '@tiptap/starter-kit' -import dynamic from "next/dynamic"; +import React, { useCallback, useState, useEffect, useRef } from 'react'; +import { useEditor, EditorContent } from '@tiptap/react'; +import Document from '@tiptap/extension-document'; +import Paragraph from '@tiptap/extension-paragraph'; +import Text from '@tiptap/extension-text'; +import HardBreak from '@tiptap/extension-hard-break'; +import Placeholder from '@tiptap/extension-placeholder'; +import { ActionIcon, Group, Paper, Text as MantineText, useMantineTheme, ScrollArea, Image, Box, Overlay } from '@mantine/core'; +import { Send, Plus, X, FileIcon } from 'lucide-react'; -export interface EditorProps { - callback: (s: string) => void +export interface ChatEditorProps { + onSend: (content: string, attachments: File[]) => void; + darkMode: boolean; } -export const Editor: React.FC = ({ - callback -}) => { - const handleChange = (s: string) => { - console.log('handling', s) - callback(s); - } - - const editor = useEditor({ - editorProps: { attributes: { class: "editor" } }, - immediatelyRender: false, - extensions: [ - Document, - Paragraph.configure({ - HTMLAttributes: { class: "paragraph" } - }), - Text, - // StarterKit, - Mention.configure({ - HTMLAttributes: { class: "mentionNode" }, - suggestion: { - render: () => { - //@ts-ignore - let reactRenderer: ReactRenderer; +export const ChatEditor: React.FC = ({ onSend, darkMode }) => { + const [attachments, setAttachments] = useState([]); + const [previews, setPreviews] = useState([]); + const [hoverIndex, setHoverIndex] = useState(null); + const fileInputRef = useRef(null); + const theme = useMantineTheme(); - return { - onStart: (props: any) => { - reactRenderer = new ReactRenderer(MentionList, { - props, - editor: props.editor - }); - }, + const editor = useEditor({ + extensions: [ + Document, + Paragraph, + Text, + HardBreak.configure({ + HTMLAttributes: { + class: 'my-2', + }, + }), + Placeholder.configure({ + placeholder: 'Type a message...', + }), + ], + editorProps: { + attributes: { + class: `focus:outline-none p-2 min-h-[40px] max-h-[300px] overflow-y-auto ${ + darkMode ? 'text-white' : 'text-gray-800' + }`, + }, + }, + }); - onUpdate(props: any) { - reactRenderer?.updateProps(props); - }, + const handleSend = useCallback(() => { + if (editor) { + const content = editor.getHTML(); + if (content.trim() !== '

' || attachments.length > 0) { + onSend(content, attachments); + editor.commands.setContent(''); + setAttachments([]); + setPreviews([]); + } + } + }, [editor, attachments, onSend]); - onKeyDown(props: any) { - if (props.event.key === "Escape") { - reactRenderer?.destroy(); - return true; - } + const handleAttachment = useCallback((event: React.ChangeEvent) => { + const files = event.target.files; + if (files) { + const newAttachments = Array.from(files); + setAttachments(prev => [...prev, ...newAttachments]); + + newAttachments.forEach(file => { + if (file.type.startsWith('image/')) { + const reader = new FileReader(); + reader.onload = (e) => { + setPreviews(prev => [...prev, e.target?.result as string]); + }; + reader.readAsDataURL(file); + } else { + setPreviews(prev => [...prev, '']); + } + }); + } + }, []); - return (reactRenderer?.ref as any)?.onKeyDown(props); - }, + const removeAttachment = useCallback((index: number) => { + setAttachments(prev => prev.filter((_, i) => i !== index)); + setPreviews(prev => prev.filter((_, i) => i !== index)); + setHoverIndex(null); // Reset hover state after removal + }, []); - onExit() { - reactRenderer.destroy(); - } - }; - } - } - }), - Placeholder.configure({ - // Use a placeholder: - placeholder: '@ an agent to talk to AIOS...', - - // Use different placeholders depending on the node type: - // placeholder: ({ node }) => { - // if (node.type.name === 'heading') { - // return 'What’s the title?' - // } - - // return 'Can you add some further context?' - // }, - }), - ], - onUpdate: ({ editor }) => { - handleChange(editor.getText()); - }, - }); + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; - const [isMounted, setIsMounted] = useState(false) + editor?.view.dom.addEventListener('keydown', handleKeyDown); - useEffect(() => { - setIsMounted(true) - }, []) + return () => { + editor?.view.dom.removeEventListener('keydown', handleKeyDown); + }; + }, [editor, handleSend]); - if (!isMounted) { - return null // or a loading placeholder - } + const openFileInput = () => { + fileInputRef.current?.click(); + }; - return ; - // return
-}; + return ( + + {attachments.length > 0 && ( + + + {attachments.map((file, index) => ( + setHoverIndex(index)} + onMouseLeave={() => setHoverIndex(null)} + > + {previews[index] ? ( + {file.name} + ) : ( +
+ + + {file.name} + +
+ )} + {hoverIndex === index && ( + + { + console.log('hi') + e.stopPropagation(); // Prevent event bubbling + removeAttachment(index); + }} + > + + + + )} +
+ ))} +
+
+ )} +
+ + + + + + + + 0 ? theme.colors[theme.primaryColor][6] : 'transparent', + color: (editor && editor.getText().trim() !== '') || attachments.length > 0 ? 'white' : theme.colors.gray[5], + boxShadow: (editor && editor.getText().trim() !== '') || attachments.length > 0 ? '0 2px 4px rgba(0,0,0,0.2)' : 'none', + }} + > + + +
+
+ ); +}; \ No newline at end of file diff --git a/agenthub/interfaces/agentchat/index.ts b/agenthub/interfaces/agentchat/index.ts new file mode 100644 index 00000000..3235a3af --- /dev/null +++ b/agenthub/interfaces/agentchat/index.ts @@ -0,0 +1,42 @@ +export interface Message { + id: number; + text: string; + sender: 'user' | 'bot'; + timestamp: Date; + attachments?: string[]; // Array of file names or URLs + } +export interface HeaderProps { + darkMode: boolean; + setDarkMode: React.Dispatch>; +} + +export interface MessageBubbleProps { + message: Message; + darkMode: boolean; + index: number; +} + +export interface MessageListProps { + messages: Message[]; + darkMode: boolean; +} + +export interface InputAreaProps { + input: string; + setInput: React.Dispatch>; + handleSend: () => void; + darkMode: boolean; +} + +export interface Chat { + id: number; + name: string; +} + +export interface SidebarProps { + chats: Chat[]; + activeChat: number; + setActiveChat: (id: number) => void; + addChat: () => void; + darkMode: boolean; +} \ No newline at end of file From 8f8581f2bd83e96fe05b3760a18aa539c9dbd8a7 Mon Sep 17 00:00:00 2001 From: BRama10 Date: Sat, 12 Oct 2024 20:56:15 -0400 Subject: [PATCH 4/8] feat: loaders+other stuff --- agenthub/app/agentchat/_page.tsx | 278 +++++++++++++++++++++++++ agenthub/app/agentchat/page.tsx | 200 ++++++++++-------- agenthub/interfaces/agentchat/index.ts | 3 +- agenthub/package-lock.json | 146 +++++++++++++ agenthub/package.json | 1 + 5 files changed, 543 insertions(+), 85 deletions(-) create mode 100644 agenthub/app/agentchat/_page.tsx diff --git a/agenthub/app/agentchat/_page.tsx b/agenthub/app/agentchat/_page.tsx new file mode 100644 index 00000000..6c5b9e98 --- /dev/null +++ b/agenthub/app/agentchat/_page.tsx @@ -0,0 +1,278 @@ +'use client' + +import React, { useState, useRef, useEffect } from 'react'; +import { ChatEditor } from '@/components/chat/editor/Editor'; +import { useMounted } from '@/lib/mounted'; + +import { TextInput, ActionIcon, Switch, useMantineTheme, Button, CopyButton, Loader } from '@mantine/core'; +import { Send, Sun, Moon, Plus, Hash, MessageCircle, Clipboard, Check } from 'lucide-react'; +import { SidebarProps, HeaderProps, MessageBubbleProps, MessageListProps, InputAreaProps, Message, Chat } from '@/interfaces/agentchat'; +import ReactMarkdown from 'react-markdown'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; +import rehypeRaw from 'rehype-raw'; + + +const Sidebar: React.FC = ({ chats, activeChat, setActiveChat, addChat, darkMode }) => ( +
+ +
+ {chats.map((chat) => ( + + ))} +
+
+); + +const Header: React.FC = ({ darkMode, setDarkMode }) => { + const theme = useMantineTheme(); + return ( +
+
+ +

General

+
+
+ + setDarkMode(event.currentTarget.checked)} + size="md" + color={theme.primaryColor} + /> + +
+
+ ); +}; + + + +// Improved Markdown component +interface MarkdownProps { + content: string; + darkMode: boolean; +} + +const Markdown: React.FC = ({ content, darkMode }) => { + console.log(typeof content) + console.log(content) + return ( + + {children} + + ) : ( +
+
+ {match ? match[1] : 'text'} + + {({ copied, copy }) => ( + + {copied ? : } + + )} + +
+ + {String(children).replace(/\n$/, '')} + +
+ ); + }, + p: ({ children, ...props }) =>

{children}

, + br: ({ ...props }) =>
, + ul: ({ children }) =>
    {children}
, + ol: ({ children }) =>
    {children}
, + li: ({ children }) =>
  • {children}
  • , + h1: ({ children }) =>

    {children}

    , + h2: ({ children }) =>

    {children}

    , + h3: ({ children }) =>

    {children}

    , + blockquote: ({ children }) => ( +
    {children}
    + ), + }} + > + {content} +
    + ); +}; + +// Updated MessageBubble component +const MessageBubble: React.FC = ({ message, darkMode, index, isThinking = false }) => ( +
    +
    + {message.sender === 'user' ? 'U' : 'B'} +
    +
    +
    + + {message.sender === 'user' ? 'User' : 'Bot'} + + + {message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + +
    +
    + {isThinking ? ( +
    + + Agent is thinking... +
    + ) : ( + + )} +
    +
    +
    + ); + + const MessageList: React.FC = ({ messages, darkMode }) => ( +
    +
    + {messages.map((message, index) => ( + + ))} +
    +
    + ); + +const ChatInterface: React.FC = () => { + const [messages, setMessages] = useState([]); + const [darkMode, setDarkMode] = useState(false); + const [chats, setChats] = useState([{ id: 1, name: 'General' }]); + const [activeChat, setActiveChat] = useState(1); + const messagesEndRef = useRef(null); + + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [messages]); + + const handleSend = (content: string, attachments: File[]) => { + if (content.trim() || attachments.length > 0) { + const newMessage: Message = { + id: Date.now(), + text: content, + sender: 'user', + timestamp: new Date(), + attachments: attachments.map(file => file.name), + }; + setMessages([...messages, newMessage]); + + // Immediately add a "thinking" message for the bot + // const thinkingMessage: Message = { + // id: Date.now() + 1, + // text: '', + // sender: 'bot', + // timestamp: new Date(), + // }; + // setMessages(prevMessages => [...prevMessages, thinkingMessage]); + + const noInterpolation = (strings: any, ...values: any) => strings.join(''); + + // Simulate bot response after a delay + setTimeout(() => { + const botMessage: Message = { + id: 1, + text: noInterpolation`Here's a sample response with Markdown: + + # Heading 1 + ## Heading 2 + + - List item 1 + - List item 2 + + \`\`\`python + def greet(name): + print(f"Hello, {name}!") + + greet("World") + \`\`\` + + > This is a blockquote. + + **Bold text** and *italic text*.`, + sender: 'bot', + timestamp: new Date(), + }; + // setMessages(prevMessages => + // prevMessages.map(msg => msg.id === thinkingMessage.id ? botMessage : msg) + // ); + setMessages(prevMessages => [...prevMessages, botMessage]); + + }, 3000); // Increased delay to 3 seconds to make the loading state more noticeable + } + }; + + const addChat = () => { + const newChat: Chat = { id: Date.now(), name: `Chat ${chats.length + 1}` }; + setChats([...chats, newChat]); + setActiveChat(newChat.id); + }; + + const mounted = useMounted(); + + return ( +
    + +
    +
    + + {mounted && } +
    +
    +
    + ); +}; + + +export default ChatInterface; \ No newline at end of file diff --git a/agenthub/app/agentchat/page.tsx b/agenthub/app/agentchat/page.tsx index b232eece..4f03cff8 100644 --- a/agenthub/app/agentchat/page.tsx +++ b/agenthub/app/agentchat/page.tsx @@ -1,12 +1,17 @@ 'use client' import React, { useState, useRef, useEffect } from 'react'; -import { TextInput, ActionIcon, Switch, useMantineTheme, Button } from '@mantine/core'; -import { Send, Sun, Moon, Plus, Hash, MessageCircle } from 'lucide-react'; -import { SidebarProps, HeaderProps, MessageBubbleProps, MessageListProps, InputAreaProps, Message, Chat } from '@/interfaces/agentchat'; import { ChatEditor } from '@/components/chat/editor/Editor'; import { useMounted } from '@/lib/mounted'; +import { TextInput, ActionIcon, Switch, useMantineTheme, Button, CopyButton } from '@mantine/core'; +import { Send, Sun, Moon, Plus, Hash, MessageCircle, Clipboard, Check } from 'lucide-react'; +import { SidebarProps, HeaderProps, MessageBubbleProps, MessageListProps, InputAreaProps, Message, Chat } from '@/interfaces/agentchat'; +import ReactMarkdown from 'react-markdown'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; +import rehypeRaw from 'rehype-raw'; + const Sidebar: React.FC = ({ chats, activeChat, setActiveChat, addChat, darkMode }) => (
    @@ -27,8 +32,8 @@ const Sidebar: React.FC = ({ chats, activeChat, setActiveChat, add onClick={() => setActiveChat(chat.id)} leftSection={} className={`justify-start ${chat.id === activeChat - ? (darkMode ? 'bg-gray-600' : 'bg-gray-400') - : (darkMode ? 'hover:bg-gray-700' : 'hover:bg-gray-300') + ? (darkMode ? 'bg-gray-600' : 'bg-gray-400') + : (darkMode ? 'hover:bg-gray-700' : 'hover:bg-gray-300') }`} > {chat.name} @@ -60,11 +65,71 @@ const Header: React.FC = ({ darkMode, setDarkMode }) => { ); }; + + +// Improved Markdown component +interface MarkdownProps { + content: string; + darkMode: boolean; +} + +const Markdown: React.FC = ({ content, darkMode }) => { + return ( + + {children} + + ) : ( +
    +
    + {match ? match[1] : 'text'} + + {({ copied, copy }) => ( + + {copied ? : } + + )} + +
    + + {String(children).replace(/\n$/, '')} + +
    + ); + }, + p: ({ children, ...props }) =>

    {children}

    , + br: ({ ...props }) =>
    , + ul: ({ children }) =>
      {children}
    , + ol: ({ children }) =>
      {children}
    , + li: ({ children }) =>
  • {children}
  • , + h1: ({ children }) =>

    {children}

    , + h2: ({ children }) =>

    {children}

    , + h3: ({ children }) =>

    {children}

    , + blockquote: ({ children }) => ( +
    {children}
    + ), + }} + > + {content} +
    + ); +}; + +// Updated MessageBubble component const MessageBubble: React.FC = ({ message, darkMode, index }) => (
    @@ -80,7 +145,9 @@ const MessageBubble: React.FC = ({ message, darkMode, index {message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
    -

    {message.text}

    +
    + +
    ); @@ -95,61 +162,8 @@ const MessageList: React.FC = ({ messages, darkMode }) => (
    ); -const InputArea: React.FC = ({ input, setInput, handleSend, darkMode }) => { - const theme = useMantineTheme(); - return ( -
    -
    - - - - setInput(event.currentTarget.value)} - onKeyPress={(event) => event.key === 'Enter' && handleSend()} - className="flex-grow" - styles={(theme) => ({ - input: { - backgroundColor: darkMode ? theme.colors.gray[7] : theme.white, - color: darkMode ? theme.white : theme.black, - border: 'none', - borderRadius: '9999px', - padding: '10px 16px', - '&::placeholder': { - color: darkMode ? theme.colors.gray[5] : theme.colors.gray[6], - }, - }, - rightSection: { - width: '40px', - }, - })} - rightSection={ - - - - } - rightSectionWidth={40} - /> -
    -
    - ); -}; - const ChatInterface: React.FC = () => { const [messages, setMessages] = useState([]); - const [input, setInput] = useState(''); const [darkMode, setDarkMode] = useState(false); const [chats, setChats] = useState([{ id: 1, name: 'General' }]); const [activeChat, setActiveChat] = useState(1); @@ -161,28 +175,45 @@ const ChatInterface: React.FC = () => { const handleSend = (content: string, attachments: File[]) => { if (content.trim() || attachments.length > 0) { - const newMessage: Message = { - id: Date.now(), - text: content, - sender: 'user', - timestamp: new Date(), - attachments: attachments.map(file => file.name), // Store file names or URLs - }; - setMessages([...messages, newMessage]); - - // Handle file uploads here (e.g., to a server) - - setTimeout(() => { - const botMessage: Message = { - id: Date.now(), - text: 'This is a bot response.', - sender: 'bot', - timestamp: new Date(), + const newMessage: Message = { + id: Date.now(), + text: content, + sender: 'user', + timestamp: new Date(), + attachments: attachments.map(file => file.name), }; - setMessages(prevMessages => [...prevMessages, botMessage]); - }, 1000); + setMessages([...messages, newMessage]); + + // Handle file uploads here (e.g., to a server) + + setTimeout(() => { + const botMessage: Message = { + id: Date.now(), + text: `Here's a sample response with Markdown: + + # Heading 1 + ## Heading 2 + + - List item 1 + - List item 2 + + \`\`\`python + def greet(name): + print(f"Hello, {name}!") + + greet("World") + \`\`\` + + > This is a blockquote. + + **Bold text** and *italic text*.`, + sender: 'bot', + timestamp: new Date(), + }; + setMessages(prevMessages => [...prevMessages, botMessage]); + }, 1000); } - }; + }; const addChat = () => { const newChat: Chat = { id: Date.now(), name: `Chat ${chats.length + 1}` }; @@ -204,11 +235,12 @@ const ChatInterface: React.FC = () => {
    - {/* */} {mounted && } +
    ); }; -export default ChatInterface; \ No newline at end of file + +export default ChatInterface; diff --git a/agenthub/interfaces/agentchat/index.ts b/agenthub/interfaces/agentchat/index.ts index 3235a3af..d9751321 100644 --- a/agenthub/interfaces/agentchat/index.ts +++ b/agenthub/interfaces/agentchat/index.ts @@ -14,7 +14,8 @@ export interface MessageBubbleProps { message: Message; darkMode: boolean; index: number; -} + isThinking?: boolean; + } export interface MessageListProps { messages: Message[]; diff --git a/agenthub/package-lock.json b/agenthub/package-lock.json index 3c720ab4..37315692 100644 --- a/agenthub/package-lock.json +++ b/agenthub/package-lock.json @@ -62,6 +62,7 @@ "react-syntax-highlighter": "^15.5.0", "react-tooltip": "^5.28.0", "recharts": "^2.12.7", + "rehype-raw": "^7.0.0", "sonner": "^1.5.0", "supabase": "^1.192.5", "ui": "*" @@ -7684,6 +7685,53 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", + "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^8.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/hastscript": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", + "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-parse-selector": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", @@ -7693,6 +7741,30 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-raw": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.4.tgz", + "integrity": "sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", @@ -7719,6 +7791,24 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -7807,6 +7897,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/https-proxy-agent": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", @@ -12107,6 +12206,17 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" }, + "node_modules/parse5": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz", + "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -13176,6 +13286,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -14385,6 +14509,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vfile-message": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", @@ -14432,6 +14569,15 @@ "loose-envify": "^1.0.0" } }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", diff --git a/agenthub/package.json b/agenthub/package.json index 627f55b5..d3ae720f 100644 --- a/agenthub/package.json +++ b/agenthub/package.json @@ -63,6 +63,7 @@ "react-syntax-highlighter": "^15.5.0", "react-tooltip": "^5.28.0", "recharts": "^2.12.7", + "rehype-raw": "^7.0.0", "sonner": "^1.5.0", "supabase": "^1.192.5", "ui": "*" From fdd8e45d3ed874edf4843c2b2423a0f187c351b1 Mon Sep 17 00:00:00 2001 From: BRama10 Date: Sun, 13 Oct 2024 13:53:34 -0400 Subject: [PATCH 5/8] feat: polished ui + typing animation --- agenthub/app/agentchat/_page.tsx | 278 --------------- agenthub/app/agentchat/page.tsx | 375 ++++++++++++++------- agenthub/components/chat/body/markdown.tsx | 3 +- agenthub/components/chat/editor/Editor.tsx | 2 +- agenthub/interfaces/agentchat/index.ts | 2 + agenthub/styles/global-stylesheet.css | 11 + 6 files changed, 275 insertions(+), 396 deletions(-) delete mode 100644 agenthub/app/agentchat/_page.tsx diff --git a/agenthub/app/agentchat/_page.tsx b/agenthub/app/agentchat/_page.tsx deleted file mode 100644 index 6c5b9e98..00000000 --- a/agenthub/app/agentchat/_page.tsx +++ /dev/null @@ -1,278 +0,0 @@ -'use client' - -import React, { useState, useRef, useEffect } from 'react'; -import { ChatEditor } from '@/components/chat/editor/Editor'; -import { useMounted } from '@/lib/mounted'; - -import { TextInput, ActionIcon, Switch, useMantineTheme, Button, CopyButton, Loader } from '@mantine/core'; -import { Send, Sun, Moon, Plus, Hash, MessageCircle, Clipboard, Check } from 'lucide-react'; -import { SidebarProps, HeaderProps, MessageBubbleProps, MessageListProps, InputAreaProps, Message, Chat } from '@/interfaces/agentchat'; -import ReactMarkdown from 'react-markdown'; -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; -import rehypeRaw from 'rehype-raw'; - - -const Sidebar: React.FC = ({ chats, activeChat, setActiveChat, addChat, darkMode }) => ( -
    - -
    - {chats.map((chat) => ( - - ))} -
    -
    -); - -const Header: React.FC = ({ darkMode, setDarkMode }) => { - const theme = useMantineTheme(); - return ( -
    -
    - -

    General

    -
    -
    - - setDarkMode(event.currentTarget.checked)} - size="md" - color={theme.primaryColor} - /> - -
    -
    - ); -}; - - - -// Improved Markdown component -interface MarkdownProps { - content: string; - darkMode: boolean; -} - -const Markdown: React.FC = ({ content, darkMode }) => { - console.log(typeof content) - console.log(content) - return ( - - {children} - - ) : ( -
    -
    - {match ? match[1] : 'text'} - - {({ copied, copy }) => ( - - {copied ? : } - - )} - -
    - - {String(children).replace(/\n$/, '')} - -
    - ); - }, - p: ({ children, ...props }) =>

    {children}

    , - br: ({ ...props }) =>
    , - ul: ({ children }) =>
      {children}
    , - ol: ({ children }) =>
      {children}
    , - li: ({ children }) =>
  • {children}
  • , - h1: ({ children }) =>

    {children}

    , - h2: ({ children }) =>

    {children}

    , - h3: ({ children }) =>

    {children}

    , - blockquote: ({ children }) => ( -
    {children}
    - ), - }} - > - {content} -
    - ); -}; - -// Updated MessageBubble component -const MessageBubble: React.FC = ({ message, darkMode, index, isThinking = false }) => ( -
    -
    - {message.sender === 'user' ? 'U' : 'B'} -
    -
    -
    - - {message.sender === 'user' ? 'User' : 'Bot'} - - - {message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - -
    -
    - {isThinking ? ( -
    - - Agent is thinking... -
    - ) : ( - - )} -
    -
    -
    - ); - - const MessageList: React.FC = ({ messages, darkMode }) => ( -
    -
    - {messages.map((message, index) => ( - - ))} -
    -
    - ); - -const ChatInterface: React.FC = () => { - const [messages, setMessages] = useState([]); - const [darkMode, setDarkMode] = useState(false); - const [chats, setChats] = useState([{ id: 1, name: 'General' }]); - const [activeChat, setActiveChat] = useState(1); - const messagesEndRef = useRef(null); - - useEffect(() => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }, [messages]); - - const handleSend = (content: string, attachments: File[]) => { - if (content.trim() || attachments.length > 0) { - const newMessage: Message = { - id: Date.now(), - text: content, - sender: 'user', - timestamp: new Date(), - attachments: attachments.map(file => file.name), - }; - setMessages([...messages, newMessage]); - - // Immediately add a "thinking" message for the bot - // const thinkingMessage: Message = { - // id: Date.now() + 1, - // text: '', - // sender: 'bot', - // timestamp: new Date(), - // }; - // setMessages(prevMessages => [...prevMessages, thinkingMessage]); - - const noInterpolation = (strings: any, ...values: any) => strings.join(''); - - // Simulate bot response after a delay - setTimeout(() => { - const botMessage: Message = { - id: 1, - text: noInterpolation`Here's a sample response with Markdown: - - # Heading 1 - ## Heading 2 - - - List item 1 - - List item 2 - - \`\`\`python - def greet(name): - print(f"Hello, {name}!") - - greet("World") - \`\`\` - - > This is a blockquote. - - **Bold text** and *italic text*.`, - sender: 'bot', - timestamp: new Date(), - }; - // setMessages(prevMessages => - // prevMessages.map(msg => msg.id === thinkingMessage.id ? botMessage : msg) - // ); - setMessages(prevMessages => [...prevMessages, botMessage]); - - }, 3000); // Increased delay to 3 seconds to make the loading state more noticeable - } - }; - - const addChat = () => { - const newChat: Chat = { id: Date.now(), name: `Chat ${chats.length + 1}` }; - setChats([...chats, newChat]); - setActiveChat(newChat.id); - }; - - const mounted = useMounted(); - - return ( -
    - -
    -
    - - {mounted && } -
    -
    -
    - ); -}; - - -export default ChatInterface; \ No newline at end of file diff --git a/agenthub/app/agentchat/page.tsx b/agenthub/app/agentchat/page.tsx index 4f03cff8..b1d32a57 100644 --- a/agenthub/app/agentchat/page.tsx +++ b/agenthub/app/agentchat/page.tsx @@ -4,8 +4,8 @@ import React, { useState, useRef, useEffect } from 'react'; import { ChatEditor } from '@/components/chat/editor/Editor'; import { useMounted } from '@/lib/mounted'; -import { TextInput, ActionIcon, Switch, useMantineTheme, Button, CopyButton } from '@mantine/core'; -import { Send, Sun, Moon, Plus, Hash, MessageCircle, Clipboard, Check } from 'lucide-react'; +import { TextInput, ActionIcon, Switch, useMantineTheme, Button, CopyButton, Loader, Tooltip } from '@mantine/core'; +import { Send, Sun, Moon, Plus, Hash, MessageCircle, Clipboard, Check, Lock, Edit2, X } from 'lucide-react'; import { SidebarProps, HeaderProps, MessageBubbleProps, MessageListProps, InputAreaProps, Message, Chat } from '@/interfaces/agentchat'; import ReactMarkdown from 'react-markdown'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; @@ -13,57 +13,136 @@ import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; import rehypeRaw from 'rehype-raw'; -const Sidebar: React.FC = ({ chats, activeChat, setActiveChat, addChat, darkMode }) => ( -
    - -
    - {chats.map((chat) => ( - - ))} -
    +const AgentLoader = () => { + return
    + + Agent is thinking...
    -); +} -const Header: React.FC = ({ darkMode, setDarkMode }) => { - const theme = useMantineTheme(); +const updateChatName = (chatId: number, newName: string) => { + // setChats(prevChats => + // prevChats.map(chat => + // chat.id === chatId ? { ...chat, name: newName } : chat + // ) + // ); + }; + +const Sidebar: React.FC = ({ chats, activeChat, setActiveChat, addChat, updateChatName, darkMode }) => { + const [editingId, setEditingId] = useState(null); + const [editingName, setEditingName] = useState(''); + + const categoryStyle = "text-xs font-semibold uppercase tracking-wide text-gray-500 mb-2 mt-4 px-2 flex justify-between items-center"; + const channelStyle = `flex items-center justify-between rounded px-2 py-1.5 text-sm font-medium transition-colors duration-200 ease-in-out cursor-pointer`; + const activeChannelStyle = darkMode ? 'bg-gray-700 text-white' : 'bg-gray-300 text-gray-900'; + const inactiveChannelStyle = darkMode ? 'text-gray-400 hover:bg-gray-700 hover:text-gray-200' : 'text-gray-700 hover:bg-gray-200 hover:text-gray-900'; + + const startEditing = (chat: Chat) => { + setEditingId(chat.id); + setEditingName(chat.name); + }; + + const cancelEditing = () => { + setEditingId(null); + setEditingName(''); + }; + + const saveEditing = () => { + if (editingId !== null && editingName.trim() !== '') { + updateChatName(editingId, editingName.trim()); + setEditingId(null); + } + }; + return ( -
    -
    - -

    General

    -
    -
    - - setDarkMode(event.currentTarget.checked)} - size="md" - color={theme.primaryColor} - /> - +
    +
    +

    Your Workspace

    +
    + +
    +
    + Channels + + + + + +
    + {chats.map((chat) => ( +
    + {editingId === chat.id ? ( +
    + + setEditingName(e.target.value)} + className="flex-grow" + size="xs" + autoFocus + onKeyPress={(e) => e.key === 'Enter' && saveEditing()} + /> + + + + + + +
    + ) : ( + <> +
    setActiveChat(chat.id)}> + + {chat.name} +
    + + startEditing(chat)} + variant="subtle" + color={darkMode ? "gray" : "dark"} + className="opacity-0 group-hover:opacity-100 transition-opacity duration-200" + > + + + + + )}
    + ))}
    +
    ); -}; + }; + + const Header: React.FC = ({ darkMode, setDarkMode }) => { + const theme = useMantineTheme(); + return ( +
    +
    + +

    General

    +
    +
    + + setDarkMode(event.currentTarget.checked)} + size="sm" + color={theme.primaryColor} + /> + +
    +
    + ); + }; @@ -74,6 +153,58 @@ interface MarkdownProps { } const Markdown: React.FC = ({ content, darkMode }) => { + const [displayedText, setDisplayedText] = useState(''); + const animationRef = useRef(null); + const currentIndexRef = useRef(0); + + useEffect(() => { + let lastTimestamp: number | null = null; + + const streamText = (timestamp: number) => { + if (lastTimestamp === null) { + lastTimestamp = timestamp; + } + + const elapsed = timestamp - lastTimestamp; + + if (elapsed >= 30) { // Minimum 30ms between updates + if (currentIndexRef.current < content.length) { + const chunkSize = Math.floor(Math.random() * 3) + 1; + const nextChunk = content.slice(currentIndexRef.current, currentIndexRef.current + chunkSize); + + setDisplayedText(prevText => prevText + nextChunk); + currentIndexRef.current += chunkSize; + + // Determine the next delay + let delay = Math.floor(Math.random() * 50) + 30; + + if (nextChunk.includes('.') || nextChunk.includes('!') || nextChunk.includes('?')) { + delay += Math.floor(Math.random() * 300) + 200; + } else if (nextChunk.includes(',') || nextChunk.includes(';')) { + delay += Math.floor(Math.random() * 150) + 100; + } + + if (nextChunk.length > 5) { + delay += nextChunk.length * 10; + } + + lastTimestamp = timestamp + delay; + } + } + + animationRef.current = requestAnimationFrame(streamText); + }; + + animationRef.current = requestAnimationFrame(streamText); + + return () => { + if (animationRef.current !== null) { + cancelAnimationFrame(animationRef.current); + } + }; + }, [content]); + + return ( = ({ content, darkMode }) => { const match = /language-(\w+)/.exec(className || ''); const isInline = !match && (props as any).inline; return isInline ? ( - + {children} ) : (
    - {match ? match[1] : 'text'} + {match ? match[1] : 'text'} {({ copied, copy }) => ( @@ -121,46 +252,46 @@ const Markdown: React.FC = ({ content, darkMode }) => { ), }} > - {content} + {displayedText} ); }; // Updated MessageBubble component -const MessageBubble: React.FC = ({ message, darkMode, index }) => ( +const MessageBubble: React.FC = ({ message, darkMode, index, isThinking = false }) => (
    -
    - {message.sender === 'user' ? 'U' : 'B'} +
    + {message.sender === 'user' ? 'U' : 'B'} +
    +
    +
    + + {message.sender === 'user' ? 'User' : 'Bot'} + + + {message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} +
    -
    -
    - - {message.sender === 'user' ? 'User' : 'Bot'} - - - {message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - -
    -
    - -
    +
    + {isThinking ? : }
    +
    -); - -const MessageList: React.FC = ({ messages, darkMode }) => ( -
    -
    - {messages.map((message, index) => ( - - ))} -
    + ); + + const MessageList: React.FC = ({ messages, darkMode }) => ( +
    +
    + {messages.map((message, index) => ( + + ))} +
    -); + ); const ChatInterface: React.FC = () => { const [messages, setMessages] = useState([]); @@ -171,7 +302,7 @@ const ChatInterface: React.FC = () => { useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }, [messages]); + }, [messages]); const handleSend = (content: string, attachments: File[]) => { if (content.trim() || attachments.length > 0) { @@ -181,37 +312,45 @@ const ChatInterface: React.FC = () => { sender: 'user', timestamp: new Date(), attachments: attachments.map(file => file.name), + thinking: false }; setMessages([...messages, newMessage]); // Handle file uploads here (e.g., to a server) + const botMessage: Message = { + id: 3, + text: `Here's a sample response with Markdown: + +# Heading 1 +## Heading 2 + +- List item 1 +- List item 2 + +\`\`\`python +def greet(name): + print(f"Hello, {name}!") + +greet("World") +\`\`\` + +> This is a blockquote. + +**Bold text** and *italic text*.`, + sender: 'bot', + timestamp: new Date(), + thinking: true + }; + setMessages(prevMessages => [...prevMessages, botMessage]); setTimeout(() => { - const botMessage: Message = { - id: Date.now(), - text: `Here's a sample response with Markdown: - - # Heading 1 - ## Heading 2 - - - List item 1 - - List item 2 - - \`\`\`python - def greet(name): - print(f"Hello, {name}!") - - greet("World") - \`\`\` - - > This is a blockquote. - - **Bold text** and *italic text*.`, - sender: 'bot', - timestamp: new Date(), - }; - setMessages(prevMessages => [...prevMessages, botMessage]); - }, 1000); + setMessages(prevMessages => [...prevMessages].map(message => { + if (message.id == 3) { + return { ...message, thinking: false }; + } + return message; + })); + }, 3000); } }; @@ -225,21 +364,25 @@ const ChatInterface: React.FC = () => { return (
    - -
    -
    - + +
    +
    + +
    {mounted && } -
    + +
    +
    - ); + ); }; diff --git a/agenthub/components/chat/body/markdown.tsx b/agenthub/components/chat/body/markdown.tsx index d7a72bb6..e5a50863 100644 --- a/agenthub/components/chat/body/markdown.tsx +++ b/agenthub/components/chat/body/markdown.tsx @@ -78,7 +78,7 @@ export default function Markdown({ content }: IProps) { return match ? (
    -
    +
    {match[1]}
    {String(children).replace(/\n$/, '')} diff --git a/agenthub/components/chat/editor/Editor.tsx b/agenthub/components/chat/editor/Editor.tsx index 249b824d..8853bc7e 100644 --- a/agenthub/components/chat/editor/Editor.tsx +++ b/agenthub/components/chat/editor/Editor.tsx @@ -104,7 +104,7 @@ export const ChatEditor: React.FC = ({ onSend, darkMode }) => { return ( void; addChat: () => void; + updateChatName: (chatId: number, newName: string) => void; darkMode: boolean; } \ No newline at end of file diff --git a/agenthub/styles/global-stylesheet.css b/agenthub/styles/global-stylesheet.css index 90339a7b..464688e1 100644 --- a/agenthub/styles/global-stylesheet.css +++ b/agenthub/styles/global-stylesheet.css @@ -16891,4 +16891,15 @@ table.inference-table tr th:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} + +pre > .relative > .rounded-b-md { + /* Your custom styles */ + margin-top: 0px !important; + margin-left: 0px !important; + margin-right: 0px !important; + margin-bottom: 0.5em !important; + border-radius: 0 !important; + border-bottom-left-radius: 0.3em !important; + border-bottom-right-radius: 0.3em !important; } \ No newline at end of file From f4a91874e9fbdc14f1dd7e5246c35df33660a1c8 Mon Sep 17 00:00:00 2001 From: BRama10 Date: Sun, 13 Oct 2024 16:20:48 -0400 Subject: [PATCH 6/8] refactor: modularize --- agenthub/app/agentchat/page.tsx | 280 +----------------- agenthub/components/agentchat/Header.tsx | 31 ++ agenthub/components/agentchat/Markdown.tsx | 122 ++++++++ .../components/agentchat/MessageBubble.tsx | 43 +++ agenthub/components/agentchat/MessageList.tsx | 18 ++ agenthub/components/agentchat/Sidebar.tsx | 109 +++++++ agenthub/interfaces/agentchat/index.ts | 31 -- 7 files changed, 330 insertions(+), 304 deletions(-) create mode 100644 agenthub/components/agentchat/Header.tsx create mode 100644 agenthub/components/agentchat/Markdown.tsx create mode 100644 agenthub/components/agentchat/MessageBubble.tsx create mode 100644 agenthub/components/agentchat/MessageList.tsx create mode 100644 agenthub/components/agentchat/Sidebar.tsx diff --git a/agenthub/app/agentchat/page.tsx b/agenthub/app/agentchat/page.tsx index b1d32a57..4550d31e 100644 --- a/agenthub/app/agentchat/page.tsx +++ b/agenthub/app/agentchat/page.tsx @@ -4,21 +4,13 @@ import React, { useState, useRef, useEffect } from 'react'; import { ChatEditor } from '@/components/chat/editor/Editor'; import { useMounted } from '@/lib/mounted'; -import { TextInput, ActionIcon, Switch, useMantineTheme, Button, CopyButton, Loader, Tooltip } from '@mantine/core'; -import { Send, Sun, Moon, Plus, Hash, MessageCircle, Clipboard, Check, Lock, Edit2, X } from 'lucide-react'; -import { SidebarProps, HeaderProps, MessageBubbleProps, MessageListProps, InputAreaProps, Message, Chat } from '@/interfaces/agentchat'; -import ReactMarkdown from 'react-markdown'; -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; -import rehypeRaw from 'rehype-raw'; +import { Message, Chat } from '@/interfaces/agentchat'; +import { Sidebar } from '@/components/agentchat/Sidebar'; +import { Header } from '@/components/agentchat/Header'; +import { MessageList } from '@/components/agentchat/MessageList'; + -const AgentLoader = () => { - return
    - - Agent is thinking... -
    -} const updateChatName = (chatId: number, newName: string) => { // setChats(prevChats => @@ -28,270 +20,12 @@ const updateChatName = (chatId: number, newName: string) => { // ); }; -const Sidebar: React.FC = ({ chats, activeChat, setActiveChat, addChat, updateChatName, darkMode }) => { - const [editingId, setEditingId] = useState(null); - const [editingName, setEditingName] = useState(''); - - const categoryStyle = "text-xs font-semibold uppercase tracking-wide text-gray-500 mb-2 mt-4 px-2 flex justify-between items-center"; - const channelStyle = `flex items-center justify-between rounded px-2 py-1.5 text-sm font-medium transition-colors duration-200 ease-in-out cursor-pointer`; - const activeChannelStyle = darkMode ? 'bg-gray-700 text-white' : 'bg-gray-300 text-gray-900'; - const inactiveChannelStyle = darkMode ? 'text-gray-400 hover:bg-gray-700 hover:text-gray-200' : 'text-gray-700 hover:bg-gray-200 hover:text-gray-900'; - - const startEditing = (chat: Chat) => { - setEditingId(chat.id); - setEditingName(chat.name); - }; - - const cancelEditing = () => { - setEditingId(null); - setEditingName(''); - }; - - const saveEditing = () => { - if (editingId !== null && editingName.trim() !== '') { - updateChatName(editingId, editingName.trim()); - setEditingId(null); - } - }; - - return ( -
    -
    -

    Your Workspace

    -
    - -
    -
    - Channels - - - - - -
    - {chats.map((chat) => ( -
    - {editingId === chat.id ? ( -
    - - setEditingName(e.target.value)} - className="flex-grow" - size="xs" - autoFocus - onKeyPress={(e) => e.key === 'Enter' && saveEditing()} - /> - - - - - - -
    - ) : ( - <> -
    setActiveChat(chat.id)}> - - {chat.name} -
    - - startEditing(chat)} - variant="subtle" - color={darkMode ? "gray" : "dark"} - className="opacity-0 group-hover:opacity-100 transition-opacity duration-200" - > - - - - - )} -
    - ))} -
    -
    - ); - }; - - const Header: React.FC = ({ darkMode, setDarkMode }) => { - const theme = useMantineTheme(); - return ( -
    -
    - -

    General

    -
    -
    - - setDarkMode(event.currentTarget.checked)} - size="sm" - color={theme.primaryColor} - /> - -
    -
    - ); - }; - - - -// Improved Markdown component -interface MarkdownProps { - content: string; - darkMode: boolean; -} - -const Markdown: React.FC = ({ content, darkMode }) => { - const [displayedText, setDisplayedText] = useState(''); - const animationRef = useRef(null); - const currentIndexRef = useRef(0); - - useEffect(() => { - let lastTimestamp: number | null = null; - - const streamText = (timestamp: number) => { - if (lastTimestamp === null) { - lastTimestamp = timestamp; - } - - const elapsed = timestamp - lastTimestamp; - - if (elapsed >= 30) { // Minimum 30ms between updates - if (currentIndexRef.current < content.length) { - const chunkSize = Math.floor(Math.random() * 3) + 1; - const nextChunk = content.slice(currentIndexRef.current, currentIndexRef.current + chunkSize); - - setDisplayedText(prevText => prevText + nextChunk); - currentIndexRef.current += chunkSize; - - // Determine the next delay - let delay = Math.floor(Math.random() * 50) + 30; - - if (nextChunk.includes('.') || nextChunk.includes('!') || nextChunk.includes('?')) { - delay += Math.floor(Math.random() * 300) + 200; - } else if (nextChunk.includes(',') || nextChunk.includes(';')) { - delay += Math.floor(Math.random() * 150) + 100; - } - - if (nextChunk.length > 5) { - delay += nextChunk.length * 10; - } - - lastTimestamp = timestamp + delay; - } - } - - animationRef.current = requestAnimationFrame(streamText); - }; - - animationRef.current = requestAnimationFrame(streamText); - - return () => { - if (animationRef.current !== null) { - cancelAnimationFrame(animationRef.current); - } - }; - }, [content]); + - return ( - - {children} - - ) : ( -
    -
    - {match ? match[1] : 'text'} - - {({ copied, copy }) => ( - - {copied ? : } - - )} - -
    - - {String(children).replace(/\n$/, '')} - -
    - ); - }, - p: ({ children, ...props }) =>

    {children}

    , - br: ({ ...props }) =>
    , - ul: ({ children }) =>
      {children}
    , - ol: ({ children }) =>
      {children}
    , - li: ({ children }) =>
  • {children}
  • , - h1: ({ children }) =>

    {children}

    , - h2: ({ children }) =>

    {children}

    , - h3: ({ children }) =>

    {children}

    , - blockquote: ({ children }) => ( -
    {children}
    - ), - }} - > - {displayedText} -
    - ); -}; -// Updated MessageBubble component -const MessageBubble: React.FC = ({ message, darkMode, index, isThinking = false }) => ( -
    -
    - {message.sender === 'user' ? 'U' : 'B'} -
    -
    -
    - - {message.sender === 'user' ? 'User' : 'Bot'} - - - {message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - -
    -
    - {isThinking ? : } -
    -
    -
    - ); - const MessageList: React.FC = ({ messages, darkMode }) => ( -
    -
    - {messages.map((message, index) => ( - - ))} -
    -
    - ); + const ChatInterface: React.FC = () => { const [messages, setMessages] = useState([]); diff --git a/agenthub/components/agentchat/Header.tsx b/agenthub/components/agentchat/Header.tsx new file mode 100644 index 00000000..0064fdd2 --- /dev/null +++ b/agenthub/components/agentchat/Header.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import { Switch, useMantineTheme } from '@mantine/core'; +import { Sun, Moon, Hash } from 'lucide-react'; + +export interface HeaderProps { + darkMode: boolean; + setDarkMode: React.Dispatch>; +} + +export const Header: React.FC = ({ darkMode, setDarkMode }) => { + const theme = useMantineTheme(); + return ( +
    +
    + +

    General

    +
    +
    + + setDarkMode(event.currentTarget.checked)} + size="sm" + color={theme.primaryColor} + /> + +
    +
    + ); + }; \ No newline at end of file diff --git a/agenthub/components/agentchat/Markdown.tsx b/agenthub/components/agentchat/Markdown.tsx new file mode 100644 index 00000000..d1239146 --- /dev/null +++ b/agenthub/components/agentchat/Markdown.tsx @@ -0,0 +1,122 @@ +'use client' + +import React, { useState, useRef, useEffect } from 'react'; + +import { ActionIcon, CopyButton } from '@mantine/core'; +import { Clipboard, Check} from 'lucide-react'; + +import ReactMarkdown from 'react-markdown'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; +import rehypeRaw from 'rehype-raw'; + +// Improved Markdown component +interface MarkdownProps { + content: string; + darkMode: boolean; +} + +export const Markdown: React.FC = ({ content, darkMode }) => { + const [displayedText, setDisplayedText] = useState(''); + const animationRef = useRef(null); + const currentIndexRef = useRef(0); + + useEffect(() => { + let lastTimestamp: number | null = null; + + const streamText = (timestamp: number) => { + if (lastTimestamp === null) { + lastTimestamp = timestamp; + } + + const elapsed = timestamp - lastTimestamp; + + if (elapsed >= 30) { // Minimum 30ms between updates + if (currentIndexRef.current < content.length) { + const chunkSize = Math.floor(Math.random() * 3) + 1; + const nextChunk = content.slice(currentIndexRef.current, currentIndexRef.current + chunkSize); + + setDisplayedText(prevText => prevText + nextChunk); + currentIndexRef.current += chunkSize; + + // Determine the next delay + let delay = Math.floor(Math.random() * 50) + 30; + + if (nextChunk.includes('.') || nextChunk.includes('!') || nextChunk.includes('?')) { + delay += Math.floor(Math.random() * 300) + 200; + } else if (nextChunk.includes(',') || nextChunk.includes(';')) { + delay += Math.floor(Math.random() * 150) + 100; + } + + if (nextChunk.length > 5) { + delay += nextChunk.length * 10; + } + + lastTimestamp = timestamp + delay; + } + } + + animationRef.current = requestAnimationFrame(streamText); + }; + + animationRef.current = requestAnimationFrame(streamText); + + return () => { + if (animationRef.current !== null) { + cancelAnimationFrame(animationRef.current); + } + }; + }, [content]); + + + return ( + + {children} +
    + ) : ( +
    +
    + {match ? match[1] : 'text'} + + {({ copied, copy }) => ( + + {copied ? : } + + )} + +
    + + {String(children).replace(/\n$/, '')} + +
    + ); + }, + p: ({ children, ...props }) =>

    {children}

    , + br: ({ ...props }) =>
    , + ul: ({ children }) =>
      {children}
    , + ol: ({ children }) =>
      {children}
    , + li: ({ children }) =>
  • {children}
  • , + h1: ({ children }) =>

    {children}

    , + h2: ({ children }) =>

    {children}

    , + h3: ({ children }) =>

    {children}

    , + blockquote: ({ children }) => ( +
    {children}
    + ), + }} + > + {displayedText} + + ); +}; \ No newline at end of file diff --git a/agenthub/components/agentchat/MessageBubble.tsx b/agenthub/components/agentchat/MessageBubble.tsx new file mode 100644 index 00000000..8cd2856f --- /dev/null +++ b/agenthub/components/agentchat/MessageBubble.tsx @@ -0,0 +1,43 @@ +import { Message } from "@/interfaces/agentchat"; +import { Loader } from "@mantine/core"; +import { Markdown } from "./Markdown"; + +export interface MessageBubbleProps { + message: Message; + darkMode: boolean; + index: number; + isThinking?: boolean; +} + +const AgentLoader = () => { + return
    + + Agent is thinking... +
    +} + +// Updated MessageBubble component +export const MessageBubble: React.FC = ({ message, darkMode, index, isThinking = false }) => ( +
    +
    + {message.sender === 'user' ? 'U' : 'B'} +
    +
    +
    + + {message.sender === 'user' ? 'User' : 'Bot'} + + + {message.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + +
    +
    + {isThinking ? : } +
    +
    +
    + ); \ No newline at end of file diff --git a/agenthub/components/agentchat/MessageList.tsx b/agenthub/components/agentchat/MessageList.tsx new file mode 100644 index 00000000..0eb6bf8a --- /dev/null +++ b/agenthub/components/agentchat/MessageList.tsx @@ -0,0 +1,18 @@ +import { Message } from "@/interfaces/agentchat"; +import { MessageBubble } from "./MessageBubble"; + +export interface MessageListProps { + messages: Message[]; + darkMode: boolean; +} + + +export const MessageList: React.FC = ({ messages, darkMode }) => ( +
    +
    + {messages.map((message, index) => ( + + ))} +
    +
    + ); \ No newline at end of file diff --git a/agenthub/components/agentchat/Sidebar.tsx b/agenthub/components/agentchat/Sidebar.tsx new file mode 100644 index 00000000..9f19ff8f --- /dev/null +++ b/agenthub/components/agentchat/Sidebar.tsx @@ -0,0 +1,109 @@ +import React, { useState } from 'react'; + +import { TextInput, ActionIcon, Tooltip } from '@mantine/core'; +import { Plus, Hash, Check, Edit2, X } from 'lucide-react'; +import { Chat } from '@/interfaces/agentchat'; + + +export interface SidebarProps { + chats: Chat[]; + activeChat: number; + setActiveChat: (id: number) => void; + addChat: () => void; + updateChatName: (chatId: number, newName: string) => void; + darkMode: boolean; +} + +export const Sidebar: React.FC = ({ chats, activeChat, setActiveChat, addChat, updateChatName, darkMode }) => { + const [editingId, setEditingId] = useState(null); + const [editingName, setEditingName] = useState(''); + + const categoryStyle = "text-xs font-semibold uppercase tracking-wide text-gray-500 mb-2 mt-4 px-2 flex justify-between items-center"; + const channelStyle = `flex items-center justify-between rounded px-2 py-1.5 text-sm font-medium transition-colors duration-200 ease-in-out cursor-pointer`; + const activeChannelStyle = darkMode ? 'bg-gray-700 text-white' : 'bg-gray-300 text-gray-900'; + const inactiveChannelStyle = darkMode ? 'text-gray-400 hover:bg-gray-700 hover:text-gray-200' : 'text-gray-700 hover:bg-gray-200 hover:text-gray-900'; + + const startEditing = (chat: Chat) => { + setEditingId(chat.id); + setEditingName(chat.name); + }; + + const cancelEditing = () => { + setEditingId(null); + setEditingName(''); + }; + + const saveEditing = () => { + if (editingId !== null && editingName.trim() !== '') { + updateChatName(editingId, editingName.trim()); + setEditingId(null); + } + }; + + return ( +
    +
    +

    Your Workspace

    +
    + +
    +
    + Channels + + + + + +
    + {chats.map((chat) => ( +
    + {editingId === chat.id ? ( +
    + + setEditingName(e.target.value)} + className="flex-grow" + size="xs" + autoFocus + onKeyPress={(e) => e.key === 'Enter' && saveEditing()} + /> + + + + + + +
    + ) : ( + <> +
    setActiveChat(chat.id)}> + + {chat.name} +
    + + startEditing(chat)} + variant="subtle" + color={darkMode ? "gray" : "dark"} + className="opacity-0 group-hover:opacity-100 transition-opacity duration-200" + > + + + + + )} +
    + ))} +
    +
    + ); + }; diff --git a/agenthub/interfaces/agentchat/index.ts b/agenthub/interfaces/agentchat/index.ts index 7fc0a1af..7ddfe72a 100644 --- a/agenthub/interfaces/agentchat/index.ts +++ b/agenthub/interfaces/agentchat/index.ts @@ -5,29 +5,6 @@ export interface Message { sender: 'user' | 'bot'; timestamp: Date; attachments?: string[]; // Array of file names or URLs - } -export interface HeaderProps { - darkMode: boolean; - setDarkMode: React.Dispatch>; -} - -export interface MessageBubbleProps { - message: Message; - darkMode: boolean; - index: number; - isThinking?: boolean; - } - -export interface MessageListProps { - messages: Message[]; - darkMode: boolean; -} - -export interface InputAreaProps { - input: string; - setInput: React.Dispatch>; - handleSend: () => void; - darkMode: boolean; } export interface Chat { @@ -35,11 +12,3 @@ export interface Chat { name: string; } -export interface SidebarProps { - chats: Chat[]; - activeChat: number; - setActiveChat: (id: number) => void; - addChat: () => void; - updateChatName: (chatId: number, newName: string) => void; - darkMode: boolean; -} \ No newline at end of file From 4c2b15c752fecaa25b37f789ed70a8dc9ace9cf7 Mon Sep 17 00:00:00 2001 From: BRama10 Date: Mon, 21 Oct 2024 15:16:06 -0400 Subject: [PATCH 7/8] mentions & conditional dark mode --- agenthub/components/chat/editor/Editor.tsx | 38 ++ .../components/chat/editor/MentionListV2.tsx | 70 ++++ agenthub/components/chat/editor/Suggestion.ts | 93 +++++ agenthub/package-lock.json | 334 +++++++++++++++++- agenthub/package.json | 4 +- agenthub/public/MentionListV2Dark.scss | 47 +++ agenthub/public/MentionListV2Light.scss | 45 +++ agenthub/styles/global-stylesheet.css | 22 ++ 8 files changed, 648 insertions(+), 5 deletions(-) create mode 100644 agenthub/components/chat/editor/MentionListV2.tsx create mode 100644 agenthub/components/chat/editor/Suggestion.ts create mode 100644 agenthub/public/MentionListV2Dark.scss create mode 100644 agenthub/public/MentionListV2Light.scss diff --git a/agenthub/components/chat/editor/Editor.tsx b/agenthub/components/chat/editor/Editor.tsx index 8853bc7e..0fc16072 100644 --- a/agenthub/components/chat/editor/Editor.tsx +++ b/agenthub/components/chat/editor/Editor.tsx @@ -7,14 +7,19 @@ import Paragraph from '@tiptap/extension-paragraph'; import Text from '@tiptap/extension-text'; import HardBreak from '@tiptap/extension-hard-break'; import Placeholder from '@tiptap/extension-placeholder'; +import Mention from '@tiptap/extension-mention' import { ActionIcon, Group, Paper, Text as MantineText, useMantineTheme, ScrollArea, Image, Box, Overlay } from '@mantine/core'; import { Send, Plus, X, FileIcon } from 'lucide-react'; +import suggestion from './Suggestion' + export interface ChatEditorProps { onSend: (content: string, attachments: File[]) => void; darkMode: boolean; } + + export const ChatEditor: React.FC = ({ onSend, darkMode }) => { const [attachments, setAttachments] = useState([]); const [previews, setPreviews] = useState([]); @@ -22,6 +27,33 @@ export const ChatEditor: React.FC = ({ onSend, darkMode }) => { const fileInputRef = useRef(null); const theme = useMantineTheme(); + let currentStyleElement: HTMLStyleElement | null = null; + + const loadStyle = async (isDarkMode: boolean) => { + // Remove the current style element if it exists + if (currentStyleElement && currentStyleElement.parentNode) { + currentStyleElement.parentNode.removeChild(currentStyleElement); + } + + const fetchStyle = async (isDarkMode: boolean) => { + const response = await fetch(isDarkMode ? '/MentionListV2Dark.scss' : '/MentionListV2Light.scss'); + return await response.text(); + }; + + // Import the new style + const style = await fetchStyle(isDarkMode) + + // Create a new style element + currentStyleElement = document.createElement('style'); + currentStyleElement.textContent = style; + document.head.appendChild(currentStyleElement); + }; + + useEffect(() => { + loadStyle(darkMode); + }, [darkMode]) + + const editor = useEditor({ extensions: [ Document, @@ -35,6 +67,12 @@ export const ChatEditor: React.FC = ({ onSend, darkMode }) => { Placeholder.configure({ placeholder: 'Type a message...', }), + Mention.configure({ + HTMLAttributes: { + class: 'mention', + }, + suggestion, + }), ], editorProps: { attributes: { diff --git a/agenthub/components/chat/editor/MentionListV2.tsx b/agenthub/components/chat/editor/MentionListV2.tsx new file mode 100644 index 00000000..096f8605 --- /dev/null +++ b/agenthub/components/chat/editor/MentionListV2.tsx @@ -0,0 +1,70 @@ +import React, { + forwardRef, useEffect, useImperativeHandle, + useState, +} from 'react' + +export default forwardRef((props: any, ref: any) => { + const [selectedIndex, setSelectedIndex] = useState(0) + + const selectItem = (index: any) => { + const item = props.items[index] + + if (item) { + props.command({ id: item }) + } + } + + const upHandler = () => { + setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length) + } + + const downHandler = () => { + setSelectedIndex((selectedIndex + 1) % props.items.length) + } + + const enterHandler = (e: any) => { + e.preventDefault(); + e.stopPropagation(); + selectItem(selectedIndex) + } + + useEffect(() => setSelectedIndex(0), [props.items]) + + useImperativeHandle(ref, () => ({ + onKeyDown: ({ event }: {event: any}) => { + if (event.key === 'ArrowUp') { + upHandler() + return true + } + + if (event.key === 'ArrowDown') { + downHandler() + return true + } + + if (event.key === 'Enter') { + enterHandler(event) + return true + } + + return false + }, + })) + + return ( +
    + {props.items.length + ? props.items.map((item: any, index: any) => ( + + )) + :
    No result
    + } +
    + ) +}) \ No newline at end of file diff --git a/agenthub/components/chat/editor/Suggestion.ts b/agenthub/components/chat/editor/Suggestion.ts new file mode 100644 index 00000000..a58f4320 --- /dev/null +++ b/agenthub/components/chat/editor/Suggestion.ts @@ -0,0 +1,93 @@ +import { ReactRenderer } from '@tiptap/react' +import tippy from 'tippy.js' + +import MentionListV2 from './MentionListV2' + +export default { + items: ({ query }: {query: any}) => { + return [ + 'Lea Thompson', + 'Cyndi Lauper', + 'Tom Cruise', + 'Madonna', + 'Jerry Hall', + 'Joan Collins', + 'Winona Ryder', + 'Christina Applegate', + 'Alyssa Milano', + 'Molly Ringwald', + 'Ally Sheedy', + 'Debbie Harry', + 'Olivia Newton-John', + 'Elton John', + 'Michael J. Fox', + 'Axl Rose', + 'Emilio Estevez', + 'Ralph Macchio', + 'Rob Lowe', + 'Jennifer Grey', + 'Mickey Rourke', + 'John Cusack', + 'Matthew Broderick', + 'Justine Bateman', + 'Lisa Bonet', + ] + .filter(item => item.toLowerCase().startsWith(query.toLowerCase())) + .slice(0, 5) + }, + + render: () => { + let component: any + let popup: any + + return { + onStart: (props: any) => { + component = new ReactRenderer(MentionListV2, { + props, + editor: props.editor, + }) + + if (!props.clientRect) { + return + } + + popup = tippy('body', { + getReferenceClientRect: props.clientRect, + appendTo: () => document.body, + content: component.element, + showOnCreate: true, + interactive: true, + trigger: 'manual', + placement: 'bottom-start', + }) + }, + + onUpdate(props: any) { + component.updateProps(props) + + if (!props.clientRect) { + return + } + + popup[0].setProps({ + getReferenceClientRect: props.clientRect, + }) + }, + + onKeyDown(props: any) { + if (props.event.key === 'Escape') { + popup[0].hide() + + return true + } + + return component.ref?.onKeyDown(props) + }, + + onExit() { + popup[0].destroy() + component.destroy() + }, + } + }, +} \ No newline at end of file diff --git a/agenthub/package-lock.json b/agenthub/package-lock.json index 37315692..c1b3b123 100644 --- a/agenthub/package-lock.json +++ b/agenthub/package-lock.json @@ -37,7 +37,7 @@ "@tiptap/pm": "^2.8.0", "@tiptap/react": "^2.8.0", "@tiptap/starter-kit": "^2.8.0", - "@tiptap/suggestion": "^2.7.2", + "@tiptap/suggestion": "^2.8.0", "axios": "^1.7.7", "class-variance-authority": "^0.7.0", "classnames": "^2.5.1", @@ -63,8 +63,10 @@ "react-tooltip": "^5.28.0", "recharts": "^2.12.7", "rehype-raw": "^7.0.0", + "sass": "^1.80.3", "sonner": "^1.5.0", "supabase": "^1.192.5", + "tippy.js": "^6.3.7", "ui": "*" }, "devDependencies": { @@ -2151,6 +2153,266 @@ "node": ">=12.4.0" } }, + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -4911,9 +5173,9 @@ } }, "node_modules/@tiptap/suggestion": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@tiptap/suggestion/-/suggestion-2.7.2.tgz", - "integrity": "sha512-ZJMNuorzQQiKyzoisyeHgPH3kywv0cvQnyz5guZWiAtFWCUbFyB9MSLNuoijubwHWfnZMe4XiW5EqVt1dBmxBw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/suggestion/-/suggestion-2.8.0.tgz", + "integrity": "sha512-ENBgO7a92cZa4gESb0Da5e7PKnHiz77tZr226VLqEdMcp7Lve2jb3Q2uL+cWCJxs7P1l6ZhetUmUiJg+Ee7Wjg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6361,6 +6623,17 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -7927,6 +8200,11 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -9586,6 +9864,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -13473,6 +13756,49 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sass": { + "version": "1.80.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.3.tgz", + "integrity": "sha512-ptDWyVmDMVielpz/oWy3YP3nfs7LpJTHIJZboMVs8GEC9eUmtZTZhMHlTW98wY4aEorDfjN38+Wr/XjskFWcfA==", + "dependencies": { + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", diff --git a/agenthub/package.json b/agenthub/package.json index d3ae720f..780ad2a0 100644 --- a/agenthub/package.json +++ b/agenthub/package.json @@ -38,7 +38,7 @@ "@tiptap/pm": "^2.8.0", "@tiptap/react": "^2.8.0", "@tiptap/starter-kit": "^2.8.0", - "@tiptap/suggestion": "^2.7.2", + "@tiptap/suggestion": "^2.8.0", "axios": "^1.7.7", "class-variance-authority": "^0.7.0", "classnames": "^2.5.1", @@ -64,8 +64,10 @@ "react-tooltip": "^5.28.0", "recharts": "^2.12.7", "rehype-raw": "^7.0.0", + "sass": "^1.80.3", "sonner": "^1.5.0", "supabase": "^1.192.5", + "tippy.js": "^6.3.7", "ui": "*" }, "devDependencies": { diff --git a/agenthub/public/MentionListV2Dark.scss b/agenthub/public/MentionListV2Dark.scss new file mode 100644 index 00000000..afa51745 --- /dev/null +++ b/agenthub/public/MentionListV2Dark.scss @@ -0,0 +1,47 @@ +/* Dropdown menu */ +.dropdown-menu { + background: rgb(31, 41, 55); + border: 1px solid var(--gray-4); + border-radius: 0.7rem; + box-shadow: var(--shadow); + display: flex; + flex-direction: column; + gap: 0.1rem; + overflow: auto; + padding: 0.4rem; + position: relative; + color: var(--white); + + button { + align-items: center; + background-color: transparent; + display: flex; + gap: 0.25rem; + text-align: left; + width: 100%; + + &:hover, + &:hover.is-selected { + background-color: #ccc6c631; + } + + &.is-selected { + background-color: #ccc6c631; + } + } + } + + .tiptap { + :first-child { + margin-top: 0; + } + + .mention { + background-color: var(--purple-light-dark); + border-radius: 0.4rem; + box-decoration-break: clone; + color: var(--white); + // font-weight: 600; + padding: 0.1rem 0.3rem; + } + } \ No newline at end of file diff --git a/agenthub/public/MentionListV2Light.scss b/agenthub/public/MentionListV2Light.scss new file mode 100644 index 00000000..2540fadc --- /dev/null +++ b/agenthub/public/MentionListV2Light.scss @@ -0,0 +1,45 @@ +/* Dropdown menu */ +.dropdown-menu { + background: var(--white); + border: 1px solid var(--gray-1); + border-radius: 0.7rem; + box-shadow: var(--shadow); + display: flex; + flex-direction: column; + gap: 0.1rem; + overflow: auto; + padding: 0.4rem; + position: relative; + + button { + align-items: center; + background-color: transparent; + display: flex; + gap: 0.25rem; + text-align: left; + width: 100%; + + &:hover, + &:hover.is-selected { + background-color: var(--gray-3); + } + + &.is-selected { + background-color: var(--gray-2); + } + } + } + + .tiptap { + :first-child { + margin-top: 0; + } + + .mention { + background-color: var(--purple-light); + border-radius: 0.4rem; + box-decoration-break: clone; + color: var(--purple); + padding: 0.1rem 0.3rem; + } + } \ No newline at end of file diff --git a/agenthub/styles/global-stylesheet.css b/agenthub/styles/global-stylesheet.css index 464688e1..79ad28a0 100644 --- a/agenthub/styles/global-stylesheet.css +++ b/agenthub/styles/global-stylesheet.css @@ -16902,4 +16902,26 @@ pre > .relative > .rounded-b-md { border-radius: 0 !important; border-bottom-left-radius: 0.3em !important; border-bottom-right-radius: 0.3em !important; +} + +:root { + --white: #FFF; + --black: #2E2B29; + --black-contrast: #110F0E; + --gray-1: rgba(61, 37, 20, .05); + --gray-2: rgba(61, 37, 20, .08); + --gray-3: rgba(61, 37, 20, .12); + --gray-4: rgba(53, 38, 28, .3); + --gray-5: rgba(28, 25, 23, .6); + --green: #22C55E; + --purple: #6A00F5; + --purple-contrast: #5800CC; + --purple-light: rgba(88, 5, 255, .05); + --purple-light-dark: rgba(88, 5, 255, .3); + --yellow-contrast: #FACC15; + --yellow: rgba(250, 204, 21, .4); + --yellow-light: #FFFAE5; + --red: #FF5C33; + --red-light: #FFEBE5; + --shadow: 0px 12px 33px 0px rgba(0, 0, 0, .06), 0px 3.618px 9.949px 0px rgba(0, 0, 0, .04) } \ No newline at end of file From 26f16b31700209d1c1167306bf6f5459aeb9b82e Mon Sep 17 00:00:00 2001 From: BRama10 Date: Mon, 21 Oct 2024 22:23:27 -0400 Subject: [PATCH 8/8] mentions list --- agenthub/app/agentchat/page.tsx | 269 ++++++++++++------ agenthub/components/chat/editor/Editor.tsx | 16 ++ .../components/chat/editor/MentionListV2.tsx | 1 + agenthub/public/MentionListV2Dark.scss | 90 +++--- agenthub/public/MentionListV2Light.scss | 8 + 5 files changed, 249 insertions(+), 135 deletions(-) diff --git a/agenthub/app/agentchat/page.tsx b/agenthub/app/agentchat/page.tsx index 4550d31e..501772f2 100644 --- a/agenthub/app/agentchat/page.tsx +++ b/agenthub/app/agentchat/page.tsx @@ -8,115 +8,196 @@ import { Message, Chat } from '@/interfaces/agentchat'; import { Sidebar } from '@/components/agentchat/Sidebar'; import { Header } from '@/components/agentchat/Header'; import { MessageList } from '@/components/agentchat/MessageList'; - +import axios from 'axios'; +import { AgentCommand } from '@/components/chat/body/message-box'; +import { baseUrl, serverUrl } from '@/lib/env'; const updateChatName = (chatId: number, newName: string) => { - // setChats(prevChats => - // prevChats.map(chat => - // chat.id === chatId ? { ...chat, name: newName } : chat - // ) - // ); - }; + // setChats(prevChats => + // prevChats.map(chat => + // chat.id === chatId ? { ...chat, name: newName } : chat + // ) + // ); +}; + + - - const ChatInterface: React.FC = () => { - const [messages, setMessages] = useState([]); - const [darkMode, setDarkMode] = useState(false); - const [chats, setChats] = useState([{ id: 1, name: 'General' }]); - const [activeChat, setActiveChat] = useState(1); - const messagesEndRef = useRef(null); - - useEffect(() => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }, [messages]); - - const handleSend = (content: string, attachments: File[]) => { - if (content.trim() || attachments.length > 0) { - const newMessage: Message = { - id: Date.now(), - text: content, - sender: 'user', - timestamp: new Date(), - attachments: attachments.map(file => file.name), - thinking: false - }; - setMessages([...messages, newMessage]); - - // Handle file uploads here (e.g., to a server) - const botMessage: Message = { - id: 3, - text: `Here's a sample response with Markdown: - -# Heading 1 -## Heading 2 - -- List item 1 -- List item 2 - -\`\`\`python -def greet(name): - print(f"Hello, {name}!") - -greet("World") -\`\`\` - -> This is a blockquote. - -**Bold text** and *italic text*.`, - sender: 'bot', - timestamp: new Date(), - thinking: true - }; - setMessages(prevMessages => [...prevMessages, botMessage]); - - setTimeout(() => { - setMessages(prevMessages => [...prevMessages].map(message => { - if (message.id == 3) { - return { ...message, thinking: false }; - } - return message; - })); - }, 3000); + const [messages, setMessages] = useState([]); + const [darkMode, setDarkMode] = useState(false); + const [chats, setChats] = useState([{ id: 1, name: 'General' }]); + const [activeChat, setActiveChat] = useState(1); + const messagesEndRef = useRef(null); + + function parseText(input: string): string { + // Step 1: Replace mention spans with the custom format + let parsed = input.replace(/@[^<]+<\/span>/g, '?>>$1/?>>'); + + // Step 2: Convert
    tags to newlines + parsed = parsed.replace(/]*>/g, '\n'); + + // Step 3: Remove all remaining HTML tags + parsed = parsed.replace(/<[^>]+>/g, ''); + + // Decode HTML entities (e.g., ", &) + parsed = parsed.replace(/"/g, '"') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/'/g, "'"); + + return parsed.trim(); + } + + interface MessageBundle { + name: string; + content: string; + } + + + function parseNamedContent(inputString: string) { + // Regular expression to match the pattern ?>>Name/?>>\s*Content + const regex = /\?>>(.*?)\/?>>([^?]*)/g; + const results = []; + + // Find all matches + let match; + while ((match = regex.exec(inputString)) !== null) { + // Extract name and content, trim whitespace + const name = match[1].trim().slice(0, -2); + // Preserve newlines in content but trim surrounding whitespace + const content = match[2].replace(/^\s+|\s+$/g, ''); + + results.push({ + name, + content + }); + } + + return results; + } + + // Ex + + + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [messages]); + + const handleSend = async (content: string, attachments: File[]) => { + if (content.trim() || attachments.length > 0) { + const newMessage: Message = { + id: Date.now(), + text: content, + sender: 'user', + timestamp: new Date(), + attachments: attachments.map(file => file.name), + thinking: false + }; + setMessages([...messages, newMessage]); + + let messageId = Date.now(); + + // Handle file uploads here (e.g., to a server) + const botMessage: Message = { + id: messageId, + text: ``, + sender: 'bot', + timestamp: new Date(), + thinking: true + }; + + setMessages(prevMessages => [...prevMessages, botMessage]); + + const res = await _(parseNamedContent(parseText(content))[0] as AgentCommand) + + setMessages(prevMessages => [...prevMessages].map(message => { + if (message.id == messageId) { + return { ...message, thinking: false }; } - }; + return res.content; + })); + } + }; - const addChat = () => { - const newChat: Chat = { id: Date.now(), name: `Chat ${chats.length + 1}` }; - setChats([...chats, newChat]); - setActiveChat(newChat.id); - }; + const addChat = () => { + const newChat: Chat = { id: Date.now(), name: `Chat ${chats.length + 1}` }; + setChats([...chats, newChat]); + setActiveChat(newChat.id); + }; - const mounted = useMounted(); - - return ( -
    - -
    -
    - -
    - {mounted && } -
    - -
    -
    + const _ = async (command: AgentCommand) => { + const addAgentResponse = await axios.post(`${baseUrl}/api/proxy`, { + type: 'POST', + url: `${serverUrl}/add_agent`, + payload: { + agent_name: command.name, + task_input: command.content, + } + }); + + console.log(addAgentResponse.data); + + // Wait for 1050ms + await new Promise(resolve => setTimeout(resolve, 1050)); + + let recent_response: any; + + try { + // Second request: Execute agent + const executeAgentResponse = await axios.post(`${baseUrl}/api/proxy`, { + type: 'GET', + url: `${serverUrl}/execute_agent?pid=${addAgentResponse.data.pid}`, + }); + + console.log(executeAgentResponse.data); + recent_response = executeAgentResponse.data.response.result.content; + + if (typeof recent_response !== 'string') { + recent_response = "Agent Had Difficulty Thinking" + } + } catch (e) { + recent_response = "Agent Had Difficulty Thinking" + } + + + //return recent_response + return { + name: command.name, + content: recent_response + }; + } + + const mounted = useMounted(); + + return ( +
    + +
    +
    + +
    + {mounted && }
    - ); + +
    +
    +
    + ); }; diff --git a/agenthub/components/chat/editor/Editor.tsx b/agenthub/components/chat/editor/Editor.tsx index 0fc16072..0bc5128e 100644 --- a/agenthub/components/chat/editor/Editor.tsx +++ b/agenthub/components/chat/editor/Editor.tsx @@ -12,6 +12,8 @@ import { ActionIcon, Group, Paper, Text as MantineText, useMantineTheme, ScrollA import { Send, Plus, X, FileIcon } from 'lucide-react'; import suggestion from './Suggestion' +import { baseUrl, serverUrl } from '@/lib/env'; +import axios from 'axios'; export interface ChatEditorProps { onSend: (content: string, attachments: File[]) => void; @@ -26,6 +28,20 @@ export const ChatEditor: React.FC = ({ onSend, darkMode }) => { const [hoverIndex, setHoverIndex] = useState(null); const fileInputRef = useRef(null); const theme = useMantineTheme(); + const [agents, setAgents] = useState([]); + + useEffect(() => { + + + const _ = async () => { + const response = await axios.post(`${baseUrl}/api/proxy`, { + type: 'GET', + url: `${serverUrl}/get_all_agents`, + }); + setAgents(response.data.agents) + } + _() + }, []) let currentStyleElement: HTMLStyleElement | null = null; diff --git a/agenthub/components/chat/editor/MentionListV2.tsx b/agenthub/components/chat/editor/MentionListV2.tsx index 096f8605..a19a5cef 100644 --- a/agenthub/components/chat/editor/MentionListV2.tsx +++ b/agenthub/components/chat/editor/MentionListV2.tsx @@ -23,6 +23,7 @@ export default forwardRef((props: any, ref: any) => { } const enterHandler = (e: any) => { + e.stopImmediatePropagation(); e.preventDefault(); e.stopPropagation(); selectItem(selectedIndex) diff --git a/agenthub/public/MentionListV2Dark.scss b/agenthub/public/MentionListV2Dark.scss index afa51745..0f082ff4 100644 --- a/agenthub/public/MentionListV2Dark.scss +++ b/agenthub/public/MentionListV2Dark.scss @@ -1,47 +1,55 @@ /* Dropdown menu */ .dropdown-menu { - background: rgb(31, 41, 55); - border: 1px solid var(--gray-4); - border-radius: 0.7rem; - box-shadow: var(--shadow); + background: rgb(31, 41, 55); + border: 1px solid var(--gray-4); + border-radius: 0.7rem; + box-shadow: var(--shadow); + display: flex; + flex-direction: column; + gap: 0.1rem; + overflow: auto; + padding: 0.4rem; + position: relative; + color: var(--white); + + button { + align-items: center; + background-color: transparent; display: flex; - flex-direction: column; - gap: 0.1rem; - overflow: auto; - padding: 0.4rem; - position: relative; - color: var(--white); - - button { - align-items: center; - background-color: transparent; - display: flex; - gap: 0.25rem; - text-align: left; - width: 100%; - - &:hover, - &:hover.is-selected { - background-color: #ccc6c631; - } - - &.is-selected { - background-color: #ccc6c631; - } - } - } + gap: 0.25rem; + text-align: left; + width: 100%; - .tiptap { - :first-child { - margin-top: 0; + &:hover, + &:hover.is-selected { + background-color: #ccc6c631; } - - .mention { - background-color: var(--purple-light-dark); - border-radius: 0.4rem; - box-decoration-break: clone; - color: var(--white); - // font-weight: 600; - padding: 0.1rem 0.3rem; + + &.is-selected { + background-color: #ccc6c631; } - } \ No newline at end of file + } +} + +.tiptap { + :first-child { + margin-top: 0; + } + .mention { + background-color: var(--purple-light-dark); + border-radius: 0.4rem; + box-decoration-break: clone; + color: var(--white); + // font-weight: 600; + padding: 0.1rem 0.3rem; + } +} + +.mention { + background-color: var(--purple-light-dark); + border-radius: 0.4rem; + box-decoration-break: clone; + color: var(--white); + // font-weight: 600; + padding: 0.1rem 0.3rem; +} diff --git a/agenthub/public/MentionListV2Light.scss b/agenthub/public/MentionListV2Light.scss index 2540fadc..9cbdf107 100644 --- a/agenthub/public/MentionListV2Light.scss +++ b/agenthub/public/MentionListV2Light.scss @@ -42,4 +42,12 @@ color: var(--purple); padding: 0.1rem 0.3rem; } + } + + .mention { + background-color: var(--purple-light); + border-radius: 0.4rem; + box-decoration-break: clone; + color: var(--purple); + padding: 0.1rem 0.3rem; } \ No newline at end of file