当前位置: 首页 > news >正文

开源AI原生代码编辑器Void:构建可定制、隐私优先的编程助手

1. 项目概述:一个开源的AI原生代码编辑器

最近在开发者圈子里,一个名为Void的项目引起了我的注意。简单来说,Void是一个开源的、对标Cursor的代码编辑器。如果你对Cursor有所了解,就会知道它是一款深度集成了AI能力的现代化IDE,而Void的目标就是提供一个开源、可自由定制且同样强大的替代品。它的核心卖点在于,允许你在自己的代码库上运行AI智能体,可视化地检查和管理代码变更,并且最关键的是,你可以自由选择任何AI模型,甚至将其部署在本地环境中。这意味着你的代码和与AI的交互数据,可以完全掌握在自己手中,因为它直接将消息发送给AI服务提供商,自身不保留任何用户数据。

这个项目对于像我这样,既希望享受AI编程助手的便利,又对数据隐私和模型选择有较高要求的开发者来说,非常有吸引力。无论是前端、后端还是全栈开发者,如果你厌倦了闭源工具的束缚,或者希望有一个能深度融入自己工作流的、可编程的AI伙伴,Void都值得你花时间了解一下。它基于我们熟悉的Visual Studio Code代码库进行深度定制和开发,这意味着它继承了VSCode强大的扩展生态和用户体验基础,同时又在其之上构建了全新的AI原生工作流。

2. 核心特性与设计思路拆解

2.1 为何选择“开源Cursor”这个定位?

Cursor的成功已经证明了市场对“AI-First”代码编辑器的强烈需求。它模糊了传统IDE与AI助手的边界,让代码补全、解释、重构甚至新功能开发都变得像对话一样自然。然而,作为闭源商业产品,Cursor在数据隐私、模型选择(通常绑定特定供应商)和深度定制化方面存在天然限制。

Void选择从这个痛点切入,其设计思路非常清晰:在成熟、强大的VSCode地基上,构建一个开源、透明、可插拔的AI协作层。这样做有几个显著优势:

  1. 降低使用门槛与风险:开发者无需担心“vendor lock-in”(供应商锁定)。你的所有代码、对话历史、项目上下文都完全由你控制。你可以选择将数据发送给OpenAI的ChatGPT、Anthropic的Claude,或者任何你信任的、甚至自己微调的开源模型(如Llama、CodeLlama等)。
  2. 极致的可定制性:因为是开源项目,你可以审查每一行代码,修改任何你觉得不合适的功能,或者集成自己公司内部的AI服务。这对于有严格合规要求的企业或对工具有特殊偏好的极客开发者来说,是无可替代的价值。
  3. 社区驱动的创新:开源生态能够汇聚全球开发者的智慧,快速迭代出各种意想不到的插件、智能体和工作流,其创新速度可能远超单个公司的闭源开发。

2.2 核心功能模块解析

从项目描述和其愿景来看,Void的核心功能模块主要围绕以下几个层面构建:

AI智能体集成与调度:这是Void的心脏。它需要一套健壮的架构来连接不同的AI服务提供商(Provider)。这不仅仅是一个简单的API调用封装,而是包括对话上下文管理、提示词工程、流式响应处理、错误重试和费用监控等一整套系统。Void需要提供一个统一的接口,让开发者能以一致的方式调用ChatGPT、Claude、Copilot或本地模型。

代码变更的可视化与检查点:这是提升开发体验的关键。传统IDE的“撤销/重做”是线性的,而Void引入的“检查点”概念,更像是为代码状态拍快照。你可以随时创建一个检查点,然后让AI进行大刀阔斧的重构或尝试新的实现方案。如果结果不满意,可以一键回滚到某个清晰的检查点,而不是在一堆混乱的撤销历史中挣扎。可视化工具则能帮你直观地对比AI修改了哪些文件、哪些行,具体改动了什么,这大大提升了代码审查和理解的效率。

本地化与隐私优先架构:Void强调“发送消息而不保留数据”。这意味着其架构设计上,编辑器本体可能只是一个“客户端”,负责组织请求和渲染界面,而将包含代码和问题的消息直接发送给用户配置的AI终端。服务器端(如果有的话)不存储对话日志或代码片段。对于支持本地部署的模型(如通过Ollama、LM Studio运行的模型),整个交互过程可以完全在离线环境下完成,实现了真正的数据零泄露。

