用Trea来快速生成一个浏览器插件

用Trea来开发一个浏览器插件

背景

浏览网页的时候,总是外网的加载不出来,弄了梯子,有时候直连的又加载不出来,最近ZeroOmega也老是不好用,总有些资源加载不出来

Trea solo

直接打开trea,新建任务,输入提示词:

提示词 复制代码
帮我开发一个浏览器插件,主要功能是管理浏览器,默认浏览器直联,如果链接异常的网址通过vpn链接

打开plan开关,点击发送 Trea就吭哧吭哧一顿计划,会生成一个计划文档

非常的专业

点击执行计划

很快的就会生成项目,给项目添加上对应的icon图片,就可以在浏览器里打开了

并且可以截图让他对对应的有问题的地方进行修改

截图改变按钮大小

改完后的效果

再微调微调,搞定

效果展示

简单实用,比较满意

不足之处

很多网站的资源都不在同一个域名下,需要配置多个域名,如:tiktok.com的很多资源在tiktokv.com这个域名下,这就需要打开网站的调试工具,检查哪些资源无法加载的,把相应的域名添加到代理列表才可以。想让Trea solo根据标签页来判断是否需要代理,需要代理的整个标签页的所有请求都走代理,但是ai也没弄成。有大佬的可以指导指导。

background.js 复制代码
// 浏览器代理管理器 - 背景脚本

// 初始化存储
async function initStorage() {
  const storage = await chrome.storage.local.get(['exceptionUrls', 'proxyConfig', 'isEnabled']);
  
  // 设置默认值
  const defaults = {
    exceptionUrls: [],
    proxyConfig: {
      host: '127.0.0.1',
      port: 1080,
      type: 'http'
    },
    isEnabled: true
  };
  
  // 合并默认值和现有值 - 只在值不存在时使用默认值
  const newStorage = {
    exceptionUrls: storage.exceptionUrls !== undefined ? storage.exceptionUrls : defaults.exceptionUrls,
    proxyConfig: storage.proxyConfig || defaults.proxyConfig,
    isEnabled: storage.isEnabled !== undefined ? storage.isEnabled : defaults.isEnabled
  };
  
  // 保存到存储
  await chrome.storage.local.set(newStorage);
  
  console.log('Storage initialized:', newStorage);
  
  // 更新规则
  await updateRules(newStorage.exceptionUrls);
}

// 更新网络请求规则
async function updateRules(exceptionUrls) {
  // 不再使用declarativeNetRequest,直接更新PAC脚本
  await initProxySettings();
}

// 配置代理函数已移除,改为使用PAC脚本处理代理逻辑

// 监听存储变化
chrome.storage.onChanged.addListener(async (changes, area) => {
  if (area === 'local') {
    if (changes.exceptionUrls) {
      await updateRules(changes.exceptionUrls.newValue);
    }
  }
});

// 初始化代理设置
async function initProxySettings() {
  const storage = await chrome.storage.local.get(['isEnabled', 'exceptionUrls', 'proxyConfig']);
  
  console.log('initProxySettings called with:', storage);
  
  if (storage.isEnabled) {
    // 配置代理
    const exceptionUrls = storage.exceptionUrls || [];
    const proxyConfig = storage.proxyConfig || { host: '127.0.0.1', port: 1080, type: 'http' };
    
    console.log('Enabling proxy with config:', proxyConfig);
    console.log('Exception URLs:', exceptionUrls);
    
    // 生成PAC脚本
    const pacScript = `function FindProxyForURL(url, host) {
      // Browser Proxy Manager - PAC Script
      const exceptionUrls = ${JSON.stringify(exceptionUrls)};
      const proxyConfig = ${JSON.stringify(proxyConfig)};
      
      // Check if URL is in proxy list
      const isException = exceptionUrls.some(proxyUrl => {
        return host === proxyUrl || host.endsWith("." + proxyUrl);
      });
      
      if (isException) {
        // Use proxy
        const proxyStr = 'PROXY ' + proxyConfig.host + ':' + proxyConfig.port + '; DIRECT';
        return proxyStr;
      } else {
        // Direct connection
        return 'DIRECT';
      }
    }`;
    
    try {
      await chrome.proxy.settings.set({
        value: {
          mode: 'pac_script',
          pacScript: {
            data: pacScript
          }
        },
        scope: 'regular'
      }, () => {
        if (chrome.runtime.lastError) {
          console.error('Error setting proxy:', chrome.runtime.lastError);
        } else {
          console.log('Proxy settings updated successfully');
        }
      });
    } catch (error) {
      console.error('Error setting proxy settings:', error);
    }
  } else {
    // 禁用代理
    console.log('Disabling proxy');
    try {
      await chrome.proxy.settings.set({
        value: {
          mode: 'direct'
        },
        scope: 'regular'
      });
    } catch (error) {
      console.error('Error disabling proxy:', error);
    }
  }
}

