├── .gitignore ├── components ├── Sidebar │ ├── components │ │ ├── activateTab │ │ │ ├── index.js │ │ │ └── modules │ │ │ │ ├── ChatMessageList.js │ │ │ │ ├── RelatedQuestions.js │ │ │ │ ├── TagButton.js │ │ │ │ ├── answerQuestion.js │ │ │ │ ├── relatedLoading.js │ │ │ │ └── textarea.js │ │ ├── common │ │ │ ├── activateBar.js │ │ │ ├── loading.js │ │ │ ├── modelSelect.js │ │ │ ├── modelSelect2.js │ │ │ ├── modelSelect3.js │ │ │ ├── modelSelectSource.js │ │ │ └── unenoughBalance.js │ │ ├── deepSearch │ │ │ ├── index.js │ │ │ └── modules │ │ │ │ ├── OptionsMenu.js │ │ │ │ ├── TypeWriter.js │ │ │ │ ├── actionButton.js │ │ │ │ ├── inputArea.js │ │ │ │ └── placeHolder.js │ │ ├── networkPage │ │ │ ├── index.js │ │ │ ├── modules │ │ │ │ ├── RelatedQuestions.js │ │ │ │ ├── messageRender.js │ │ │ │ ├── modelSelect.js │ │ │ │ ├── placeHolder.js │ │ │ │ ├── responseLoading.js │ │ │ │ └── timeLine.js │ │ │ └── utils │ │ │ │ ├── api.js │ │ │ │ ├── documentProcess.js │ │ │ │ ├── index.js │ │ │ │ ├── streamUtils.js │ │ │ │ └── thingAgent.js │ │ ├── settingPage │ │ │ ├── index.js │ │ │ ├── modules │ │ │ │ ├── about.js │ │ │ │ ├── baseModel.js │ │ │ │ ├── baseModel │ │ │ │ │ ├── index.js │ │ │ │ │ └── modules │ │ │ │ │ │ ├── checkOption.js │ │ │ │ │ │ └── pointCard.js │ │ │ │ ├── check.js │ │ │ │ ├── login.js │ │ │ │ ├── logo.js │ │ │ │ ├── modelSetting.js │ │ │ │ ├── navBar.js │ │ │ │ ├── notification.js │ │ │ │ ├── qrcode.js │ │ │ │ └── shareAI.js │ │ │ └── utils │ │ │ │ └── check.js │ │ ├── taskList │ │ │ ├── index.js │ │ │ └── modules │ │ │ │ └── index.js │ │ ├── updateModel.js │ │ └── welcomePage │ │ │ ├── index.js │ │ │ └── modules │ │ │ ├── check.js │ │ │ ├── couldNotGetWebContent.js │ │ │ ├── noLogin.js │ │ │ ├── parseMarkdown.js │ │ │ └── question.js │ ├── config │ │ └── models.js │ ├── contants │ │ ├── activateBar.js │ │ ├── deepSearchSvg.js │ │ └── pageTransltions.js │ ├── core │ │ ├── agent.js │ │ ├── callAi.js │ │ ├── check.js │ │ ├── deepInvolve.js │ │ ├── involve.js │ │ ├── makQuestionMore.js │ │ ├── search.js │ │ ├── utils.js │ │ └── webSearch.js │ ├── hooks │ │ ├── useCheckBalance.js │ │ ├── useCheckLoginTime.js │ │ ├── useCheckUpdate.js │ │ ├── useDeepSearch.js │ │ ├── useFetchPointCost.js │ │ ├── useGessingTime.js │ │ ├── useMessageHander.js │ │ ├── useMessageHandler.js │ │ ├── useNotesChat.js │ │ ├── useRelatedQuestions.js │ │ ├── useScreenshotHandler.js │ │ ├── useSearchEngine.js │ │ ├── useSeetingHandler.js │ │ ├── useSummary.js │ │ ├── useTime.js │ │ ├── useTimeGussing.js │ │ ├── useTimer.js │ │ └── useUrlContentCache.js │ ├── index.js │ ├── services │ │ ├── ai.js │ │ └── aiText.js │ └── utils │ │ ├── breathLight.js │ │ ├── chat.js │ │ ├── contentProcessor.js │ │ ├── getSystemPrompt.js │ │ ├── index.js │ │ ├── netWork.js │ │ ├── network │ │ └── index.js │ │ └── service.js ├── Welcome │ ├── hooks │ │ └── useCountdown.js │ ├── index.js │ ├── modules │ │ ├── StepBar.js │ │ ├── confirmModal.js │ │ └── logo.js │ ├── service │ │ └── login.js │ └── step │ │ ├── First.js │ │ ├── FirstRight.js │ │ ├── Second.js │ │ ├── SecondRight.js │ │ └── importModal.js └── config │ └── index.js ├── hooks ├── useChat.js └── useMessageHandler.ts ├── index.json ├── next-env.d.ts ├── out.js ├── package.json ├── pages ├── _app.js ├── sidepanel.js └── welcome.js ├── pnpm-lock.yaml ├── point.json ├── pointPost.json ├── postcss.config.js ├── public ├── Readability.js ├── background.js ├── content-script.js ├── content.js ├── icons │ ├── logo-128.png │ └── logo.png ├── index.css ├── inject.js ├── js │ ├── d3.js │ ├── hightlight.js │ ├── markmap-autoloader.js │ ├── markmap-lib.js │ ├── markmap-view.js │ └── toolbar.js ├── manifest.json ├── markmap.html ├── markmap.js ├── storage.js ├── turndown.js └── utils.js ├── readme.md ├── styles └── globals.css ├── tailwind.config.js └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.next 3 | /node_modules 4 | /.env 5 | /out 6 | out.pem 7 | out.zip 8 | 9 | .history 10 | .history/* 11 | 12 | history 13 | history/* 14 | -------------------------------------------------------------------------------- /components/Sidebar/components/activateTab/index.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from "react"; 2 | import { ChatMessageList } from "./modules/chatMessageList"; 3 | import { TextareaRef } from "./modules/textarea"; 4 | 5 | const ActivateTabChatPanel = ({ 6 | pageContent, 7 | useInput, 8 | selectedModelProvider, 9 | selectedModelIsSupportsImage, 10 | setSelectedModelProvider, 11 | setSelectedModelIsSupportsImage, 12 | getCurrentUrlMessages, 13 | isAiThinking, 14 | copiedMessageId, 15 | onCopy, 16 | onRetry, 17 | onSubmit, 18 | clearCurrentUrlMessages, 19 | currentUrl, 20 | selectedModel, 21 | setSelectedModel, 22 | isContentReady, 23 | setActivatePage, 24 | currentUrlRelatedQuestions, 25 | currentUrlLoading, 26 | thinkingTimeMap, 27 | }) => { 28 | const handleQuestionClick = useCallback( 29 | (question, reason_content) => { 30 | if (!question) return; 31 | 32 | const message = { 33 | role: "user", 34 | content: question, 35 | isFromSuggestion: true, 36 | }; 37 | 38 | if (reason_content) { 39 | message.reason_content = reason_content; 40 | } 41 | 42 | onSubmit([message], false); 43 | }, 44 | [onSubmit] 45 | ); 46 | 47 | const handleSubmit = useCallback( 48 | (messages, isRetry = false) => { 49 | onSubmit(messages, isRetry); 50 | }, 51 | [onSubmit] 52 | ); 53 | 54 | return ( 55 |
56 |
57 | 69 |
70 |
71 | 88 |
89 |
90 | ); 91 | }; 92 | 93 | export { ActivateTabChatPanel }; 94 | -------------------------------------------------------------------------------- /components/Sidebar/components/activateTab/modules/RelatedQuestions.js: -------------------------------------------------------------------------------- 1 | import { HelpCircle } from "lucide-react"; 2 | import { QuestionLoading } from "./relatedLoading"; 3 | 4 | const RelatedQuestions = ({ loading, setQuery, questions }) => { 5 | return ( 6 | <> 7 | {loading && ( 8 |
9 |
10 | 11 | 猜你想问 12 |
13 | 14 |
15 | )} 16 | 17 | {!loading && questions?.length > 0 && ( 18 |
19 |
20 | 21 | 猜你想问 22 |
23 |
24 | {questions.map((question, index) => ( 25 | 33 | ))} 34 |
35 |
36 | )} 37 | 38 | ); 39 | }; 40 | 41 | export { RelatedQuestions }; 42 | -------------------------------------------------------------------------------- /components/Sidebar/components/activateTab/modules/TagButton.js: -------------------------------------------------------------------------------- 1 | import { memo } from "react"; 2 | import { Tooltip } from "react-tooltip"; 3 | 4 | const TagButton = memo(({ tag, isFirstTag, isAiThinking, onTagClick, useInput }) => { 5 | const getTagButtonClassName = () => { 6 | const baseClass = `tags-button-${tag.type}`; 7 | const commonClasses = ` 8 | p-2 rounded-xl 9 | flex items-center justify-center 10 | transition-all duration-200 11 | shadow-sm hover:shadow-md 12 | `; 13 | 14 | const disabledStyle = "bg-gray-100 text-gray-400 cursor-not-allowed"; 15 | 16 | const activeStyles = isFirstTag 17 | ? "bg-indigo-500 text-white hover:bg-indigo-600" 18 | : "bg-white text-gray-600 hover:text-indigo-600 hover:bg-indigo-50 border border-gray-200"; 19 | 20 | return `${baseClass} ${commonClasses} ${ 21 | tag.disabled || isAiThinking ? disabledStyle : activeStyles 22 | }`; 23 | }; 24 | 25 | return ( 26 | 43 | ); 44 | }); 45 | 46 | TagButton.displayName = "TagButton"; 47 | 48 | export { TagButton }; 49 | -------------------------------------------------------------------------------- /components/Sidebar/components/activateTab/modules/answerQuestion.js: -------------------------------------------------------------------------------- 1 | import { motion } from "framer-motion"; 2 | import { HelpCircle } from "lucide-react"; 3 | import { memo } from "react"; 4 | 5 | const QuestionLoading = () => { 6 | const ballCount = 5; 7 | const balls = Array.from({ length: ballCount }, (_, index) => ( 8 | 23 | )); 24 | 25 | return ( 26 |
27 |
{balls}
28 |
29 | ); 30 | }; 31 | 32 | const AnswerQuestion = memo( 33 | ({ relatedQuestions, onQuestionClick, isRelatedQuestions, isShowRelatedQuestions }) => { 34 | return ( 35 | <> 36 | {isRelatedQuestions && ( 37 |
38 |
39 | 40 | 相关问题 41 |
42 | 43 |
44 | )} 45 | 46 | {!isRelatedQuestions && ( 47 |
48 |
49 | 50 | 相关问题 51 |
52 |
53 | {relatedQuestions?.map((question, index) => ( 54 | 62 | ))} 63 |
64 |
65 | )} 66 | 67 | ); 68 | } 69 | ); 70 | 71 | export { AnswerQuestion }; 72 | -------------------------------------------------------------------------------- /components/Sidebar/components/activateTab/modules/relatedLoading.js: -------------------------------------------------------------------------------- 1 | import { motion } from "framer-motion"; 2 | import React from "react"; 3 | 4 | export const QuestionLoading = () => { 5 | const ballCount = 5; 6 | 7 | const balls = Array.from({ length: ballCount }, (_, index) => ( 8 | 23 | )); 24 | 25 | return ( 26 |
27 |
{balls}
28 |
29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /components/Sidebar/components/common/activateBar.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { Tooltip } from "react-tooltip"; 3 | import { ACTIVATE_ITEMS } from "../../contants/activateBar"; 4 | 5 | const ActivateBar = ({ activatePage, setActivatePage }) => { 6 | const topItems = ACTIVATE_ITEMS.slice(0, 4); 7 | const bottomItems = ACTIVATE_ITEMS.slice(4); 8 | 9 | const renderActivateItem = useCallback( 10 | ({ id, icon: Icon, tooltip }) => { 11 | const isActive = activatePage === id; 12 | 13 | return ( 14 | 38 | ); 39 | }, 40 | [activatePage, setActivatePage] 41 | ); 42 | 43 | return ( 44 |
45 |
{topItems.map((item) => renderActivateItem(item))}
46 |
{bottomItems.map((item) => renderActivateItem(item))}
47 |
48 | ); 49 | }; 50 | 51 | export { ActivateBar }; 52 | -------------------------------------------------------------------------------- /components/Sidebar/components/common/loading.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { motion } from "framer-motion"; 3 | 4 | export const Loading = () => { 5 | const ballCount = 5; 6 | 7 | const balls = Array.from({ length: ballCount }, (_, index) => ( 8 | 23 | )); 24 | 25 | return ( 26 |
27 |
{balls}
28 |
29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /components/Sidebar/components/common/modelSelect2.js: -------------------------------------------------------------------------------- 1 | import { ChevronDown } from "lucide-react"; 2 | import { useEffect, useRef, useState } from "react"; 3 | import { setCurrentSearchSource } from "../../../../public/storage"; 4 | import { useSearchEngine } from "../../hooks/useSearchEngine"; 5 | 6 | const ModelSelector2 = ({ useInput }) => { 7 | const { getSearchSource, handleSearchSourceChange, getSearchSourceIcon, getSearchSourceName } = 8 | useSearchEngine(); 9 | const [isOpen, setIsOpen] = useState(false); 10 | const [modelList, setModelList] = useState([]); 11 | const dropdownRef = useRef(null); 12 | 13 | useEffect(() => { 14 | const initializeModelList = async () => { 15 | const models = getSearchSource(); 16 | setModelList(models); 17 | }; 18 | 19 | initializeModelList(); 20 | }, []); 21 | 22 | useEffect(() => { 23 | const handleClickOutside = (event) => { 24 | if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { 25 | setIsOpen(false); 26 | } 27 | }; 28 | 29 | document.addEventListener("mousedown", handleClickOutside); 30 | return () => document.removeEventListener("mousedown", handleClickOutside); 31 | }, [setIsOpen]); 32 | 33 | const handleModelSelect = (model) => { 34 | handleSearchSourceChange(model); 35 | setCurrentSearchSource(model.value); 36 | }; 37 | 38 | const handleClick = () => { 39 | setIsOpen(!isOpen); 40 | }; 41 | 42 | useEffect(() => { 43 | const handleClickOutside = (event) => { 44 | if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { 45 | setIsOpen(false); 46 | } 47 | }; 48 | 49 | document.addEventListener("mousedown", handleClickOutside); 50 | return () => document.removeEventListener("mousedown", handleClickOutside); 51 | }, [setIsOpen]); 52 | 53 | return ( 54 |
59 | {isOpen && ( 60 |
61 |
62 | 68 |
69 |
70 | )} 71 |
80 | {getSearchSourceIcon()} 81 |
82 | {getSearchSourceName()} 83 |
84 | 85 |
86 |
87 | ); 88 | }; 89 | 90 | const ModelGroup = ({ models, useInput = true, onModelSelect, setIsOpen }) => { 91 | return ( 92 | <> 93 | {models.map((model) => ( 94 |
{ 99 | onModelSelect(model); 100 | setIsOpen(false); 101 | }} 102 | > 103 | {model.selectIcon} 104 | {model.name} 105 |
106 | ))} 107 | 108 | ); 109 | }; 110 | 111 | export { ModelSelector2 }; 112 | -------------------------------------------------------------------------------- /components/Sidebar/components/common/modelSelect3.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shareAI-lab/Super2Brain-Extension/147f30193af983643e3a6bf77e56a210c0a91be3/components/Sidebar/components/common/modelSelect3.js -------------------------------------------------------------------------------- /components/Sidebar/components/common/modelSelectSource.js: -------------------------------------------------------------------------------- 1 | import { useSearchEngine } from "../../hooks/useSearchEngine"; 2 | import { useState } from "react"; 3 | 4 | const modelSelectSource = () => { 5 | const { searchSource, handleSearchSourceChange, getSearchSource } = 6 | useSearchEngine(); 7 | const [isOpen, setIsOpen] = useState(false); 8 | 9 | const toggleDropdown = () => { 10 | setIsOpen(!isOpen); 11 | }; 12 | 13 | const handleOptionClick = (value) => { 14 | handleSearchSourceChange(value); 15 | setIsOpen(false); 16 | }; 17 | 18 | return ( 19 |
20 |
21 | {searchSource} 22 |
23 | {isOpen && ( 24 |
25 | {getSearchSource().map((source) => ( 26 |
handleOptionClick(source.value)} 29 | > 30 | {source.name} 31 |
32 | ))} 33 |
34 | )} 35 |
36 | ); 37 | }; 38 | 39 | export { modelSelectSource }; 40 | -------------------------------------------------------------------------------- /components/Sidebar/components/common/unenoughBalance.js: -------------------------------------------------------------------------------- 1 | import { X, Wallet, AlertCircle } from "lucide-react"; 2 | import { QRCodeImage } from "../settingPage/modules/qrcode"; 3 | 4 | const UnenoughBalance = ({ 5 | isShowModal, 6 | setIsShowModal, 7 | isAddPoint, 8 | setIsAddPoint, 9 | }) => { 10 | const handleCloseModal = () => { 11 | setIsShowModal(false); 12 | setIsAddPoint(false); 13 | }; 14 | 15 | return ( 16 | <> 17 | {isShowModal && ( 18 |
19 |
23 | 24 |
28 |
29 |
30 |
31 | {isAddPoint ? ( 32 | 33 | ) : ( 34 | 35 | )} 36 |
37 |

38 | {isAddPoint ? "充值积分" : "余额不足"} 39 |

40 |
41 | 47 |
48 | 49 |
50 |
51 | 52 |
53 |

54 | {isAddPoint 55 | ? "想要更多积分,请联系管理员进行充值" 56 | : "您目前的账户余额不足,请联系管理员充值"} 57 |

58 |
59 | 充值二维码 64 |
65 |
66 |
67 |
68 | 69 |
70 | 80 |
81 |
82 |
83 | )} 84 | 85 | ); 86 | }; 87 | 88 | export { UnenoughBalance }; 89 | -------------------------------------------------------------------------------- /components/Sidebar/components/deepSearch/modules/OptionsMenu.js: -------------------------------------------------------------------------------- 1 | import { useState, useRef, useEffect } from "react"; 2 | import { 3 | Settings, 4 | HelpCircle, 5 | RefreshCw, 6 | Download, 7 | Copy, 8 | FileText, 9 | } from "lucide-react"; 10 | import { jsPDF } from "jspdf"; 11 | import html2canvas from "html2canvas"; 12 | 13 | const OptionsMenu = ({ isOpen, setIsOpen, content }) => { 14 | const menuRef = useRef(null); 15 | const [copyStatus, setCopyStatus] = useState(""); 16 | 17 | useEffect(() => { 18 | const handleClickOutside = (event) => { 19 | if (menuRef.current && !menuRef.current.contains(event.target)) { 20 | setIsOpen(false); 21 | } 22 | }; 23 | 24 | document.addEventListener("mousedown", handleClickOutside); 25 | return () => document.removeEventListener("mousedown", handleClickOutside); 26 | }, [setIsOpen]); 27 | 28 | const copyConversation = async () => { 29 | try { 30 | await navigator.clipboard.writeText(content); 31 | setCopyStatus("已复制"); 32 | setTimeout(() => setCopyStatus(""), 2000); 33 | } catch (err) { 34 | setCopyStatus("复制失败"); 35 | console.error("复制失败:", err); 36 | } 37 | }; 38 | 39 | const exportToPDF = async () => { 40 | try { 41 | // 创建一个临时的 div 元素 42 | const element = document.createElement("div"); 43 | element.innerHTML = ` 44 |
45 |

DeepSeek 对话记录

46 |
47 | ${content} 48 |
49 |
50 | 导出时间:${new Date().toLocaleString()} 51 |
52 |
53 | `; 54 | document.body.appendChild(element); 55 | 56 | // 使用 html2canvas 将内容转换为图片 57 | const canvas = await html2canvas(element, { 58 | scale: 2, 59 | useCORS: true, 60 | logging: false, 61 | }); 62 | 63 | // 创建 PDF 64 | const pdf = new jsPDF("p", "mm", "a4"); 65 | const imgWidth = 210; // A4 宽度 66 | const imgHeight = (canvas.height * imgWidth) / canvas.width; 67 | 68 | pdf.addImage( 69 | canvas.toDataURL("image/jpeg", 0.98), 70 | "JPEG", 71 | 0, 72 | 0, 73 | imgWidth, 74 | imgHeight 75 | ); 76 | 77 | // 保存 PDF 78 | pdf.save(`对话记录_${new Date().toISOString().slice(0, 10)}.pdf`); 79 | 80 | // 清理临时元素 81 | document.body.removeChild(element); 82 | } catch (err) { 83 | console.error("PDF导出失败:", err); 84 | } 85 | }; 86 | 87 | const menuItems = [ 88 | { 89 | icon: , 90 | label: "重置对话", 91 | onClick: () => console.log("重置被点击"), 92 | }, 93 | { 94 | icon: , 95 | label: copyStatus || "复制回答", 96 | onClick: copyConversation, 97 | }, 98 | { 99 | icon: , 100 | label: "导出PDF", 101 | onClick: exportToPDF, 102 | }, 103 | ]; 104 | 105 | return ( 106 |
107 | {isOpen && ( 108 |
109 |
110 | {menuItems.map((item, index) => ( 111 | 122 | ))} 123 |
124 |
125 | )} 126 |
127 | ); 128 | }; 129 | 130 | export { OptionsMenu }; 131 | -------------------------------------------------------------------------------- /components/Sidebar/components/deepSearch/modules/TypeWriter.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | const TypeWriter = ({ text, onComplete, isPulsing = false, instant = false }) => { 4 | const [displayText, setDisplayText] = useState(""); 5 | const [currentIndex, setCurrentIndex] = useState(0); 6 | 7 | useEffect(() => { 8 | // 如果设置为即时显示,直接显示全部文本 9 | if (instant) { 10 | setDisplayText(text); 11 | setCurrentIndex(text.length); 12 | if (onComplete) { 13 | onComplete(); 14 | } 15 | return; 16 | } 17 | 18 | // 否则使用打字机效果 19 | if (currentIndex < text.length) { 20 | const timer = setTimeout(() => { 21 | setDisplayText((prev) => prev + text[currentIndex]); 22 | setCurrentIndex((prev) => prev + 1); 23 | }, 25); 24 | 25 | return () => clearTimeout(timer); 26 | } else if (onComplete) { 27 | onComplete(); 28 | } 29 | }, [currentIndex, text, instant]); 30 | 31 | return ( 32 | 33 | {displayText} 34 | 35 | ); 36 | }; 37 | 38 | export { TypeWriter }; 39 | -------------------------------------------------------------------------------- /components/Sidebar/components/deepSearch/modules/inputArea.js: -------------------------------------------------------------------------------- 1 | import { Brain, Send } from "lucide-react"; 2 | import { useState } from "react"; 3 | import { Tooltip } from "react-tooltip"; 4 | import { ModelSelector } from "../../common/modelSelect"; 5 | import { ModelSelector2 } from "../../common/modelSelect2"; 6 | const InputArea = ({ 7 | query, 8 | setQuery, 9 | maxDepth, 10 | setMaxDepth, 11 | isLoading, 12 | handleSendMessage, 13 | selectedModel, 14 | setSelectedModel, 15 | selectedModelProvider, 16 | setSelectedModelProvider, 17 | setActivatePage, 18 | setSelectedModelIsSupportsImage, 19 | }) => { 20 | const [dropdownOpen, setDropdownOpen] = useState(false); 21 | 22 | return ( 23 | <> 24 |
25 |
26 |
27 | 28 | AI洞察分析 29 |
30 |
31 | setMaxDepth(parseInt(e.target.value))} 37 | className="w-24 h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-indigo-500" 38 | /> 39 | {maxDepth} 40 |
41 |
42 |
43 |
48 |