基于VSCode的扩展与兼容:作为VSCode的一个分支(fork),Void天然兼容VSCode庞大的扩展市场。这意味着开发者无需放弃已有的工具链(如GitLens、ESLint、Prettier等),可以平滑过渡。同时,Void团队可以在VSCode的Monaco编辑器、语言服务器协议等成熟组件之上,专注于构建其独特的AI功能,避免了重复造轮子。

3. 从源码构建与深度定制指南

虽然项目目前处于“探索性暂停”状态,但其代码库完全开放,对于想要尝鲜或基于此进行二次开发的开发者来说,这正是一个深入理解其架构的好时机。以下是我根据官方文档和代码库结构梳理的构建与定制路径。

3.1 环境准备与依赖安装

构建Void与构建VSCode类似,对开发环境有一定要求。首先,你需要确保你的系统满足以下条件:

  • Node.js:建议使用最新的LTS版本(如18.x或20.x)。VSCode及其衍生品的构建工具链对Node版本有一定要求,使用过旧或过新的版本可能导致未知错误。
  • Git:用于克隆代码库和后续的版本管理。
  • Python:某些构建步骤或原生模块编译可能需要Python。建议安装Python 3.x版本。
  • C++构建工具:在Windows上,你可能需要安装Visual Studio Build Tools或Windows SDK;在macOS上,需要Xcode Command Line Tools;在Linux上,需要gcc、g++、make等基础编译工具。
  • Yarn:VSCode生态使用Yarn 1.x作为包管理器,而不是npm。你需要全局安装Yarn (npm install -g yarn)。

准备好环境后,克隆仓库并安装依赖:

git clone https://github.com/voideditor/void.git cd void yarn

这个yarn命令会读取package.json,安装所有必要的Node模块依赖。由于依赖数量庞大,首次安装可能需要较长时间(10-30分钟,取决于网络速度)。

注意:构建过程可能会消耗大量内存(建议至少8GB可用内存)和磁盘空间。如果遇到内存不足的错误,可以尝试设置Node.js的堆内存限制:export NODE_OPTIONS="--max-old-space-size=8192"(在Linux/macOS终端中)或设置对应的环境变量。

3.2 理解代码库结构与核心模块

Void的代码库结构基本继承了VSCode,但核心的AI功能会集中在特定的目录中。对于想要定制AI行为的开发者,需要重点关注以下几个部分:

  1. src/vs/workbench/contrib/chat/或类似目录:这里很可能存放着与AI对话界面、交互逻辑相关的核心代码。包括聊天面板的UI组件、消息渲染、输入框处理等。
  2. src/vs/workbench/services/chat/src/vs/platform/ai/:这里可能定义了AI服务的抽象层、提供商接口、请求/响应模型、上下文管理等后端服务。这是连接不同AI模型的关键。
  3. src/vs/base/common/src/vs/platform/:这些是VSCode的基础设施库,包含事件、命令、配置、存储等通用模块。Void的AI功能很可能通过扩展这些基础能力来实现。
  4. build/目录:包含项目构建和打包的脚本。如果你想修改最终产物的名称、图标或打包方式,需要从这里入手。
  5. 产品化配置文件:如product.jsonpackage.json中的特定字段。这些文件定义了编辑器的名称(Void)、版本号、应用ID、默认配置等产品信息。

官方推荐的入门指南是阅读VOID_CODEBASE_GUIDE.md文件,它会提供更详细的代码地图。理解这些结构,是你进行任何功能修改或调试的第一步。

3.3 编译、运行与调试

安装完依赖并了解了代码结构后,就可以开始编译和运行了。

开发模式运行

yarn watch

执行yarn watch会启动一个监视进程,它会编译TypeScript/JavaScript源代码,并将结果输出到out/目录。这个过程是增量编译的,即你修改源代码后,它会自动重新编译相关的模块,非常适合开发阶段。

在另一个终端标签页中,运行:

yarn electron

这个命令会使用Electron启动编译好的Void编辑器。你现在运行的就是你自己编译的开发版本。你可以在此版本中测试功能、修改代码,并实时看到变化(得益于yarn watch)。

打包生成可分发版本: 如果你想生成一个可以安装的独立应用(如.dmg, .exe, .deb文件),需要使用专门的打包脚本。Void可能提供了类似VSCode的yarn run package命令,或者参考其独立的构建器仓库void-builder

