尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Cursor纯对话 1小时开发浏览器翻译插件!完全零代码,你也可以!

Cursor纯对话 1小时开发浏览器翻译插件!完全零代码,你也可以! 1. 为什么我劝你先别急着写代码而是先跟 Cursor 聊清楚需求浏览器翻译插件这个东西听起来像是个小工具但它其实是个特别适合拿来练手 Cursor 纯对话开发的项目。原因很简单它涉及 UI 注入、划词监听、网络请求、结果渲染这几个典型环节麻雀虽小五脏俱全。你如果能用自然语言把它跑通后面做更复杂的插件心里就有底了。我先把结论放前面零代码不是说你什么都不用管而是说你不用手写代码但你必须把需求说清楚、把配置贴对、把报错原样丢回去。这三件事做不好Cursor 再强也救不了你。先说说这个插件到底能做什么。你打开任意一个英文网页选中一段文字旁边弹出一个小浮层上面显示中文翻译下面还能顺手让模型帮你总结一下这段内容。适合谁适合每天要读英文文档、英文论文、英文技术博客但英语又不是母语的人。也适合那些想验证 Cursor 工程能力、想看看纯对话到底能走多远的人。我试过用纯对话的方式从零搭这个插件整个过程大概一小时中间踩了几个坑后面会一个个讲。你现在要做的第一件事不是打开编辑器而是打开 Cursor新建一个空文件夹然后在里面建一个需求.md文件。这个文件就是你跟 Cursor 之间的合同写得越具体后面返工越少。需求文件里至少要写清楚这几件事插件叫什么名字、在哪些页面上生效、用户怎么触发翻译、翻译结果展示在哪里、调用哪个模型接口、有没有总结功能、界面大概长什么样。你不用写得像产品经理那么正式用大白话就行。比如你可以写“用户在网页上选中一段英文松开鼠标后在选中文字旁边出现一个小卡片卡片上半部分是中文翻译下半部分是一个‘总结’按钮点了之后显示这段文字的中文摘要”。这里有个关键点你要明确告诉 Cursor 这个插件是 Chrome Manifest V3 的扩展。因为 V2 和 V3 的写法差别很大尤其是 background 脚本和权限声明部分。如果你不说Cursor 有可能给你生成 V2 的代码加载的时候直接报错。我一开始就吃了这个亏后面会详细讲。另外需求文件里最好把“不做什么”也写清楚。比如“不要弹出新标签页”“不要修改原网页的 DOM 结构”“不要自动翻译整页”。这些约束能帮 Cursor 收敛方案避免它给你搞出一个过度复杂的实现。写完需求文件之后先别急着让它写代码。你可以先让它根据需求生成一个界面原型就是一个静态的 HTML 文件能看到浮层长什么样。这一步很重要因为界面是你能直观看到的东西改起来也快。如果界面不对后面逻辑写得再好也白搭。原型确认之后再让它基于需求和原型生成完整的插件代码。这时候你要做一件事把模型 API 的文档地址也喂给 Cursor。因为翻译和总结都要调模型接口接口的请求格式、鉴权方式、返回结构Cursor 需要知道。你不给它文档它就可能凭记忆瞎写跑起来就是 401 或者解析错误。说到模型接口这里就引出下一个问题你用什么来提供这个接口。你可以用任何兼容 OpenAI 格式的服务只要把 Base URL、API Key、Model ID 三样东西配对就行。下面我会以 TaoToken 为例把配置过程完整走一遍因为它同时支持对话模型和编码场景后面你想把插件升级成更复杂的 Agent 也方便。2. 把 TaoToken 接进 CursorBase URL、API Key、Model ID 一个都不能少很多人卡在这一步不是因为难而是因为信息不全。你只填了 Key没填 Base URL请求就发不出去你填了 Base URL但 Model ID 写错了返回就是模型不存在。所以这一节我把三件套拆开讲你照着填就行。先说 TaoToken 是什么。它是一个模型调用平台提供兼容 OpenAI 格式的 API 接口你可以用它来调用对话模型、编码模型也可以用它来管理 API Key 和用量。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。注意 API 地址后面不加任何多余路径具体端点由你的代码或工具去拼。你要做的第一件事是拿到 API Key。登录之后进入控制台找到 API Keys 页面新建一个 Key复制下来。这个 Key 只显示一次复制完再关页面。如果你用的是 Cursor可以在 Cursor 的设置里找到 Models 或者 OpenAI API Key 的配置项把 Key 填进去。但光填 Key 不够你还要改 Base URL。Cursor 默认走的是它自己的服务你要让它走 TaoToken就得在设置里覆盖 Base URL。具体位置在 Cursor 设置的 Models 选项卡里找到 OpenAI API Key 那一栏下面有一个 Override OpenAI Base URL 的输入框填https://taotoken.net/api。注意不要加/v1也不要加/chat/completions这些由 Cursor 自己拼。填完之后点 Verify如果 Key 和 Base URL 都对它会显示验证通过。接下来是 Model ID。你在 Cursor 里添加自定义模型的时候需要填一个模型名称。这个名称必须和 TaoToken 支持的模型 ID 一致。比如你想用 Claude 系列的编码模型就填对应的 ID想用 GPT 系列的对话模型就填对应的 ID。填错的话请求会返回 model not found。如果你不确定有哪些可用模型可以去模型对话页面看看当前支持的列表地址是 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。这里有个细节Cursor 里配置的模型和你在插件代码里调用的模型是两回事。Cursor 里的模型是用来帮你写代码的插件代码里的模型是用来做翻译和总结的。你可以在插件代码里也用 TaoToken 的接口这样你只需要维护一套 Key 和 Base URL。具体做法是在插件里发请求到https://taotoken.net/api/chat/completions请求头带上Authorization: Bearer 你的Key请求体里指定 model 和 messages。如果你后面想把这个插件升级成更复杂的 Agent比如让它能自动抓取页面内容、分段翻译、生成摘要那你可以考虑用 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。它更适合长期编码和 Agent 场景额度和管理方式也更省心。现在你把三件套凑齐了Base URL 是https://taotoken.net/apiAPI Key 是你复制的那串Model ID 是你选定的模型名称。接下来就是把这些东西写进插件的配置文件里。注意API Key 不要硬编码在 content script 里因为 content script 是注入到网页里的用户能看到。正确做法是把请求逻辑放在 background service worker 里content script 通过消息传递把选中的文字发给 backgroundbackground 再去调接口。这样 Key 只存在于扩展的 background 环境里相对安全。下面是一个 background service worker 的配置片段你可以直接复制到background.js里然后把YOUR_API_KEY和YOUR_MODEL_ID替换成你自己的const API_BASE https://taotoken.net/api; const API_KEY YOUR_API_KEY; const MODEL_ID YOUR_MODEL_ID; async function callModel(text, task) { const systemPrompt task translate ? 你是一个翻译引擎把用户输入的英文翻译成简体中文只输出译文不要解释。 : 你是一个总结助手用简体中文总结用户输入的内容控制在三句话以内。; const response await fetch(${API_BASE}/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${API_KEY} }, body: JSON.stringify({ model: MODEL_ID, messages: [ { role: system, content: systemPrompt }, { role: user, content: text } ], temperature: 0.3 }) }); if (!response.ok) { const errText await response.text(); throw new Error(API error ${response.status}: ${errText}); } const data await response.json(); return data.choices[0].message.content; } chrome.runtime.onMessage.addListener((request, sender, sendResponse) { if (request.action translate || request.action summarize) { callModel(request.text, request.action) .then(result sendResponse({ ok: true, result })) .catch(err sendResponse({ ok: false, error: err.message })); return true; } });这段代码里API_BASE就是 TaoToken 的 API 地址API_KEY和MODEL_ID就是三件套里的另外两件。注意return true这一行它是用来保持消息通道打开的因为callModel是异步的。如果你忘了写content script 会收到 undefined。manifest 文件里需要声明 background service worker 和 host 权限。下面是一个最小化的manifest.json{ manifest_version: 3, name: 划词翻译总结, version: 1.0, description: 选中网页文字一键翻译并总结, permissions: [activeTab, scripting], host_permissions: [https://taotoken.net/*], background: { service_worker: background.js }, content_scripts: [ { matches: [all_urls], js: [content.js], css: [content.css] } ], action: { default_title: 划词翻译 } }注意host_permissions里要加上https://taotoken.net/*否则 background 发请求会被浏览器拦截。content_scripts的matches写成all_urls表示在所有页面上生效你也可以限制成特定域名。content script 负责监听鼠标抬起事件拿到选中的文字然后给 background 发消息。下面是一个简化版let popup null; document.addEventListener(mouseup, (e) { const selection window.getSelection(); const text selection.toString().trim(); if (!text || text.length 2) return; const range selection.getRangeAt(0); const rect range.getBoundingClientRect(); showPopup(rect, text); }); function showPopup(rect, text) { if (popup) popup.remove(); popup document.createElement(div); popup.className translate-popup; popup.style.left ${rect.left window.scrollX}px; popup.style.top ${rect.bottom window.scrollY 8}px; popup.innerHTML div classtranslate-result翻译中.../div button classsummarize-btn总结/button ; document.body.appendChild(popup); chrome.runtime.sendMessage({ action: translate, text }, (res) { const resultEl popup.querySelector(.translate-result); if (res res.ok) { resultEl.textContent res.result; } else { resultEl.textContent 翻译失败 (res ? res.error : 无响应); } }); popup.querySelector(.summarize-btn).addEventListener(click, () { const resultEl popup.querySelector(.translate-result); resultEl.textContent 总结中...; chrome.runtime.sendMessage({ action: summarize, text }, (res) { if (res res.ok) { resultEl.textContent res.result; } else { resultEl.textContent 总结失败 (res ? res.error : 无响应); } }); }); }CSS 你可以让 Cursor 帮你生成也可以自己写一个简单的浮层样式。关键是position: absolute和z-index要够高不然会被网页内容盖住。到这里配置部分就齐了。你有了 manifest、background、content script三件套也填进了 background。接下来就是加载到 Chrome 里验证。3. 加载插件到 Chrome 并完成第一次划词翻译验证打开 Chrome地址栏输入chrome://extensions/右上角打开“开发者模式”然后点“加载已解压的扩展程序”选择你那个包含 manifest.json 的文件夹。如果一切正常你会看到插件卡片出现没有红色报错。这时候你打开任意一个英文网页比如一篇英文技术博客选中一段文字松开鼠标。如果浮层出现并且显示“翻译中...”然后变成中文说明整条链路通了。如果浮层没出现先看 Chrome 控制台有没有报错再看扩展页面有没有报错。我实测下来第一次加载最容易出的问题是 manifest 版本写错。如果你让 Cursor 生成代码时没说清楚它可能给你生成 Manifest V2 的写法比如manifest_version: 2然后 background 用scripts: [background.js]而不是service_worker。V2 在最新版 Chrome 里已经不支持了加载时会直接报错。解决办法很简单把manifest_version改成 3把 background 改成 service_worker 形式。第二个常见问题是 host_permissions 没加。你 background 里 fetch 的地址是https://taotoken.net/api/chat/completions但 manifest 里只写了permissions: [activeTab]没有host_permissions。这时候请求会被 CORS 或者扩展权限拦截控制台会报Failed to fetch或者net::ERR_BLOCKED_BY_CLIENT。加上host_permissions: [https://taotoken.net/*]就好了。第三个问题是 content script 没注入。你打开网页选中文字没反应F12 控制台也没有任何日志。这时候去chrome://extensions/看插件卡片点“详细信息”看“错误”那一栏有没有提示。常见原因是matches写错了比如写成了https://example.com/*但你测试的页面不是这个域名。改成all_urls或者你实际测试的域名。第四个问题是消息传递失败。你在 content script 里chrome.runtime.sendMessage但 background 里没有return true或者 background 报错了但你没捕获。这时候 content script 收到的res是 undefined浮层会显示“翻译失败无响应”。解决办法是在 background 的callModel里把错误 catch 住然后sendResponse({ ok: false, error: err.message })这样你就能在浮层上看到具体错误。第五个问题是 API 返回 401。这个最直接就是 Key 不对或者没带。检查 background 里的Authorization头是不是Bearer加你的 Key注意 Bearer 后面有一个空格。另外检查 Key 有没有复制完整有没有多余的空格或换行。第六个问题是 API 返回 404。这个通常是 Base URL 拼错了。你在 background 里写的${API_BASE}/chat/completions如果API_BASE是https://taotoken.net/api拼出来就是https://taotoken.net/api/chat/completions这是对的。如果你写成了https://taotoken.net/api/v1拼出来就是https://taotoken.net/api/v1/chat/completions可能就 404 了。所以 Base URL 不要带/v1。第七个问题是返回结果解析失败报Cannot read properties of undefined (reading choices)。这说明data.choices是 undefined通常是接口返回了错误信息而不是正常结果。你可以在 background 里先把data打印出来看看或者检查response.ok是否为 false。如果response.ok是 false你应该先读response.text()而不是response.json()因为错误响应可能不是 JSON 格式。第八个问题是浮层位置不对。你选中文字后浮层出现在页面左上角或者别的地方。这是因为rect的坐标是相对于视口的你加了window.scrollX和window.scrollY之后才是相对于文档的。如果你没加页面滚动后浮层就会错位。另外如果网页本身有 CSS transformgetBoundingClientRect的坐标可能不准这种情况比较少见可以先不管。第九个问题是浮层被网页样式覆盖。你明明注入了浮层但看不到。打开 F12用元素选择器找到浮层看它的 computed style可能是z-index太低或者被某个父元素的overflow: hidden裁掉了。解决办法是把浮层的z-index设成 2147483647并且确保它直接挂在document.body下。第十个问题是修改代码后没生效。你改了 background.js但插件行为没变。这是因为 service worker 有缓存你需要在chrome://extensions/页面点一下插件卡片上的刷新按钮或者点“重新加载”。content script 也一样改完之后要重新加载插件然后刷新网页。把这些问题一个个排掉之后你的插件就能稳定工作了。整个过程就是“加载、运行、发现问题、看报错、丢给 Cursor、改完重新加载”的循环。Cursor 能识别你粘贴的报错文本也能识别截图所以遇到问题直接把控制台截图丢给它让它告诉你改哪里。4. 用 Cursor 纯对话迭代从能跑到好用只差几句提示词插件能跑通之后你会发现它离“好用”还有距离。比如浮层关不掉、翻译结果太长撑破页面、总结按钮点了没反应、选中文字后浮层挡住了原文。这些问题都不需要你手写代码你只需要把现象描述给 Cursor让它改。我总结了几条提示词模板你可以直接拿去用。第一条是修 bug 的“我在 Chrome 里加载了这个插件选中文字后浮层出现了但点击页面其他地方浮层不消失。请修改 content.js让浮层在点击外部区域时自动移除。”第二条是调样式的“浮层里的翻译结果太长时会超出屏幕右边。请修改 content.css让浮层最大宽度 360px超出部分自动换行并且距离视口边缘至少 16px。”第三条是加功能的“我想在浮层里加一个复制按钮点击后把翻译结果复制到剪贴板并显示‘已复制’提示两秒。”这些提示词的特点是说清楚现象、说清楚期望、说清楚涉及哪个文件。你不需要懂 JavaScript你只需要把看到的问题描述清楚。Cursor 会根据你的描述去改对应的代码。还有一个技巧当你不知道问题出在哪的时候可以让 Cursor 帮你加日志。比如你说“我在 background.js 里加了 console.log但在扩展的 service worker 控制台里看不到输出请帮我检查日志加对地方没有。”Cursor 会告诉你 service worker 的日志要在chrome://extensions/页面点击“Service Worker”链接才能看到而不是在网页的 F12 控制台里。另外如果你发现翻译质量不稳定比如有时候翻译成繁体有时候夹杂英文你可以在 system prompt 里加约束。比如把“你是一个翻译引擎”改成“你是一个专业的英译中翻译引擎只输出简体中文不要输出繁体中文不要保留英文原文不要添加任何解释”。这个改动只需要在 background.js 里改一行字符串不需要动逻辑。如果你想让插件支持更多语言比如日文翻译成中文你可以在 content script 里加一个语言检测或者直接让模型自动识别。提示词可以写“请修改 background.js让 system prompt 支持自动识别源语言并统一翻译成简体中文。”Cursor 会帮你把 prompt 改好。还有一个实用功能是“翻译并替换原文”。有些人不喜欢浮层他们希望选中文字后直接替换成中文。这个改动稍微大一点但也可以用对话完成。你可以说“请修改 content.js选中文字后不显示浮层而是直接把选中的文字替换成翻译结果并在旁边加一个小按钮可以还原原文。”Cursor 会给你生成对应的 DOM 操作代码。整个迭代过程不需要你理解代码细节但你需要理解“现象”和“期望”之间的差距。你描述得越具体Cursor 改得越准。如果你只说“不好用”它不知道从哪里下手。如果你说“浮层在页面滚动后位置错乱”它就知道要去改坐标计算。最后提醒一点每次让 Cursor 改完代码你都要重新加载插件并刷新网页否则改动静不会生效。这个循环看起来麻烦但熟练之后一次改动也就十几秒。一小时里大部分时间其实花在描述问题和验证结果上真正写代码的时间几乎为零。如果你后面想把这个插件做成更完整的项目比如加上历史记录、生词本、多模型切换那你可以考虑用 Coding Plan 来管理更长的上下文和更多的调用额度。地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。它适合这种持续迭代的场景不用每次担心额度不够。5. 常见报错对照表401、local proxy failed、reading choices 怎么破这一节我把调试过程中真实遇到的报错和解决办法列出来你遇到类似问题可以直接对照。第一个报错是401 Unauthorized。控制台或者浮层上显示这个说明你的 API Key 不对。检查三件事Key 有没有复制完整、Authorization头有没有写对、Key 有没有过期。如果你是在 Cursor 里配置模型时遇到 401那可能是 Base URL 没覆盖Cursor 还在走它自己的服务。去设置里确认 Override OpenAI Base URL 填的是https://taotoken.net/api。第二个报错是local proxy failed或者Failed to fetch。这个通常出现在 background 发请求的时候。原因可能是 host_permissions 没加或者网络环境有问题。先检查 manifest.json 里有没有host_permissions: [https://taotoken.net/*]。如果有再去chrome://extensions/看 service worker 的控制台那里会显示更详细的错误。如果是net::ERR_BLOCKED_BY_CLIENT说明请求被扩展权限拦截了。第三个报错是Cannot read properties of undefined (reading choices)。这个说明data.choices是 undefined接口返回的不是正常结果。你可以在 background 里把response.status和response.text()打印出来看看实际返回了什么。常见原因是 Model ID 写错了接口返回了{error: {message: model not found}}。这时候你去模型对话页面确认一下可用的模型 ID地址是 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。第四个报错是OAuth相关的。如果你在 Cursor 里登录或者验证模型时看到 OAuth 错误那通常是 Cursor 自己的账号体系问题和 TaoToken 的 API Key 无关。你可以先在 Cursor 里退出登录再重新登录或者检查网络是否能正常访问 Cursor 的服务。如果你只是用 TaoToken 的 API不走 Cursor 的 OAuth那这个错误不会影响你。第五个报错是Manifest version 2 is deprecated。这个说明 Cursor 给你生成的 manifest.json 是 V2 的。你只需要把manifest_version: 2改成3然后把background: {scripts: [background.js]}改成background: {service_worker: background.js}。如果还有browser_action改成action。第六个报错是Could not load background script。这个通常是文件名不对或者路径不对。检查 manifest.json 里的service_worker值是不是和你实际的文件名一致。比如你写的是background.js但文件实际叫background.ts那就加载不了。第七个报错是Content script not injected。你打开网页选中文字没反应F12 控制台也没有任何日志。去chrome://extensions/看插件卡片的“错误”按钮如果有错误会显示在那里。常见原因是matches写错了或者 content script 里有语法错误导致整个脚本没执行。第八个报错是Unchecked runtime.lastError: The message port closed before a response was received。这个说明 content script 发消息后background 没有及时调sendResponse。检查 background 的监听函数里有没有return true以及callModel是不是异步的。如果是异步的必须return true才能保持通道打开。第九个报错是Refused to load the script。这个通常是 CSP 问题某些网站的内容安全策略会阻止扩展注入脚本。这种情况比较少见如果遇到可以尝试把 content script 的注入方式改成run_at: document_idle或者用chrome.scripting.executeScript动态注入。第十个报错是API rate limit exceeded。这个说明你的调用频率太高了。你可以在 background 里加一个简单的节流比如同一个页面 1 秒内只允许一次请求。或者去控制台看看用量确认是不是额度用完了。把这些报错对照表存下来下次遇到直接搜关键词。大部分问题都不是代码逻辑问题而是配置问题或者权限问题。你只要把报错原文复制给 Cursor它基本都能告诉你改哪个文件哪一行。6. 从翻译插件到更多可能把 TaoToken 用顺手的几个建议插件跑通之后你可以把同样的方法用到其他小工具上。比如做一个“选中代码解释”的插件选中一段代码浮层显示这段代码在做什么。或者做一个“选中英文单词查词”的插件浮层显示音标、释义、例句。这些都可以用同一套 background content script 的结构只需要改 system prompt 和浮层样式。如果你想让插件更稳定建议把 API Key 放在 background 里不要放在 content script 里。因为 content script 是注入到网页里的任何网页都能通过window访问到你的变量。虽然 Chrome 的隔离环境提供了一定保护但把 Key 放在 background 是更安全的做法。另外建议你在 background 里加一个简单的错误处理和重试。比如请求失败时等 1 秒再试一次最多试两次。这样偶尔的网络抖动不会直接让用户看到“翻译失败”。代码可以让 Cursor 帮你写你只需要说“请在 background.js 的 callModel 里加一个重试机制失败后等 1 秒重试最多两次”。如果你后面想把这个插件分享给别人用你需要把 API Key 做成用户自己填的。可以在插件的 options 页面加一个输入框让用户填自己的 Key 和 Base URL然后存在chrome.storage.local里。background 发请求时从 storage 里读。这个改动也可以用对话完成提示词是“请给插件加一个 options 页面让用户填写 API Key 和 Base URL保存在 chrome.storage.localbackground 发请求时从 storage 读取”。最后如果你发现自己经常需要调用模型来做各种小工具可以多看看 TaoToken 的文档和模型列表了解当前支持的模型和接口格式。文档地址是 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite API Keys 管理页面是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。把这两个页面收藏起来后面配任何工具都用得上。整个流程走下来你会发现零代码开发的核心不是“不写代码”而是“把写代码的活交给模型你把需求、配置、报错这三件事管好”。需求写清楚配置填对报错原样丢回去剩下的就是等它改完重新加载。一小时做出一个能用的浏览器翻译插件真的不难。
返回列表