小白开发插件,在期间看文件的时候学到的一些东西,特地分享给大家: 博文链接: https://418122.xyz/posts/project/chrome-extension-basic-structure-beginners-guide 插件仓库链接: https://github.com/V-IOLE-T/tab-harbor GitHub 正在尝试上架 Chrome 插件商店 很多人第一次打开一个 Chrome 插件项目时,会看到一堆文件名,然后立刻陷入混乱。 index.html 、 style.css 、 manifest.json 、 background.js 、 content.js 、 app.js 看起来都像"代码文件",可它们根本不在同一个层面上工作。想真正看懂插件,关键不是背文件名,而是理解这些文件分别处在什么位置,承担什么职责,以及它们如何配合浏览器、网页和用户界面一起运转。 这篇文章想做的事很直接,就是把这些文件背后的逻辑彻底串起来,让你在看到一个插件目录时,能迅速判断每个文件到底在做什么。 index.html 是插件界面的骨架 在 Chrome 插件里, index.html 通常用来描述某个页面的内容和结构。它决定页面上会出现什么元素,比如标题、按钮、输入框、文本区域,也负责把样式文件和脚本文件接进来。你可以把它理解为一个界面的骨架,因为页面里"有什么"这件事,主要就是由 HTML 决定的。 如果一个插件有弹窗界面,那么点击插件图标后看到的内容,通常来自某个 HTML 文件。有些项目把它命名为 popup.html ,有些项目也会叫 index.html 。名字并不重要,重要的是它是否被浏览器当成插件页面真正加载了。 下面这个例子足够说明它的角色: <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="style.css"> </head> <body> <h1>Hello Chrome Extension</h1> <button>点击我</button> </body> </html> 这段代码里,页面里有哪些内容,完全由 HTML 决定。它告诉浏览器,这个页面有一个标题,有一个按钮,同时还引入了样式文件。你在页面里看到的一切界面元素,本质上都从这里开始。 style.css 决定界面看起来是什么样 如果说 HTML 负责把页面元素摆出来,那么 style.css 负责让这些元素有可读性、有层次感,也更像一个真正能用的界面。字体大小、颜色、背景、边距、按钮的外观、元素之间的排列方式,这些都属于 CSS 的领域。 比如下面这段代码: body { width: 250px; font-family: Arial, sans-serif; padding: 10px; } h1 { color: blue; font-size: 18px; } button { background-color: green; color: white; } 这段样式并没有改变页面里"有什么",但它显著改变了页面"长什么样"。这正是 CSS 的作用。很多初学者一开始会把 HTML 和 CSS 混着理解,实际上它们解决的是两个完全不同的问题。HTML 决定内容和结构,CSS 决定视觉和排版。 放到插件环境里也是一样。无论这个页面是弹窗页、选项页,还是插件扩展出来的其他界面,CSS 的职责都很稳定,就是把原本生硬的结构变成可以阅读、可以操作、也更符合界面习惯的样子。 在插件里,HTML、CSS、JavaScript 分别站在不同位置上 只看 HTML 和 CSS,还只是理解了插件界面的静态部分。一个真正可用的插件,还必须让界面"动起来",这时候 JavaScript 才会加入进来。 HTML 负责搭出页面结构,CSS 负责赋予它视觉效果,JavaScript 负责让页面对用户的操作做出反应。比如用户点击按钮后获取信息,或者把某个结果显示到页面上,这些都属于 JavaScript 的工作。 下面这段代码很简单,但它能准确展示 JavaScript 的作用: document.getElementById("btn").addEventListener("click", () => { console.log("按钮被点击"); }); 这时候你就能看到三者之间的协作关系。HTML 放了一个按钮,CSS 把这个按钮变得更清晰、易用,JavaScript 让这个按钮具备"点击以后发生事情"的能力。它们共同组成了插件界面这一层的完整逻辑。 manifest.json 是插件的入口和规则中心 当你把眼光从界面移开,就会看到插件最核心的配置文件,也就是 manifest.json 。这个文件的重要性非常高,因为 Chrome 浏览器安装和加载插件时,最先读取的就是它。没有它,插件无法被识别。写错了它,插件也可能根本无法运行。 它承担的职责可以概括为一件事,就是告诉浏览器:这个插件是谁,它有哪些页面,有哪些脚本,想申请哪些权限,以及这些能力应该如何被组织起来。 最简单的内容通常长这样: { "name": "My Extension", "version": "1.0", "manifest_version": 3 } 这里记录了插件的基本身份信息。接着你还会看到它声明插件弹窗页面: { "action": { "default_popup": "popup.html" } } 浏览器读到这里,就知道用户点击插件图标时,应该打开 popup.html 。如果插件带后台脚本,还会出现类似配置: { "background": { "service_worker": "background.js" } } 如果插件需要把脚本注入网页,则可能是这样: { "content_scripts": [ { "matches": ["https://*/*"], "js": ["content.js"], "css": ["content.css"] } ] } 如果插件要访问某些浏览器能力,还得显式申请权限: { "permissions": ["storage", "tabs", "activeTab"] } 所以,从更本质的角度看, manifest.json 的作用,就是定义插件"能做什么、在哪里做、通过谁去做"。这也是为什么它像一个总开关。你后面看到的页面、脚本、权限,最终都要回到这里去确认。 background.js 像插件的后台调度中心 理解了 manifest.json 后,再去看 background.js 就容易多了。这个文件通常不负责展示界面,也不会直接嵌在某个网页里。它更像插件的后台控制层,负责监听浏览器事件、处理全局逻辑、协调不同模块之间的通信。 比如插件刚被安装时,它可以执行初始化逻辑: chrome.runtime.onInstalled.addListener(() => { console.log("插件已安装"); }); 它也可以监听某些全局事件,或者接收来自界面页和内容脚本的消息: chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === "getData") { sendResponse({ data: "这是后台返回的数据" }); } }); 为什么插件需要这样的文件?因为有些事情不适合写在界面脚本里,也不适合写在网页注入脚本里。比如统一管理状态、协调多个标签页、处理浏览器级别的事件、访问某些只允许后台使用的 API,这些任务都更适合放在后台脚本中。 如果你使用的是 Manifest V3,那么这里还有一个重要变化。 background.js 在很多情况下是以 service_worker 的方式运行的。它不会一直常驻,而是事件来了就唤醒,执行完任务后可能进入休眠。这背后体现的是 Chrome 的设计取向,它希望插件更节省资源,也更容易控制风险。 content.js 是插件进入网页内部后的执行者 如果说后台脚本站在浏览器层面处理逻辑,那么 content.js 站在网页现场工作。它会被注入某个网页中,因此可以直接访问这个网页的 DOM,也就是页面上的标题、按钮、正文、输入框这些真实存在的元素结构。 举个最简单的例子: const title = document.querySelector("h1"); console.log(title.innerText); 这段代码能直接读取网页上的标题内容。它也可以修改页面: document.body.style.backgroundColor = "lightyellow"; 甚至还能监听页面中的某些操作。也就是说, content.js 的核心价值,在于让插件真正进入网页环境,看到页面内容,并对页面进行读取或修改。 不过这里有一个非常容易被忽略的边界。 content.js 虽然运行在网页里,但它依然属于插件。它拥有插件赋予的能力,也受到插件环境的限制。它和页面原生脚本之间并不是完全共享一切的关系,因为浏览器会通过隔离机制防止它们互相污染。这个细节很关键,因为很多初学者会误以为内容脚本和网页自身的 JavaScript 完全是一回事,实际情况并没有这么简单。 插件的核心难点,在于多个运行环境同时存在 当你把 popup.js 、 background.js 、 content.js 放在一起看时,很容易觉得它们全都是 JavaScript,所以好像只是写法不同。真正的区别并不在语法,而在运行环境。 界面脚本运行在插件自己的页面里,只有当这个页面被打开时它才活跃。后台脚本运行在插件后台,专门处理全局事件和中转逻辑。内容脚本运行在目标网页中,负责接触页面本身。这三种脚本虽然都写成 .js 文件,但它们能访问的对象、拥有的权限、存在的生命周期都不一样。 这正是 Chrome 插件学习曲线真正陡峭的地方。你卡住的往往不是 API,而是脑中没有建立"多环境协作"的图景。一旦建立起这个图景,再看文件结构就会清晰很多。 一个最常见的协作流程,到底是怎样跑起来的 假设我们做一个非常简单的插件。用户点击插件图标,弹出一个小窗口,窗口里有一个按钮,点击按钮后读取当前网页标题并显示出来。这个功能很小,但它足以把前面提到的所有角色串到一起。 首先浏览器会读取 manifest.json : { "manifest_version": 3, "name": "Title Reader", "version": "1.0", "action": { "default_popup": "popup.html" }, "permissions": ["activeTab"], "background": { "service_worker": "background.js" } } 这一步完成后,浏览器已经知道这个插件长什么样,有什么弹窗页面,有什么后台脚本,以及它申请了当前标签页权限。 当用户点击插件图标时,浏览器会根据 default_popup 打开 popup.html 。页面一打开,HTML 会把结构渲染出来,CSS 负责样式,页面脚本负责交互逻辑。如果 popup.html 里有一个按钮和一个显示结果的区域,那么脚本就可以写成这样: document.getElementById("btn").addEventListener("click", async () => { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); document.getElementById("result").textContent = tab.title; }); 如果需求只是读取当前标签页的标题,这样就够了。但如果你想读取网页内部更细的内容,比如某段正文、某个按钮文本、某个元素属性,那仅靠弹窗脚本通常不够,你就得让 content.js 进入网页现场去执行。 它可以先读取网页内容,然后把结果通过消息机制发回插件系统: const pageTitle = document.title; chrome.runtime.sendMessage({ type: "pageTitle", data: pageTitle }); 这时候如果流程稍微复杂一些,后台脚本就会出场,承担协调者角色。比如弹窗先给后台发消息,后台再联系当前标签页里的内容脚本,内容脚本拿到网页数据后返回给后台,后台再把结果转给弹窗。这个链路看起来绕了一层,但你会发现它的分工很清晰。界面只处理用户交互,后台处理协调和调度,内容脚本只关注网页现场。 示例代码大致可以写成这样。 popup.js : chrome.runtime.sendMessage({ type: "getPageInfo" }, (response) => { document.getElementById("result").textContent = response.data; }); background.js : chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === "getPageInfo") { chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { chrome.tabs.sendMessage(tabs[0].id, { type: "readTitle" }, (response) => { sendResponse({ data: response.title }); }); }); return true; } }); content.js : chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === "readTitle") { sendResponse({ title: document.title }); } }); 把这段流程真正看懂后,你对插件的整体架构就已经入门了。因为你会意识到,插件开发的本质不是单纯地写一个页面,而是把浏览器环境、插件界面和网页环境连接成一个系统。 app.js 到底是什么,它为什么总让人困惑 很多人学到这里,又会看到一个新的文件名,叫 app.js ,然后开始怀疑自己是不是漏学了某种"官方角色"。其实这里最需要澄清的一点是, app.js 并不是 Chrome 插件规范里规定必须存在的文件。它通常只是开发者自己起的名字。 这意味着,当你在一个插件项目里看到 app.js 时,不能直接根据名字判断它一定负责什么。判断它职责的关键,始终只有两件事,就是它在哪里被加载,以及它运行在什么环境中。 如果 popup.html 里有这样的代码: <script src="app.js"></script> 那这个 app.js 很可能就是弹窗页面的主逻辑脚本。它可能负责监听按钮点击、获取输入框内容、调用浏览器 API、更新页面文本等交互行为。比如: document.getElementById("btn").addEventListener("click", async () => { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); document.getElementById("result").textContent = tab.title; }); 如果它被 options.html 引入,它就可能是设置页脚本。如果它出现在 manifest.json 的后台配置中: { "background": { "service_worker": "app.js" } } 那它虽然名字叫 app.js ,实际承担的就是后台脚本的职责。如果它被声明在 content_scripts 中: { "content_scripts": [ { "matches": ["<all_urls>"], "js": ["app.js"] } ] } 那它本质上就是内容脚本。 所以,理解 app.js 最重要的一句话是, 文件名本身不决定身份,加载位置和运行环境才决定身份 。很多初学者容易被文件名带偏,以为名字像什么就是什么。实际开发里, main.js 、 index.js 、 app.js 这类名字都很常见,它们更多反映的是工程命名习惯,而不是浏览器规范。 判断一个脚本作用时,最可靠的方法是什么 如果你以后再打开一个陌生的插件项目,最稳妥的阅读方式就是先看 manifest.json ,再看 HTML 文件,最后才去看具体脚本内容。 manifest.json 会告诉你有哪些页面、有哪些后台脚本、有哪些内容脚本、申请了哪些权限。HTML 文件会告诉你哪些 JavaScript 是页面脚本,因为它们会被 <script src="..."> 直接引入。脚本内容本身才会进一步告诉你,这个文件内部具体写了什么业务逻辑。 这个阅读顺序很重要,因为它会强迫你从"运行上下文"去理解代码,而不是从"文件名猜测"去理解代码。你一旦形成这种习惯,不光看 Chrome 插件会快很多,将来学 Vue、React,甚至看更复杂的前端工程,也会有更强的拆解能力。 学插件时,最推荐的理解路径 对于初学者来说,最容易迷失的地方,是一上来就想把所有文件和 API 全部记住。这样做通常效果很差,因为你的脑海里没有一张系统图,记忆就没有挂钩点。 更好的路径,是先看清谁负责界面,也就是 HTML、CSS 和页面脚本。然后理解谁负责进入网页读取和修改内容,也就是 content.js 。最后再理解谁负责监听全局事件、连接各个模块、处理中间调度,也就是 background.js 。当这条主线建立起来以后,消息传递、权限管理、脚本注入这些内容都会顺势变得可理解。 如果你一定要用一句话概括整个插件结构,那么这句话可以写成这样: manifest.json 先注册角色并声明权限,HTML 和 CSS 负责界面呈现,JavaScript 负责交互逻辑, background.js 负责后台调度, content.js 负责进入网页执行具体动作,而 app.js 是否重要,取决于它究竟被谁加载、运行在哪个环境里。 结尾:真正该建立的,是"环境意识" 学 Chrome 插件,表面上像是在学很多零散文件。更深一层看,你其实是在学习多个受控运行环境如何协作。浏览器层、插件界面层、网页内容层,这三层有各自的边界,也通过消息和配置互相连接。你一旦抓住这个系统视角,就不会再被文件名和目录结构轻易迷惑。 所以当你下次看到一个插件项目时,先别急着问"这个文件名是什么意思"。更值得问的是,这个文件由谁加载,它运行在哪里,它能访问什么,它和谁通信。只要这四个问题想清楚,整个项目的骨架就会慢慢显形。 【拓展思考】 Chrome 插件很像一个小型多进程系统。弹窗脚本、后台脚本、内容脚本分别处在不同上下文中,消息传递像协议,权限声明像访问控制,页面注入像受限部署。你如果从系统设计的视角理解插件,后面学浏览器扩展、Electron、前端工程化,很多概念都会互相打通。 2 个帖子 - 2 位参与者 阅读完整话题
<script setup> import { ref } from 'vue'; import { onLoad } from '@dcloudio/uni-app'; import request from '@/utils/request'; import { getFiles, getRandomFile, getFileArray } from '@/utils/yangUtils'; const imageSuffix = ['png', 'jpg', 'jpeg']; const videoSuffix = ['mp4', 'avi', 'mov']; const imageNum = ref(0); const videoNum = ref(0); // 获取父级传入的数据 const props = defineProps(["modelValue", "limit", "message", "isDelete", "isAdd"]); // 定义 emits const emit = defineEmits(['update:modelValue']); // const listData = ref(["https://yxdjpw.oss-cn-beijing.aliyuncs.com/uploadDefault/20260417192542-d3ea46.png", "https://yxdjpw.oss-cn-beijing.aliyuncs.com/uploadDefault/20260417194449-bfb1ba.mp4"]); const listData = ref([]) const onSelectFile = async () => { let result; // #ifdef APP || APP-PLUS || MP-WEIXIN || MP-TOUTIAO || MP-LARK || MP-JD || MP-HARMONY || MP-XHS result = await uni.chooseMedia({ count: props.limit || 9, mediaType: ['image', 'video'], sourceType: ['album', 'camera'], maxDuration: '30s' }) // #endif // #ifdef WEB || H5 result = await uni.chooseFile({ count: props.limit || 9, type: 'all', }) // #endif let arr = []; console.log(result); for (let filePath of result.tempFiles) { // 判断文件是否超出10M if (filePath.size > 10 * 1024 * 1024) { uni.showModal({ title: '提示', content: '文件大小不能超过10M!', showCancel: true, }) return; } if (imageSuffix.includes(filePath.name.replaceAll('"', '').split('.').pop()?.toLowerCase())) { imageNum.value++; } else if (videoSuffix.includes(filePath.name.replaceAll('"', '').split('.').pop()?.toLowerCase())) { // 判断之前有没有上传过视频或者图片 if (imageNum.value >= 1 || videoNum.value >= 1) { uni.showModal({ title: '提示', content: '图片和视频不能同时上传,并且视频只能上传一个!', showCancel: true, }) return; } videoNum.value++; } const data = await request("/upload/uploadFile", filePath.path, "post"); arr.push(getFileArray(data)[0]); } listData.value = listData.value.concat(arr); // 将数据返回出去 emit("update:modelValue", listData.value.join(",")); } // 删除 const onDelete = (index) => { // 1. 先拿到要删除的项(必须在 splice 之前拿!) const deletedItem = listData.value[index]; // 2. 判断类型,更新计数 const ext = deletedItem.split('.').pop()?.toLowerCase(); if (imageSuffix.includes(ext)) { imageNum.value--; } else if (videoSuffix.includes(ext)) { videoNum.value--; } // 3. 再删除元素 listData.value.splice(index, 1); // 4. 更新双向绑定 emit("update:modelValue", listData.value.join(",")); } // 查看 const onView = (item) => { } // 类型判断 const isSuffix = () => { return listData.value.some(item => { // 获取后缀(转小写,避免大小写问题) const ext = item.split('.').pop()?.toLowerCase() return videoSuffix.includes(ext) }) } </script> <template> <view> <view class="header"> <view v-if="props.message" class="message">{{ props.message }}</view> <view class="numCount">{{ `${listData.length}/${props.limit || 9}` }}</view> </view> <view class="image-grid" :class="isSuffix() ? 'grid-video' : ''"> <template v-for="(item, index) in listData" :key="index"> <view class="grid-item grid-item-content" :class="isSuffix() ? 'grid-item-video' : ''" @click="onView(item)"> <image v-if="imageSuffix.includes(item.split('.').pop())" class="img" :src="item" mode="aspectFill" /> <video v-else-if="videoSuffix.includes(item.split('.').pop())" class="video" :src="item" /> <view class="grid-item-delete" v-if="(isAdd ?? true)" @click="onDelete(index)"><uni-icons class="icon-delete" type="closeempty" color="#D3D4D6" size="24" /></view> </view> </template> <view class="grid-item icon-add" v-if="(isAdd ?? true) && !isSuffix() && listData.length < (props.limit ?? 9)" @click="onSelectFile()"> <uni-icons type="plusempty" size="60" color="#F1F1F1"></uni-icons> </view> </view> </view> </template> <style lang="scss" scoped> .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; } .image-grid { display: grid; grid-template-columns: repeat(3, 1fr); /* 一行3个,自动均分 */ gap: 20rpx; /* 格子间距 */ box-sizing: border-box; } .grid-item { width: 240rpx; height: 240rpx; } .grid-video { grid-template-columns: repeat(1, 1fr) !important; } .grid-item-video { width: 100%; height: 400rpx; } .numCount { display: flex; flex-direction: row-reverse; font-size: 26rpx; } .img, .video { width: 100%; height: 100%; } .grid-item-content { position: relative; } .icon-add { background: #FFFFFF; line-height: 240rpx; text-align: center; border: 1rpx solid #EEEEEE; border-radius: 6rpx; } .grid-item-delete { position: absolute; top: 0rpx; right: 0rpx; z-index: 1; } </style> 方法解析 文件后端返回的是 /api/upload/xxx.png getFiles 将图片拼接成正常能访问的 比如 http://域名.com/api/upload/xxx.png getRandomFile 是随机访问 getFileArray 是拼接成数组 limit → 最多上传数量 默认为9 message → 提示 比如请上传视频 可不填 isDelete → 是否可以删除重新上传 默认为true isAdd → 是否可以新增,比如有些地方可以直接回显 比如产品的图片,但是你不想让它显示新增的框 可以为false 默认为true // 文件加载 export const getFiles = (url) => { if (!url) { return ""; } if (url.startsWith("http://") || url.startsWith("https://")) { return cleanString(url); } else { return baseURL + cleanString(url); } }; // 随机加载文件 export const getRandomFile = (url) => { if (!url) { return ""; } const urls = url.split(","); const randomIndex = Math.floor(Math.random() * urls.length); const selectedUrl = urls[randomIndex]; if ( cleanString(selectedUrl).startsWith("http://") || cleanString(selectedUrl).startsWith("https://") ) { return cleanString(selectedUrl); } else { return baseURL + cleanString(selectedUrl); } }; export const getFileArray = (url) => { if (!url) { return []; } let baseUrl = []; url.split(",").forEach((v) => { if ( cleanString(v).startsWith("http://") || cleanString(v).startsWith("https://") ) { baseUrl.push(cleanString(v)); } else { baseUrl.push(baseURL + cleanString(v)); } }); console.log(baseUrl); return baseUrl; }; 请求就是 uni.request的请求 你们按照你们自己的改 当前只支持H5 你们需要的话 我会再更新 如果上传了视频就直接独占一行 并且不能上传其他视频和图片 仿照的是朋友圈 其他页面调用就是 比如文件名叫uploadFile.vue吧 import UpLoadFile from '@/components/uploadFile.vue' // 页面代码 <UpLoadFile v-model="form.media" /> 第一次封装 还有些问题 我会持续更新的 1 个帖子 - 1 位参与者 阅读完整话题
想请教Python代码在linux系统打包成.exe文件的方法 10 个帖子 - 10 位参与者 阅读完整话题
我让他根据我的代码写论文,前面一切正常,获取文件,读取文件啥的,但是一旦进入说要开始写论文,就会像这样卡住,token数也不增加。 我用的是第三方中转,后台已经5分钟没有新的请求了,但是在终端还在转圈圈 4 个帖子 - 4 位参与者 阅读完整话题
各位有遇到过吗?传输大概10G文件,中途就卡死不动了 8 个帖子 - 6 位参与者 阅读完整话题
我默认名称是惠普,现在用codex apply patch有问题,我已急哭 15 个帖子 - 15 位参与者 阅读完整话题
之前看过佬发的instruction.md,这个文件的破限仅限于逆向呢,还是说安装后所有的话题都破掉了呢?有佬实验过么。。。 1 个帖子 - 1 位参与者 阅读完整话题
正常模式下 左侧是文件目录树,右侧是AI对话。 SOLO模式下 非要把这俩反过来,还不能手动改。 5 个帖子 - 4 位参与者 阅读完整话题
在配置文件中指定 Model Name,可实时切换模型。当前支持配置的 Model Name: 可用模型 deepseek-v3.2 kimi-k2.5 glm-5 minimax-m2.5 ernie-4.5-turbo-20260402 接口地址 OpenAI 兼容: https://qianfan.baidubce.com/v2/coding Anthropic 兼容: https://qianfan.baidubce.com/anthropic/coding bce-v3/ALTAKSP-f5dZc3ndJHYbxrDCZiV0U/621c8c0f42e4e93b6b89a37d93f7e3ebc469b7a1 1 个帖子 - 1 位参与者 阅读完整话题
站内有很多脚本清理401的,但每次都要找到文件然后打开终端运行脚本,感觉有点麻烦。可能适合服务器定时调用,但我是本地开发不太想在本机弄定时任务。就根据站内的方法改写了个油猴插件,安装之后在该页面会出现账号统计,以及清理工具。 一直在纠结是否要加禁用的功能,因为很多脚本都有禁用额度耗尽的,而且禁用了之后没有恢复开启,额度刷新也不会开启,手动开启也很慢。现在的号不如之前容易弄,能等到额度刷新也不想把号废了,而且现在古法插件注册的号存活率还蛮高的。 关于账号耗尽账号是否要禁用可以查看该issue,cpa内置已经考虑了额度耗尽调过移除队列和恢复之后重回队列的逻辑。因此禁用额度耗尽的账号属于负优化,所以我增加了一个一键启用的功能,帮大家把被禁用的账号全部启用。 github.com/router-for-me/CLIProxyAPI codex认证文件,在额度用完是否需要关闭? 已打开 01:36AM - 08 Apr 26 UTC 已关闭 05:41PM - 12 Apr 26 UTC aicloud-cpu 开源作者们,你们好! codex认证文件,在额度用完是否需要关闭? 如果不关闭,轮询会不会跳过这个额度已经用完的认证文件? 如果关闭了,系统自动刷新toke … n的时候会不会跳过不刷新? 脚本内容太长没办法直接贴进来,大家自行下载吧。 clear_401.user.js.7z (16.6 KB) 2 个帖子 - 2 位参与者 阅读完整话题
AI Coding 时代,vscode 的职责基本只剩下了: 文件预览 全局搜索 git 管理和 diff 查看 但为了这些功能专门去开启一个 vscode 实在有点不值,于是我就整了个终端版的集成了这些功能的 TUI 来取代 vscode 的作用,占用小,速度快,并发跑 Coding Agent 更友好。 blushyes.github.io Reef — the minimal dev terminal for the AI coding era AI writes the code. You review it. Reef does only the second half. A keyboard-first terminal workbench: file tree, read-only preview, git diff. 4 个帖子 - 3 位参与者 阅读完整话题
我做python程序时打包后文件会特别大,特别是为了程序看起来好看使用py6界面,实际功能其实不多,最多就是进行了格式转化,并且打包的文件每次运行都要很久,让ai优化,ai建议用文件夹打包的方法,我按照要求打包之后运行速度还是很慢,想请教下大家有这个问题吗?怎么解决的?是继续用python还是换其他编码方式 1 个帖子 - 1 位参与者 阅读完整话题
如题,楼主古法自动注册了若干个codex账号,导入CPA, 发现只能导入45个 ,超过这个数量的账号都没法导入成功了,没找到哪里有配置在限制。 各位佬友有遇到过这种情况吗? 5 个帖子 - 4 位参与者 阅读完整话题
best权重是个软链接,指向具体的权重文件。我以为ChatGPT能反应过来,结果直接一下全删了。给的默认权限,现在删文件都不需要审核了!奥特曼对自家模型也太自信了 尤其是ChatGPT在闯祸之后还一种无所吊谓的语气,真是气死我了! 重新训练吧… 4 个帖子 - 3 位参与者 阅读完整话题
IT之家 4 月 18 日消息,微软昨日(4 月 17 日)发布博文,邀请 Release Preview 频道的 Windows Insider 项目成员,测试适用于 Windows 11 的 KB5083631 更新, 24H2 用户安装后版本号升至 Builds 26100.8313;25H2 用户安装后版本号升至 Build 26200.8313。 游戏体验方面,微软为该预览版引入 Xbox 模式,该模式源自 Xbox 主机界面, 提供简洁的全屏沉浸式体验,让游戏内容成为视觉焦点,有效屏蔽后台干扰。 用户可通过 Xbox 应用、游戏栏设置或快捷键 Win + F11 进入该模式,适配笔记本、台式机及平板电脑等多种设备形态。 文件管理器方面,微软重点改进文件夹视图, 用户自定义设置(例如按名称对文件进行排序或调整图标大小),系统会在打开文件夹的所有方式中生效。 例如用户从其他应用程序(例如网页浏览器)打开同一文件夹后,会自动保留用户偏好设置。 微软在新版中还提升文件管理器的启动速度和进程可靠性,修复了深色模式下的“闪光弹”白屏闪烁问题。 微软还进一步扩展文件管理器的原生压缩包支持格式,新增支持 uu、cpio、xar 及 NuGet 包等压缩格式。 在任务栏体验上,微软引入 Agents 监控功能,支持第一方及第三方应用。IT之家援引博文介绍,在该预览版中, 已率先适配 Microsoft 365 Copilot 应用中的 Researcher 功能,用户悬停图标即可查看报告生成进度 ,任务完成后系统会即时通知。第三方开发者可通过 Windows.UI.Shell.Tasks API 集成该功能。 输入体验方面,系统新增触觉信号反馈,支持 Surface Slim Pen 2、ASUS Pen 3.0 等设备,用户在窗口对齐或对象对齐时可获得触觉反馈。新版简化触摸键盘语音输入界面,移除全屏遮罩,动画直接显示在听写键上。 安全与管理层面,Windows 内核调整了第三方驱动信任机制,移除对跨签名驱动的默认信任,仅保留 WHCP 认证驱动及可信旧驱动白名单。
IT之家 4 月 18 日消息,微软今天(4 月 18 日)发布博文,邀请 Canary 频道的 Windows Insider 项目成员,测试 Windows 11 预览版 28020.1863 和 29570.1000 预览版。 在 Windows 11 Build 28020.1863 预览版中,微软在常规提升系统体验外,主要修复网络问题,该问题会导致系统错误报告无互联网连接,进而阻碍部分应用程序的正常登录。 在 Windows 11 Build 29570.1000 预览版中,微软引入全新 Xbox 模式,为 PC 提供主机般沉浸式全屏游戏体验。 该模式源自 Xbox 主机界面,提供简洁的全屏沉浸式体验,让游戏内容成为视觉焦点,有效屏蔽后台干扰。用户可通过 Xbox 应用、游戏栏设置或快捷键 Win + F11 进入该模式,适配笔记本、台式机及平板电脑等多种设备形态。 在文件管理器中,微软优化右键菜单逻辑, 用户右键.exe、.bat 及 .cmd 文件后,“打开”选项图标将匹配其默认应用图标。 IT之家附上相关截图如下: 触控板设置新增右键区域大小调节功能,提供默认、小、中、大四档选择,兼容支持可按压表面的触控板硬件。 锁屏小组件个性化功能扩展至全球区域,用户可自由添加、移除或排序天气、关注列表、体育及交通等小组件。新版还改进触控笔设置页面,笔尾按键新增“与 Copilot 键相同”选项,实现一键调用。 新版还升级企业管理功能,管理员可利用组策略,通过指定应用包系列名称,移除额外的 MSIX / APPX 格式预装应用。
IT之家 4 月 18 日消息,微软昨日(4 月 17 日)发布博文,邀请 Dev 和 Beta 频道的 Windows Insider 项目成员,测试 Windows 11 最新预览版。 Dev 频道用户安装 KB5083726 后,版本号升至 Build 26300.8276;Beta 频道安装 KB5083728 后,版本号升至 Build 26220.8271。 IT之家注:Dev 和 Beta 频道目前的更新内容基本相同,聚焦于优化系统细节体验,涵盖设置界面、文件资源管理器、输入体验以及 Windows Hello 等多个核心模块。 微软为了消除用户困惑,在位置服务关闭后,明确提示应用与服务无法获取位置信息。用户在设置 > 隐私和安全性 > 位置路径下,关闭定位服务后,自动禁用默认位置(Default location)和允许位置覆盖(Allow location override)选项,并显示灰色状态。 针对文件管理器,新版多项视觉与功能调整,首先是更新了搜索框内的图标布局,让其在不同设备间保持视觉一致性。新版该提升无障碍操作的流畅度,改善了语音访问用户使用导航窗格的体验。 旧版搜索框。图源:微软 新的搜索框。图源:微软 Copilot+ 电脑搜索框。图源:微软 系统交互方面,微软提升 Windows Hello 指纹识别在电脑从睡眠唤醒后的性能与可靠性,解决了唤醒后解锁迟缓的问题。 输入体验方面,微软新版优化了日常复制粘贴的操作效率,加速剪贴板历史记录的打开速度。微软还优化多语言显示效果,升级字体渲染引擎,,Leelawadee UI 字体家族针对泰语、老挝语等脚本进行了字形排序与定位优化。
但网上居然搜不到相关反馈,总不能只有我一个人遇到这问题吧 图一的软件,用的是music tag 3 个帖子 - 2 位参与者 阅读完整话题
有大佬用过office-cli这个skill吗?我今天晚上电脑不知道咋了,用着用着给我弹出来一个html文件 我看了一下这个文件前缀叫officecli 我怀疑是这个skill干的,但是我电脑啥也没干呀,也没开什么agent干活。不能是电脑被黑了吧 1 个帖子 - 1 位参与者 阅读完整话题
用的中转站,wsl中使用codex cli,版本是最新版 部分配置 sandbox_mode = "workspace-write" approval_policy = "on-request" 3 个帖子 - 2 位参与者 阅读完整话题