# 可能的方式之一,具体请参考项目根目录的package.json中的scripts字段 yarn run build:production # 然后进行打包 yarn run package

打包过程会生成针对当前操作系统的安装包。这个过程更耗时,并且需要确保所有依赖和原生模块都已正确编译。

调试技巧

  • 主进程调试:Void作为Electron应用,分为主进程和渲染进程。你可以通过在VSCode(或其它编辑器)中配置调试启动任务来附加调试器。
  • 渲染进程调试:在开发版Void中,你可以直接使用Chrome开发者工具(Ctrl+Shift+ICmd+Opt+I)来调试界面和渲染进程的脚本,这和调试网页应用一样。
  • 日志输出:关注终端中运行yarn watchyarn electron的窗口输出,任何编译错误或运行时异常都会在这里打印。

4. 集成自定义AI模型与智能体实战

Void最大的魅力在于其开放性,允许你接入任何AI模型。下面我将以一个假设的场景,详细说明如何将一个本地运行的开源代码模型(例如通过Ollama运行的CodeLlama)集成到Void中。

4.1 理解AI提供商接口

首先,我们需要找到Void中定义AI服务提供商的地方。通常,会有一个抽象类或接口,比如叫做IAIProviderChatProvider。这个接口会定义几个核心方法:

  • sendMessage(request: ChatRequest): Promise<ChatResponse>:发送消息并获取流式或非流式响应。
  • getModels(): Promise<Model[]>:获取该提供商支持的模型列表。
  • validateConfig(config: ProviderConfig): boolean:验证用户提供的配置(如API密钥、端点地址)是否有效。

