浏览代码

mentions & conditional dark mode

BRama10 19 小时之前
父节点
当前提交
4c2b15c752

+ 38 - 0
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<ChatEditorProps> = ({ onSend, darkMode }) => {
   const [attachments, setAttachments] = useState<File[]>([]);
   const [previews, setPreviews] = useState<string[]>([]);
@@ -22,6 +27,33 @@ export const ChatEditor: React.FC<ChatEditorProps> = ({ onSend, darkMode }) => {
   const fileInputRef = useRef<HTMLInputElement>(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<ChatEditorProps> = ({ onSend, darkMode }) => {
       Placeholder.configure({
         placeholder: 'Type a message...',
       }),
+      Mention.configure({
+        HTMLAttributes: {
+          class: 'mention',
+        },
+        suggestion,
+      }),
     ],
     editorProps: {
       attributes: {

+ 70 - 0
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 (
+    <div className="dropdown-menu">
+      {props.items.length
+        ? props.items.map((item: any, index: any) => (
+          <button
+            className={index === selectedIndex ? 'is-selected' : ''}
+            key={index}
+            onClick={() => selectItem(index)}
+          >
+            {item}
+          </button>
+        ))
+        : <div className="item">No result</div>
+      }
+    </div>
+  )
+})

+ 93 - 0
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()
+      },
+    }
+  },
+}

+ 330 - 4
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",

+ 3 - 1
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": {

+ 47 - 0
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;
+    }
+  }

+ 45 - 0
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;
+    }
+  }

+ 22 - 0
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)
 }