// 监听扩展安装或更新
chrome.runtime.onInstalled.addListener(async () => {
  await initStorage();
  await initProxySettings();
  // 初始化所有标签页的图标和徽章颜色
  const storage = await chrome.storage.local.get(['isEnabled']);
  await updateAllTabsIconAndColor(storage.isEnabled);
});

// 监听扩展启动
chrome.runtime.onStartup.addListener(async () => {
  await initProxySettings();
  // 初始化所有标签页的图标和徽章颜色
  const storage = await chrome.storage.local.get(['isEnabled']);
  await updateAllTabsIconAndColor(storage.isEnabled);
});

// 直连模式使用默认图标
const greenIcon = {
  "16": "icons/icon16.png",
  "48": "icons/icon48.png",
  "128": "icons/icon128.png"
};

// 代理模式使用默认图标
const redIcon = {
  "16": "icons/icon16.png",
  "48": "icons/icon48.png",
  "128": "icons/icon128.png"
};

// 根据连接状态更新标签页图标
async function updateTabIcon(tabId, url) {
  // 获取设置
  const storage = await chrome.storage.local.get(['exceptionUrls', 'isEnabled']);
  
  // 如果扩展未启用,显示默认图标和禁用状态
  if (!storage.isEnabled) {
    chrome.action.setBadgeText({ text: 'DISABLED', tabId: tabId });
    chrome.action.setBadgeBackgroundColor({ color: '#6c757d', tabId: tabId });
    return;
  }
  
  try {
    const urlObj = new URL(url);
    const hostname = urlObj.hostname;
    
    const isException = storage.exceptionUrls.some(proxyUrl => {
      return hostname === proxyUrl || hostname.endsWith("." + proxyUrl);
    });
    
    // 更新徽章文本和颜色
    if (isException) {
      // 代理模式 - 显示VPN徽章和红色背景
      chrome.action.setBadgeText({ text: 'VPN', tabId: tabId });
      chrome.action.setBadgeBackgroundColor({ color: '#dc3545', tabId: tabId });
    } else {
      // 直连模式 - 显示DIRECT徽章和绿色背景
      chrome.action.setBadgeText({ text: 'DIR', tabId: tabId });
      chrome.action.setBadgeBackgroundColor({ color: '#3fc944', tabId: tabId });
    }
  } catch (error) {
    // 对于无法解析的URL,显示直连状态
    chrome.action.setBadgeText({ text: 'DIR', tabId: tabId });
    chrome.action.setBadgeBackgroundColor({ color: '#3fc944', tabId: tabId });
  }
}

// 监听标签页更新
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
  if (changeInfo.status === 'complete' && tab.url) {
    // 更新标签页图标
    await updateTabIcon(tabId, tab.url);
  }
});

// 监听标签页切换
chrome.tabs.onActivated.addListener(async (activeInfo) => {
  const tab = await new Promise(resolve => chrome.tabs.get(activeInfo.tabId, resolve));
  if (tab.url) {
    // 更新激活标签页的图标
    await updateTabIcon(activeInfo.tabId, tab.url);
  }
});

// 更新所有标签页的图标和徽章颜色
async function updateAllTabsIconAndColor(isEnabled) {
  const tabs = await new Promise(resolve => chrome.tabs.query({}, resolve));
  
  for (const tab of tabs) {
    if (tab.url) {
      await updateTabIcon(tab.id, tab.url);
    }
  }
}