你的任务就是创建一个新的类,例如OllamaProvider,来实现这个接口。你需要查阅Ollama的本地API文档(通常是一个REST API,运行在http://localhost:11434),了解其对话接口的请求格式和响应格式。

4.2 实现本地模型提供商

假设我们在src/vs/workbench/services/chat/contrib/目录下创建一个新文件ollamaProvider.ts

// 示例代码,结构仅供参考 import { IAIProvider, ChatRequest, ChatResponse, StreamHandler } from '../common'; export class OllamaProvider implements IAIProvider { id = 'ollama'; name = 'Ollama (Local)'; private baseUrl: string; constructor(private config: { endpoint: string }) { this.baseUrl = config.endpoint || 'http://localhost:11434'; } async getModels(): Promise<Model[]> { try { const response = await fetch(`${this.baseUrl}/api/tags`); const data = await response.json(); // 将Ollama返回的模型列表转换为Void内部定义的Model格式 return data.models.map((m: any) => ({ id: m.name, name: m.name, provider: this.id, })); } catch (error) { console.error('Failed to fetch models from Ollama:', error); return []; } } async sendMessage(request: ChatRequest, streamHandler?: StreamHandler): Promise<ChatResponse> { const { messages, modelId, options } = request; // 将Void的对话历史格式转换为Ollama API所需的格式 const ollamaMessages = messages.map(msg => ({ role: msg.role, // 可能需要映射,如 'user', 'assistant', 'system' content: msg.content, })); const body = { model: modelId, messages: ollamaMessages, stream: !!streamHandler, // 是否启用流式响应 options: { temperature: options?.temperature || 0.7, // ... 其他参数 }, }; const response = await fetch(`${this.baseUrl}/api/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); if (!streamHandler) { // 非流式响应 const data = await response.json(); return { content: data.message.content, model: data.model, }; } else { // 流式响应处理 const reader = response.body?.getReader(); if (!reader) throw new Error('No response body'); // 这里需要处理流式数据,分块解码并调用streamHandler // ... 流式处理逻辑 return { content: '' }; // 流式处理中,最终内容由handler收集 } } validateConfig(config: any): boolean { return typeof config.endpoint === 'string' && config.endpoint.length > 0; } }

4.3 注册提供商并配置界面

实现类之后,需要在Void的扩展激活点或服务注册中心将其注册。这通常在一个入口文件(如src/vs/workbench/services/chat/contrib/contributions.ts)中完成。

import { Registry } from 'vs/platform/registry/common/platform'; import { IAIProviderRegistry, Extensions } from '../common'; import { OllamaProvider } from './ollamaProvider'; // 在扩展激活时注册 Registry.as<IAIProviderRegistry>(Extensions.AIProviderRegistry).registerProvider( new OllamaProvider() );

接下来,你还需要在Void的设置UI中添加配置项,让用户能够输入Ollama服务的地址。这涉及到修改设置架构定义和UI组件。用户最终在设置里看到的效果可能是:

AI Providers: [x] OpenAI (ChatGPT) [x] Anthropic (Claude) [ ] Ollama (Local) Endpoint: http://localhost:11434

4.4 测试与验证

完成代码修改后,重启开发版的Void。在AI聊天界面,你应该能在模型选择下拉列表中看到你从本地Ollama拉取到的模型(如codellama:7b,deepseek-coder等)。选择一个模型进行对话,如果一切配置正确,Void就会将你的代码和问题发送到本地的Ollama服务,并将回复展示出来。

实操心得:在集成本地模型时,最大的挑战往往是网络请求的稳定性和错误处理。本地服务可能未启动、端口被占用或模型未加载。你的Provider实现必须包含健壮的错误处理,给用户清晰的反馈(例如“无法连接到Ollama服务,请检查是否已启动”),而不是一个晦涩的网络错误。此外,流式响应的处理比一次性响应更复杂,你需要正确处理文本的分块(chunk)接收、解码和实时渲染,这涉及到前端的事件处理和状态更新。

5. 构建自己的“检查点”与可视化diff系统

Void宣传的另一个亮点是“检查点和可视化变更”。这个功能本质上是一个增强版的、与AI操作深度集成的版本控制系统快照。

5.1 检查点的工作原理

在传统开发中,我们使用Git来管理代码状态。Void的检查点可以理解为在本地、针对单个工作区或文件,创建了一个轻量级的、语义化的Git Commit。其技术实现可能包括:

  1. 状态捕获:当用户触发“创建检查点”命令时,Void会遍历当前工作区中所有已打开或受监控的文件,获取它们当前的内容。
  2. 差异计算与存储:它可能不会完整存储每个文件的全部内容(那样太占空间),而是计算相对于上一个检查点或初始状态的差异(diff)。这些差异数据会被结构化地存储在本地的某个数据库(如SQLite)或文件系统中。
  3. 元数据关联:每个检查点会保存丰富的元数据,例如创建时间、关联的AI对话ID或提示词、用户添加的标签或描述。这使得你可以通过“为尝试重构函数A创建检查点”这样的语义来查找历史,而不是枯燥的时间戳。
  4. 快速回滚:回滚到某个检查点时,系统会应用从当前状态到目标状态的反向差异,快速将文件内容还原。这个过程应该在内存中完成,并给予用户预览确认的机会,避免误操作。

5.2 实现一个简单的检查点系统思路

如果你想在Void的基础上扩展或理解其实现,可以思考以下简化版的代码逻辑:

// 伪代码,展示核心概念 class CheckpointManager { private storage: CheckpointStorage; // 负责差异的存储和读取 async createCheckpoint(workspaceRoot: string, label: string, context?: AIContext): Promise<CheckpointId> { const currentFileSnapshots = await this.captureFileStates(workspaceRoot); const previousCheckpoint = await this.getLatestCheckpoint(); // 计算差异 const diffs = this.computeDiffs(previousCheckpoint?.fileSnapshots, currentFileSnapshots); // 创建检查点对象 const checkpoint: Checkpoint = { id: generateId(), timestamp: Date.now(), label, diffs, aiContext: context, // 关联的AI会话信息 }; // 存储 await this.storage.save(checkpoint); return checkpoint.id; } async restoreCheckpoint(checkpointId: CheckpointId): Promise<void> { const targetCheckpoint = await this.storage.load(checkpointId); const currentState = await this.captureFileStates(); // 计算从当前状态回退到目标状态所需的“反向补丁” const reverseDiffs = this.computeReverseDiffs(currentState, targetCheckpoint.diffs); // 在UI中预览变更 const userConfirmed = await this.previewChanges(reverseDiffs); if (userConfirmed) { // 应用反向差异,还原文件 await this.applyDiffsToFiles(reverseDiffs); } } private computeDiffs(before: FileState[], after: FileState[]): FileDiff[] { // 使用类似diff-match-patch的算法计算文本差异 // 对于每个文件,生成一个差异描述对象 } }

5.3 可视化差异界面

可视化是检查点功能体验的核心。Void需要提供一个比传统Git diff更友好、更聚焦于AI协作的界面。

  1. 树形文件列表:左侧以树状结构展示在两次检查点之间发生变更的文件。文件图标旁可以用颜色高亮(绿色代表新增行,红色代表删除,蓝色代表修改)。
  2. 并排对比视图:点击一个文件,主区域展示并排(Side-by-Side)或内联(Inline)的差异对比。关键是要高亮显示AI生成或修改的代码块,可能通过特殊的背景色或边框来区分于人工修改。
  3. 变更摘要与导航:对于大型变更,提供一个摘要面板,列出所有被AI影响的函数、类或方法。用户可以点击摘要项快速跳转到对应的代码位置。
  4. 操作集成:在差异视图的每一处修改旁,提供便捷的操作按钮,如“接受此块变更”、“拒绝此块变更”、“就此变更继续与AI对话”。这实现了从代码审查到迭代优化的闭环。

注意事项:实现一个健壮的差异计算和可视化系统非常复杂,尤其是要处理好代码的移动(move)、重命名(rename)等复杂变更。直接使用现成的文本diff库(如diff-match-patch)可能对代码的结构化感知不够。更高级的实现可以考虑集成语言服务器协议,获取语法树级别的差异,这样能更准确地识别出“一个函数被重构了”而不是“一堆行被删除和添加了”。

6. 项目现状、挑战与未来展望

根据项目README中的“Note”部分,Void IDE的开发目前处于暂停状态。团队将精力转向了探索“一些新颖的编码想法”,追求创新而非与现有产品的功能对齐。这是一个非常现实且常见于开源项目,尤其是雄心勃勃的初创项目的状态。

6.1 当前状态解读与使用建议

状态:代码仓库可用,可以编译、运行和探索。但核心功能可能不完整,某些高级AI特性或UI交互可能存在Bug。官方不会主动修复问题或合并新功能请求(PR),但会通过邮件回复关于构建和维护自身版本的问题。

给开发者的建议

  1. 用于学习和研究:这是当前使用Void最主要的价值。你可以通过阅读其源码,深入理解一个现代化的、AI原生的IDE是如何架构的,特别是如何将AI能力无缝嵌入到编辑、浏览、调试等核心工作流中。
  2. 作为二次开发的起点:如果你所在团队或社区需要一个高度定制化的内部AI编程工具,Void的代码库是一个绝佳的起点。你可以在其基础上,集成自己训练的领域特定模型,或者开发符合内部规范的特殊智能体。
  3. 谨慎用于生产:由于维护暂停,随着底层依赖(如Electron、Node.js、VSCode上游)的更新,Void可能会逐渐出现兼容性问题,某些功能也可能失效。不建议将其作为团队主力开发工具,除非你有能力持续维护自己的分支。

6.2 开发中可能遇到的典型问题与排查

即使只是构建和运行,你也可能会遇到一些挑战。以下是一些常见问题及解决思路:

问题现象可能原因排查与解决思路
yarn安装依赖失败,网络超时网络连接问题,或某些包需要特定环境1. 检查网络,可尝试使用国内镜像源配置npm/yarn。
2. 确保Python、C++编译工具已正确安装。
3. 清除缓存yarn cache clean后重试。
yarn watch编译时报 TypeScript 错误依赖版本不匹配,或代码本身存在类型问题1. 确保使用项目要求的Node.js版本。
2. 查看具体错误信息,可能是某个类型定义文件缺失,尝试yarn install --force
3. 如果是暂停开发导致的遗留问题,可能需要根据错误提示注释掉或简单修复类型定义。
yarn electron启动后白屏或崩溃原生模块编译失败,或运行时资源加载错误1. 检查终端错误输出,看是否有明显的原生模块(如keytar,spdlog)编译错误。
2. 尝试完全重新构建原生模块:yarn rebuild-native(如果脚本存在)。
3. 删除node_modulesout目录,从头开始yarnyarn watch
AI聊天功能无法使用,模型列表为空AI提供商配置缺失,或相关服务未启动1. 检查设置中是否已配置有效的AI提供商(如OpenAI API Key)。
2. 如果使用本地模型,确认Ollama等服务已启动并在指定端口监听。
3. 在开发者工具(F12)中查看网络请求和主进程日志,确认API调用是否成功。
检查点功能点击无反应相关后端服务未正常初始化1. 同样查看开发者工具的控制台输出,寻找JavaScript错误。
2. 检查与检查点相关的服务是否在启动时被正确注册和激活。

6.3 对未来的个人展望

虽然Void IDE的开发暂停了,但其代表的“开源、可定制、隐私优先的AI编程环境”这一方向,我认为具有长久的生命力。未来的编程工具,必然会更深地与AI融合,但融合的方式不应是黑盒。

我个人期待社区能出现更多基于Void理念的项目。也许未来的形态不是一个庞大的、一体化的IDE,而是一套模块化的协议和插件标准。核心编辑器(可以是VSCode、Neovim或任何其他编辑器)提供稳定的文本编辑和扩展平台,而AI能力则由一系列遵循统一协议的独立智能体插件提供。这些插件可以自由组合、替换,它们管理自己的上下文、调用不同的模型、提供特定的代码操作(如“重构此函数为异步”、“为这段代码生成单元测试”)。

Void的这次探索,无论其最终是否以IDE的形式回归,都为这个未来图景贡献了宝贵的实践经验和代码资产。对于开发者而言,深入其中,理解其设计得失,本身就是一次宝贵的学习旅程。至少,下一次当你使用某个AI编程助手时,你会更清楚它背后可能发生了什么,以及你真正想要的控制权在哪里。

http://www.jsqmd.com/news/739095/

相关文章:

  • 中兴光猫解锁终极指南:5分钟获取完整root权限的完整教程
  • 基于MCP协议构建智能文件管理工具:从原理到实践
  • 2026压力传感器怎么选?哪个品牌靠谱首选广东犸力 - 速递信息
  • 通过 Taotoken 控制台清晰追踪每个开发项目的 API 调用量与费用消耗
  • AI编程工具集成营销技能:Claude Code Marketing Skills实战指南
  • 工业电源模块选型参考:钡特电源 AS03-23S05 与 LS03-13B05R3 封装兼容解析
  • 2026压力传感器选哪家靠谱?广东犸力稳居行业前列 - 速递信息
  • 在微服务架构中集成 Taotoken 实现各服务模块的灵活 AI 能力调用
  • 第24集:跨云多活架构!AIOps 平台的容灾与故障切换实战
  • 终极指南:WeChatFerry微信自动化框架完整使用教程
  • World999_Labs-Proof-Layer:构建可验证计算的证明层中间件
  • 手把手调试LIN总线:用示波器抓取Break、Sync和PID,快速定位通信故障
  • QRCode 核心知识汇总
  • 如何免费获取Grammarly Premium高级版Cookie:终极自动化解决方案
  • 2026-05-01-01-行业热点-2026年5月数字孪生行业展望三大厂商战略布局深度解析
  • 去水印不破坏原图的方法有哪些?2026实测去水印工具推荐 - 科技热点发布
  • 基于MCP协议构建Google Workspace AI助手:从原理到企业级部署
  • 一台电脑,多人同乐:Nucleus Co-Op 让单机游戏变身派对神器
  • FPGA时序优化小技巧:为什么你的状态机输出要加个寄存器?
  • 2026年4月市面上评价好的防锈膜公司推荐,气相防锈剂/VCI气相防锈膜/气相防锈膜/防锈纸,防锈膜源头厂家推荐 - 品牌推荐师
  • 上海市BIM技术协会:2025上海市第二届数建杯数字城市建设成果赛BIM获奖作品成果汇编
  • 农业物联网数据孤岛终结者:Python实现跨厂商设备语义互操作(OWL本体建模+SPARQL实时融合查询)
  • 无需第三方应用!安卓系统自带功能免费创建PDF,扫描敏感文件需谨慎
  • CCC数字车钥匙UWB测距实战:手把手教你配置MAC时间网格参数(含避坑指南)
  • 快手保存的视频怎么去水印?官方方法+2026实测去水印工具全盘点 - 科技热点发布
  • RimSort:从模组下载失败到流畅管理的完整解决方案
  • 3分钟学会B站缓存视频转换:m4s-converter完整使用教程
  • 暗黑破坏神2存档编辑解决方案:d2s-editor深度解析与实践指南
  • 抖音不能下载的视频怎么保存到相册?无法保存视频的原因分析与2026实测保存方法盘点 - 科技热点发布
  • 科研党必备:除了知云,这些免费OCR工具也能救活你的‘图片PDF’(附Abbyy对比)