Ver código fonte

优化ai-agent工具

zxlie 8 meses atrás
pai
commit
3f586bc6fc

+ 74 - 23
apps/aiagent/fh.ai.js

@@ -3,10 +3,40 @@ import EncodeUtils from '../en-decode/endecode-lib.js';
  * 用零一万物大模型来进行流式问答输出
  */
 let AI = (() => {
-    const defaultKey = 'MWFhZWE0M2Y3ZDBkNDJhNmJhNjMzOTZkOGJlNTA4ZmY=';
+    /**
+     * 用 SiliconFlow 大模型(CoderVM)进行流式问答输出,支持多轮上下文对话
+     * @param {Array} messages 聊天历史数组,每项格式: {role: 'user'|'assistant', content: string}
+     * @param {function} receivingCallback 每次收到新内容时的回调,参数为 message 对象
+     * @param {string} apiKey 可选,API Key
+     * @example
+     * const messages = [
+     *   { role: 'user', content: '你好' },
+     *   { role: 'assistant', content: '你好,有什么可以帮您?' },
+     *   { role: 'user', content: '帮我写个排序算法' }
+     * ];
+     * AI.askCoderLLM(messages, callback);
+     */
+    async function askCoderLLM(messages, receivingCallback, apiKey) {
+        // 默认插入system prompt
+        const systemPrompt = {
+            role: 'system',
+            content: '你是一个专为开发者服务的AI助手。你的目标是精准理解开发者的技术需求,并以最简洁、直接、专业的方式输出高质量代码。请避免无关的解释和冗余描述,只输出开发者真正需要的代码和必要的技术要点说明。遇到不明确的需求时,优先追问关键细节,绝不输出与开发无关的内容。'
+        };
+        let msgs;
+        if (typeof messages === 'string') {
+            // 单轮对话,自动组装为数组
+            msgs = [systemPrompt, { role: 'user', content: messages }];
+        } else if (Array.isArray(messages)) {
+            // 多轮对话,插入system prompt(如未包含)
+            const hasSystemPrompt = messages.some(m => m.role === 'system' && m.content === systemPrompt.content);
+            msgs = hasSystemPrompt ? messages : [systemPrompt, ...messages];
+        } else {
+            // 其他类型,降级为空对话
+            msgs = [systemPrompt];
+        }
 
-    async function streamChatCompletions(prompt,receivingCallback,apiKey) {
-        const url = 'https://api.lingyiwanwu.com/v1/chat/completions';
+        const defaultKey = 'c2stamJ5eGlldmVmdmhnbnBnbGF3cmxlZ25uam9rY25kc3BpYndjZmh1d2Ntbm9jbmxp';
+        const url = 'https://api.siliconflow.cn/v1/chat/completions';
         const options = {
             method: 'POST',
             headers: {
@@ -14,50 +44,71 @@ let AI = (() => {
                 'Authorization': `Bearer ${apiKey || EncodeUtils.base64Decode(defaultKey)}`
             },
             body: JSON.stringify({
-                model: "yi-large",
-                messages: [{ role: "user", content: prompt }],
-                temperature: 0.3,
-                stream: true
+                "model": "Qwen/Qwen2.5-Coder-7B-Instruct",
+                "messages": msgs, // 直接传递多轮历史
+                "stream": true, // 开启流式输出
+                "max_tokens": 4096,
+                "enable_thinking": true,
+                "thinking_budget": 4096,
+                "min_p": 0.05,
+                "stop": [],
+                "temperature": 0.7,
+                "top_p": 0.7,
+                "top_k": 50,
+                "frequency_penalty": 0.5,
+                "n": 1,
+                "response_format": {
+                    "type": "text"
+                }
             })
         };
-    
         try {
             const response = await fetch(url, options);
             if (!response.ok) {
                 throw new Error(`HTTP error! status: ${response.status}`);
             }
-    
-            // 创建一个ReadableStream用于处理流式数据
+            // 处理流式返回(text/event-stream)
             const reader = response.body.getReader();
             const decoder = new TextDecoder('utf-8');
+            let buffer = '';
             let done = false;
-    
+            const msg = {id:'',content:''};
             while (!done) {
                 const { value, done: readerDone } = await reader.read();
                 done = readerDone;
                 if (value) {
-                    // 将接收到的数据块解码为字符串,并假设每一行都是一个独立的JSON对象
-                    const lines = decoder.decode(value, { stream: true }).split('\n').filter(line => line.trim() !== '');
-                    for (const line of lines) {
+                    buffer += decoder.decode(value, { stream: true });
+                    // 以换行分割,逐条处理
+                    let lines = buffer.split('\n');
+                    // 最后一行可能不完整,留到下次
+                    buffer = lines.pop();
+                    for (let line of lines) {
+                        line = line.trim();
+                        if (!line || !line.startsWith('data:')) continue;
+                        let jsonStr = line.replace(/^data:/, '').trim();
+                        if (jsonStr === '[DONE]') continue;
                         try {
-                            // 解析每一行作为单独的JSON对象
-                            const message = JSON.parse(line.replace(/^data:\s+/,''));
-                            receivingCallback && receivingCallback(message);
-                        } catch (jsonError) {
-                            if(line === 'data: [DONE]'){
-                                receivingCallback && receivingCallback(null,true);
+                            let obj = JSON.parse(jsonStr);
+                            if (obj.choices && obj.choices[0] && obj.choices[0].delta) {
+                                msg.id = obj.id;
+                                msg.created = obj.created;
+                                msg.content += obj.choices[0].delta.content;
+                                receivingCallback && receivingCallback(msg);
                             }
+                        } catch (e) {
+                            // 忽略解析失败的片段
                         }
                     }
                 }
             }
+            receivingCallback && receivingCallback(null,true);
         } catch (error) {
-            console.error('Error fetching chat completions:', error);
+            console.error('Error fetching coderVM stream:', error);
         }
     }
     
-    return {askYiLarge: streamChatCompletions};
+    return {askCoderLLM};
 })();
 
 
-export default AI;
+export default AI;

+ 98 - 0
apps/aiagent/index.css

@@ -206,3 +206,101 @@ h3.panel-title {
 .x-xcontent table td {
     border: 1px solid #ccc;
 }
+.x-donate-link {
+    right: 210px;
+    top: 0px;
+}
+.panel-title>a.x-other-tools {
+    top: -2px;
+}
+
+.fh-history-sidebar {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 280px;
+    height: 100vh;
+    background: #fff;
+    border-right: 1px solid #eee;
+    z-index: 10000;
+    box-shadow: 2px 0 8px rgba(0,0,0,0.04);
+    display: flex;
+    flex-direction: column;
+}
+.fh-history-header {
+    padding: 18px 16px 10px 16px;
+    font-size: 18px;
+    font-weight: bold;
+    border-bottom: 1px solid #eee;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+}
+.fh-history-close {
+    background: none;
+    border: none;
+    font-size: 16px;
+    color: #888;
+    cursor: pointer;
+}
+.fh-history-list {
+    flex: 1;
+    overflow-y: auto;
+    padding: 12px 0 12px 0;
+}
+.fh-history-date {
+    font-size: 14px;
+    color: #888;
+    margin: 10px 0 4px 18px;
+}
+.fh-history-list ul {
+    list-style: none;
+    margin: 0 0 0 8px;
+    padding: 0;
+}
+.fh-history-list li {
+    padding: 8px 18px;
+    cursor: pointer;
+    border-radius: 6px;
+    transition: background 0.2s;
+    font-size: 15px;
+    color: #333;
+}
+.fh-history-list li:hover {
+    background: #f5f7fa;
+}
+.fh-history-theme {
+    font-weight: 500;
+    color: #222;
+}
+.fh-main-shrink {
+    margin-left: 280px !important;
+    transition: margin-left 0.2s;
+}
+
+.fh-nav-btn {
+    display: inline-block;
+    margin-left: 18px;
+    padding: 3px 14px 3px 10px;
+    border-radius: 18px;
+    background: #f5f7fa;
+    color: #333;
+    font-size: 12px;
+    font-weight: normal;
+    text-decoration: none;
+    transition: background 0.2s, color 0.2s;
+    border: 1px solid #e0e0e0;
+    box-shadow: 0 1px 2px rgba(0,0,0,0.03);
+    vertical-align: middle;
+}
+.fh-nav-btn:hover {
+    background: #e6f0ff;
+    color: #1976d2;
+    border-color: #b3d1ff;
+}
+.x-history-link .icon-clock {
+    color: #1976d2;
+}
+.x-newchat-link .icon-plus-circle {
+    color: #43a047;
+}

+ 59 - 47
apps/aiagent/index.html

@@ -1,7 +1,7 @@
 <!DOCTYPE HTML>
 <html lang="zh-CN">
 <head>
-    <title>AI,请帮帮忙</title>
+    <title>AI(智能助手)</title>
     <meta charset="UTF-8">
     <link rel="shortcut icon" href="../static/img/favicon.ico">
     <link rel="stylesheet" href="index.css"/>
@@ -15,55 +15,66 @@
         <div class="panel-heading">
             <h3 class="panel-title">
                 <a href="https://www.baidufe.com/fehelper/index/index.html" target="_blank" class="x-a-high">
-                    <img src="../static/img/fe-16.png" alt="fehelper"/> FeHelper:</a>AI,请帮帮忙
-            </h3>
+                    <img src="../static/img/fe-16.png" alt="fehelper"/> FeHelper:</a>AI(智能助手)
+                    <a href="#" class="x-donate-link" @click="openDonateModal($event)"><i class="nav-icon">❤&nbsp;</i>打赏鼓励</a>
+                    <a class="x-other-tools" @click="openOptionsPage($event)"><i class="icon-plus-circle"></i> 探索更多实用工具 <span class="tool-market-badge">工具市场</span></a>
+                    <a href="#" class="x-history-link fh-nav-btn" @click="onHistoryClick($event)">
+                        <i class="icon-clock" style="margin-right:4px;font-size:18px;vertical-align:-2px;"></i>历史对话
+                    </a>
+                    <a href="#" class="x-newchat-link fh-nav-btn" @click="startNewChat($event)">
+                        <i class="icon-plus-circle" style="margin-right:4px;font-size:18px;vertical-align:-2px;"></i>开启新对话
+                    </a>
+                </h3>
+            </div>
         </div>
-    </div>
-    <div class="panel-body">
-        <div class="row mod-inputs box-prompt">
-            <form class="ui-mt-10" @submit.prevent="goChat">
-                <textarea type="text" id="prompt" ref="prompt" v-model="prompt" class="form-control" placeholder="你有什么内容想要咨询AI的?"
-                    data-key="c2stNEJUbWRxYW00eENTUXJXRnlNajFUM0JsYmtGSlVlbDJnZ2dGbjI5MVBKQVdzZnZR"></textarea>
-                <input class="btn btn-sm btn-primary btn-chat" type="button" value="发送" @click="goChat">
-            </form>
+        <div v-if="showHistoryPanel" class="fh-history-sidebar">
+            <div class="fh-history-header">
+                <span>历史对话</span>
+                <button class="fh-history-close" @click="showHistoryPanel = false">关闭</button>
+            </div>
+            <div class="fh-history-list">
+                <template v-for="(day, date) in groupedHistory">
+                    <div class="fh-history-date">{{date}}</div>
+                    <ul>
+                        <li v-for="item in day" @click="loadHistory(item)">
+                            <span class="fh-history-theme">{{item.theme || item.message}}</span>
+                        </li>
+                    </ul>
+                </template>
+            </div>
         </div>
-        <div class="row mod-inputs box-result" ref="boxResult">
-            <div class="row box-tips" v-if="!hideDemo">
-                <div>你好,我是你的专属AI助理,不管你是要查BUG、写代码、还是要咨询什么技术问题,你都可以跟我说,我会竭尽全力帮你解决,比如你可以这样问我👇👇👇</div>
-                <ul class="x-demos clearfix">
-                    <li v-for="demo in demos" @click="sendMessage(demo)">{{demo}}</li>
-                </ul>
+        <div :class="['panel-body', showHistoryPanel ? 'fh-main-shrink' : '']">
+            <div class="row mod-inputs box-prompt">
+                <form class="ui-mt-10" @submit.prevent="goChat">
+                    <textarea type="text" id="prompt" ref="prompt" v-model="prompt" class="form-control" placeholder="你有什么内容想要咨询AI的?"
+                        data-key="c2stNEJUbWRxYW00eENTUXJXRnlNajFUM0JsYmtGSlVlbDJnZ2dGbjI5MVBKQVdzZnZR"></textarea>
+                    <input class="btn btn-sm btn-primary btn-chat" type="button" value="发送" @click="goChat">
+                </form>
             </div>
-            <div class="row box-message">
-                <table>
-                    <template v-for="msg in history">
-                        <tr class="x-from-fh">
-                            <td class="td-icon x-me"><img src="../static/img/me.png" alt="me"/></td>
-                            <td class="td-content"><b class="x-time">{{msg.sendTime}} </b><div class="x-xcontent">{{msg.message}}</div></td>
-                        </tr>
-                        <tr class="x-back-gpt">
-                            <td class="td-icon">
-                                <img src="../static/img/fe-16.png" alt="fehelper"/></td></td>
-                            <td class="td-content"><b class="x-time">{{msg.respTime}}</b>
-                                <div :id="msg.id" class="x-xcontent" v-html="msg.respContent"></div>
-                            </td>
-                        </tr>
-                    </template>
-
-                    <template v-if="respResult.id">
-                        <tr class="x-from-fh">
-                            <td class="td-icon x-me"><img src="../static/img/me.png" alt="me"/></td>
-                            <td class="td-content"><b class="x-time">{{respResult.sendTime}} </b><div class="x-xcontent">{{respResult.message}}</div></td>
-                        </tr>
-                        <tr class="x-back-gpt">
-                            <td class="td-icon">
-                                <img src="../static/img/fe-16.png" alt="fehelper"/></td>
-                            <td class="td-content"><b class="x-time">{{respResult.respTime}}</b>
-                                <div :id="respResult.id" class="x-xcontent" v-html="respResult.respContent"></div>
-                            </td>
-                        </tr>
-                    </template>
-                </table>
+            <div class="row mod-inputs box-result" ref="boxResult">
+                <div class="row box-tips" v-if="!hideDemo">
+                    <div>你好,我是你的专属AI助理,不管你是要查BUG、写代码、还是要咨询什么技术问题,你都可以跟我说,我会竭尽全力帮你解决,比如你可以这样问我👇👇👇</div>
+                    <ul class="x-demos clearfix">
+                        <li v-for="demo in demos" @click="sendMessage(demo)">{{demo}}</li>
+                    </ul>
+                </div>
+                <div class="row box-message">
+                    <table>
+                        <template v-if="respResult.id">
+                            <tr class="x-from-fh">
+                                <td class="td-icon x-me"><img src="../static/img/me.png" alt="me"/></td>
+                                <td class="td-content"><b class="x-time">{{respResult.sendTime}} </b><div class="x-xcontent">{{respResult.message}}</div></td>
+                            </tr>
+                            <tr class="x-back-gpt">
+                                <td class="td-icon">
+                                    <img src="../static/img/fe-16.png" alt="fehelper"/></td>
+                                <td class="td-content"><b class="x-time">{{respResult.respTime}}</b>
+                                    <div :id="respResult.id" class="x-xcontent" v-html="respResult.respContent"></div>
+                                </td>
+                            </tr>
+                        </template>
+                    </table>
+                </div>
             </div>
         </div>
     </div>
@@ -76,3 +87,4 @@
 <script type="module" src="index.js"></script>
 </body>
 </html>
+

+ 240 - 13
apps/aiagent/index.js

@@ -10,9 +10,9 @@ new Vue({
     data: {
         prompt: '',
         demos: [
-            'FeHelper是什么?怎么安装?',
             '用Js写一个冒泡排序的Demo',
-            'Js里的Fetch API是怎么用的'
+            'Js里的Fetch API是怎么用的',
+            '帮我写一个单网页版的俄罗斯方块游戏'
         ],
         initMessage: {
             id:'id-test123',
@@ -31,11 +31,43 @@ new Vue({
         history:[],
         tempId:'',
         hideDemo: false,
-        undergoing: false
+        undergoing: false,
+        messages: [],
+        showHistoryPanel: false
+    },
+    computed: {
+        groupedHistory() {
+            // 按日期分组,主题为message前20字
+            const groups = {};
+            this.history.forEach(item => {
+                const date = item.sendTime ? item.sendTime.split(' ')[0] : '未知日期';
+                if (!groups[date]) groups[date] = [];
+                groups[date].push({
+                    ...item,
+                    theme: item.message ? item.message.slice(0, 20) : ''
+                });
+            });
+            return groups;
+        }
+    },
+    watch: {
+        history: {
+            handler(val) {
+                localStorage.setItem('fh-aiagent-history', JSON.stringify(val));
+            },
+            deep: true
+        }
     },
     mounted: function () {
         this.$refs.prompt.focus();
         this.hideDemo = !!(new URL(location.href)).searchParams.get('hideDemo');
+        // 加载本地历史
+        const local = localStorage.getItem('fh-aiagent-history');
+        if(local){
+            try {
+                this.history = JSON.parse(local);
+            } catch(e) {}
+        }
     },
     methods: {
         // 这个代码,主要用来判断大模型返回的内容是不是包含完整的代码块
@@ -71,7 +103,14 @@ new Vue({
         sendMessage(prompt){
             if(this.undergoing) return;
             if(this.respResult.id){
-                this.history.push(this.respResult);
+                // 先存储上一轮对话到历史
+                this.history.push({
+                    id: this.respResult.id,
+                    sendTime: this.respResult.sendTime,
+                    message: this.respResult.message,
+                    respTime: this.respResult.respTime,
+                    respContent: this.respResult.respContent
+                });
                 this.respResult.id = '';
             }
 
@@ -83,9 +122,39 @@ new Vue({
 
             this.tempId = '';
             let respContent = '';
-            AI.askYiLarge(prompt,(respJson,done) => {
+
+            // 1. 先把用户输入 push 到 messages
+            this.messages.push({ role: 'user', content: prompt });
+
+            AI.askCoderLLM(this.messages, (respJson, done) => {
                 if(done){
                     this.undergoing = false;
+                    // 2. 回复结束后,把助手回复 push 到 messages
+                    if(this.respResult.id && this.respResult.respContent){
+                        // 取纯文本(去掉HTML标签)
+                        const tempDiv = document.createElement('div');
+                        tempDiv.innerHTML = this.respResult.respContent;
+                        const plainText = tempDiv.textContent || tempDiv.innerText || '';
+                        this.messages.push({ role: 'assistant', content: plainText });
+                        // === 关键:push到history ===
+                        this.history.push({
+                            id: this.respResult.id,
+                            sendTime: this.respResult.sendTime,
+                            message: this.respResult.message,
+                            respTime: this.respResult.respTime,
+                            respContent: this.respResult.respContent
+                        });
+                        this.respResult.id = '';
+                    }
+                
+                    this.$nextTick(() => {
+                        let elm = document.getElementById(this.tempId);
+                        elm && elm.querySelectorAll('pre code').forEach((block) => {
+                            hljs.highlightBlock(block);
+                            insertCodeToolbar(block);
+                        });
+                        this.scrollToBottom();
+                    });
                     return;
                 }
                 let id = respJson.id;
@@ -102,14 +171,7 @@ new Vue({
                 }else{
                     this.respResult.respContent = respContent;
                 }
-                
-                this.$nextTick(() => {
-                    let elm = document.getElementById(id);
-                    elm.querySelectorAll('pre code').forEach((block) => {
-                        hljs.highlightBlock(block);
-                    });
-                    this.scrollToBottom();
-                });
+                this.$nextTick(() => this.scrollToBottom());
             });            
         },
 
@@ -119,7 +181,172 @@ new Vue({
         goChat(){
             this.sendMessage(this.prompt);
             this.$nextTick(() => this.prompt='');
+        },
+
+        openOptionsPage: function(event) {
+            event.preventDefault();
+            event.stopPropagation();
+            chrome.runtime.openOptionsPage();
+        },
+
+        openDonateModal: function(event ){
+            event.preventDefault();
+            event.stopPropagation();
+            chrome.runtime.sendMessage({
+                type: 'fh-dynamic-any-thing',
+                thing: 'open-donate-modal',
+                params: { toolName: 'aiagent' }
+            });
+        },
+
+        loadHistory(item) {
+            // 渲染到主面板
+            this.respResult = {
+                id: item.id,
+                sendTime: item.sendTime,
+                message: item.message,
+                respTime: item.respTime,
+                respContent: item.respContent
+            };
+            this.showHistoryPanel = false;
+            this.$nextTick(() => this.scrollToBottom());
+        },
+
+        startNewChat(event) {
+            event && event.preventDefault();
+            this.messages = [];
+            this.respResult = {
+                id: '',
+                sendTime: '',
+                message: '',
+                respTime: '',
+                respContent: ''
+            };
+            this.showHistoryPanel = false;
+            this.$nextTick(() => {
+                this.$forceUpdate();
+                this.scrollToBottom();
+            });
+        },
+
+        onHistoryClick(event) {
+            event.preventDefault();
+            event.stopPropagation();
+            this.showHistoryPanel = !this.showHistoryPanel;
         }
     }
 
 });
+
+// 工具函数:复制和运行
+function copyCode(code) {
+    if (navigator.clipboard) {
+        navigator.clipboard.writeText(code);
+    } else {
+        const textarea = document.createElement('textarea');
+        textarea.value = code;
+        document.body.appendChild(textarea);
+        textarea.select();
+        document.execCommand('copy');
+        document.body.removeChild(textarea);
+    }
+}
+
+// 新增:为代码块插入工具栏
+function insertCodeToolbar(block) {
+    // 检查是否已插入按钮,避免重复
+    if (block.parentNode.querySelector('.fh-code-toolbar')) return;
+
+    // 创建工具栏
+    const toolbar = document.createElement('div');
+    toolbar.className = 'fh-code-toolbar';
+    toolbar.style.cssText = 'position:absolute;bottom:6px;right:12px;z-index:10;display:flex;gap:8px;';
+
+    // 复制按钮
+    const btnCopy = document.createElement('button');
+    btnCopy.innerText = '复制';
+    btnCopy.className = 'fh-btn-copy';
+    btnCopy.style.cssText = 'padding:2px 8px;font-size:12px;cursor:pointer;';
+    btnCopy.onclick = (e) => {
+        e.stopPropagation();
+        copyCode(block.innerText);
+        // 复制成功反馈
+        const oldText = btnCopy.innerText;
+        btnCopy.innerText = '已复制';
+        btnCopy.disabled = true;
+        setTimeout(() => {
+            btnCopy.innerText = oldText;
+            btnCopy.disabled = false;
+        }, 1000);
+    };
+
+    // 运行按钮
+    const lang = (block.className || '').toLowerCase();
+    let btnRun = document.createElement('button');
+    btnRun.className = 'fh-btn-run';
+    btnRun.style.cssText = 'padding:2px 8px;font-size:12px;cursor:pointer;';
+    if (lang.includes('lang-javascript') || lang.includes('lang-js')) {
+        btnRun.innerText = 'Console运行';
+        btnRun.onclick = (e) => {
+            e.stopPropagation();
+            copyCode(block.innerText);
+            btnRun.innerText = '已复制到剪贴板';
+            btnRun.disabled = true;
+            setTimeout(() => {
+                btnRun.innerText = 'Console运行';
+                btnRun.disabled = false;
+            }, 1200);
+            showToast('代码已复制到剪贴板,请按F12打开开发者工具,切换到Console粘贴回车即可运行!');
+        };
+    } else if (lang.includes('lang-html')) {
+        btnRun.innerText = '下载并运行';
+        btnRun.onclick = (e) => {
+            e.stopPropagation();
+            const blob = new Blob([block.innerText], {type: 'text/html'});
+            const url = URL.createObjectURL(blob);
+            const a = document.createElement('a');
+            a.href = url;
+            a.download = 'fehelper-demo.html';
+            document.body.appendChild(a);
+            a.click();
+            document.body.removeChild(a);
+            URL.revokeObjectURL(url);
+            btnRun.innerText = '已下载';
+            btnRun.disabled = true;
+            setTimeout(() => {
+                btnRun.innerText = '下载并运行';
+                btnRun.disabled = false;
+            }, 1200);
+            showToast('HTML文件已下载,请双击打开即可运行!');
+        };
+    } else {
+        btnRun.remove();
+    }
+
+    toolbar.appendChild(btnCopy);
+    toolbar.appendChild(btnRun);
+
+    // 让pre相对定位,插入工具栏到底部
+    const pre = block.parentNode;
+    pre.style.position = 'relative';
+    pre.appendChild(toolbar);
+}
+
+// 页面内Toast提示
+function showToast(msg) {
+    let toast = document.createElement('div');
+    toast.className = 'fh-toast';
+    toast.innerText = msg;
+    toast.style.cssText = `
+        position:fixed;left:50%;top:80px;transform:translateX(-50%);
+        background:rgba(0,0,0,0.85);color:#fff;padding:10px 24px;
+        border-radius:6px;font-size:16px;z-index:99999;box-shadow:0 2px 8px rgba(0,0,0,0.2);
+        transition:opacity 0.3s;opacity:1;
+    `;
+    document.body.appendChild(toast);
+    setTimeout(() => {
+        toast.style.opacity = '0';
+        setTimeout(() => document.body.removeChild(toast), 300);
+    }, 1800);
+}
+

+ 2 - 1
apps/background/tools.js

@@ -71,7 +71,7 @@ let toolMap = {
         }]
     },
     'aiagent': {
-        name: 'AI,请帮帮忙',
+        name: 'AI(智能助手)',
         tips: '由AI强力支撑的超智能对话工具,可以让它帮你写代码、改代码、做方案设计、查资料、做分析等',
         menuConfig: [{
             icon: '֍',
@@ -267,3 +267,4 @@ let toolMap = {
 };
 
 export default toolMap;
+

+ 3 - 3
apps/manifest.json

@@ -1,7 +1,7 @@
 {
   "name": "FeHelper(前端助手)-Dev",
   "short_name": "FeHelper",
-  "version": "2025.04.1510",
+  "version": "2025.05.2013",
   "manifest_version": 3,
   "description": "JSON自动格式化、手动格式化,支持排序、解码、下载等,更多功能可在配置页按需安装!",
   "icons": {
@@ -29,8 +29,7 @@
     "activeTab",
     "storage",
     "notifications",
-    "unlimitedStorage",
-    "sidePanel"
+    "unlimitedStorage"
   ],
   "host_permissions": [
     "http://*/*",
@@ -96,3 +95,4 @@
   "update_url": "https://clients2.google.com/service/update2/crx",
   "homepage_url": "https://www.baidufe.com/fehelper"
 }
+

+ 1 - 19
apps/popup/index.js

@@ -117,25 +117,6 @@ new Vue({
     methods: {
 
         runHelper: async function (toolName) {
-            // 如果是aiagent工具,我们就用sidePanel打开
-            
-            if(toolName === 'aiagent'){ 
-                const [tab] = await chrome.tabs.query({
-                    active: true,
-                    lastFocusedWindow: true
-                  });
-                  
-                const tabId = tab.id;
-                await chrome.sidePanel.setOptions({
-                  tabId,
-                  path: '/aiagent/index.html',
-                  enabled: true
-                });
-                await chrome.sidePanel.open({ tabId });
-                return window.close();
-            }
-
-            // 其他的情形,就不在sidePanel中打开了
             let request = {
                 type: MSG_TYPE.OPEN_DYNAMIC_TOOL,
                 page: toolName,
@@ -161,3 +142,4 @@ new Vue({
         }
     }
 });
+