// 初始化
initStorage();

// 更新所有标签页的徽章颜色
async function updateAllTabsBadgeColor(isEnabled) {
  const tabs = await new Promise(resolve => chrome.tabs.query({}, resolve));
  
  if (!isEnabled) {
    // 插件被禁用时,将所有标签页的徽章背景色设为灰色
    tabs.forEach(tab => {
      chrome.action.setBadgeBackgroundColor({ color: '#808080', tabId: tab.id });
    });
  } else {
    // 插件启用时,根据每个标签页的连接状态设置颜色
    const storage = await chrome.storage.local.get(['exceptionUrls']);
    
    tabs.forEach(tab => {
      if (tab.url) {
        try {
          const url = new URL(tab.url);
          const hostname = url.hostname;
          
          const isException = storage.exceptionUrls.some(proxyUrl => {
            return hostname === proxyUrl || hostname.endsWith("." + proxyUrl);
          });
          
          if (isException) {
            // 代理模式下设置为红色
            chrome.action.setBadgeBackgroundColor({ color: '#FF0000', tabId: tab.id });
          } else {
            // 直连模式下设置为绿色
            chrome.action.setBadgeBackgroundColor({ color: '#00FF00', tabId: tab.id });
          }
        } catch (e) {
          // 对于无法解析的URL,设置为默认颜色
          chrome.action.setBadgeBackgroundColor({ color: '#00FF00', tabId: tab.id });
        }
      }
    });
  }
}

// 监听消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  switch (message.action) {
    case 'getSettings':
      // 获取设置
      chrome.storage.local.get(['exceptionUrls', 'proxyConfig', 'isEnabled'], (result) => {
        sendResponse(result);
      });
      return true;
    
    case 'saveSettings':
      // 保存设置
      chrome.storage.local.set(message.data, async () => {
        const proxyUrls = message.data.exceptionUrls || [];
        console.log('Settings saved:', message.data);
        console.log('Proxy URLs:', proxyUrls);
        await updateRules(proxyUrls);
        await initProxySettings();
        // 更新所有标签页的图标
        await updateAllTabsIconAndColor(message.data.isEnabled !== false);
        sendResponse({ success: true });
      });
      return true;
    
    case 'toggleEnabled':
      // 切换启用状态
      chrome.storage.local.get('isEnabled', async (result) => {
        const newEnabled = !result.isEnabled;
        await chrome.storage.local.set({ isEnabled: newEnabled });
        await initProxySettings();
        // 更新所有标签页的图标和徽章颜色
        await updateAllTabsIconAndColor(newEnabled);
        sendResponse({ isEnabled: newEnabled });
      });
      return true;
    
    default:
      sendResponse({ error: 'Unknown action' });
      return true;
  }
});

结束语

现在有了trea真的太方便了,做一个浏览器小插件2个小时不到就做出来了,真的是大大节省了我们开发工作量,必须点赞。

相关推荐
dorisrv2 小时前
React 状态管理:Zustand 快速上手指南
前端·react.js
lkbhua莱克瓦242 小时前
IO流——打印流
java·开发语言·前端·学习方法
im_AMBER2 小时前
Canvas架构手记 08 CSS Transform | CSS 显示模型 | React.memo
前端·css·笔记·学习·架构
申阳2 小时前
Day 23:登录设计的本质:从XSS/CSRF到Session回归的技术演进
前端·后端·程序员
岛风风3 小时前
前端HTML导出PDF分页难题:10天踩坑后的终极方案,精细到每个像素点!!!
前端·javascript
LYFlied3 小时前
单页应用与多页应用:架构选择与前端演进
前端·架构·spa·mpa·ssr
前端老宋Running3 小时前
你的组件 API 为什么像个垃圾场?—— React 复合组件模式 (Compound Components) 实战教学
前端·react.js·架构
alanAltman3 小时前
前端架构范式:意图系统构建web
前端·javascript
梦鱼3 小时前
我踩了 72 小时的 Electron webview PDF 灰色坑,只为告诉你:别写这行代码!
前端·javascript·electron