强大功能,尽在指尖
AI 助手、一键打包 APK、安卓能力桥接,让移动 Web 开发与发布一步到位
AI 助手
内置 AI 助手,能理解你的项目,帮你生成、搜索定位并精确修改代码,移动端也能智能高效地开发。
一键打包 APK
把网页项目直接打包成可安装的安卓 App,自定义包名、图标、权限与版本号,全程在手机本地完成,无需电脑。
CatNative 原生能力
CatNative 是 WebCatX 自研的安卓交互框架。用它制作或打包的 App,页面能像真正的手机软件一样操作手机:读写文件、保存相册与下载、拍照选图、一键分享、申请权限、查看已装应用等,网页从此不再只是网页。
语法高亮
支持 HTML、CSS、JavaScript 等多种语言的语法高亮,让代码结构清晰易读。
实时预览
编辑代码的同时实时预览效果,支持多设备视图模拟,所见即所得。
共享空间
支持上传源码分享给其他用户,或下载其他用户分享的源码资源。
界面预览
直观简洁的界面设计,让移动编码更加高效
代码编辑界面
专业的代码编辑环境,支持语法高亮和自动缩进
项目管理
轻松管理多个项目
共享空间
一键导入其他用户共享的源码
用户怎么说
来自真实用户的使用体验和评价
"作为一名前端开发者,我经常需要在外出时快速修改代码。WebCat 让我能够在手机上高效工作,实时预览功能非常实用!"
李明
资深前端工程师
"学生党表示非常好用!在课堂上听老师讲课时,可以直接用手机编写示例代码,云同步功能让我回家后能在电脑上继续学习。"
张晓华
计算机专业学生
"作为自由职业者,我需要随时随地响应客户需求。WebCat 的一键导入共享源码功能让我能快速向客户展示效果,大大提高了工作效率。"
王建国
全栈自由开发者
常见问题
关于 WebCat 的一些常见疑问解答
立即开始移动编码之旅
下载 WebCat,随时随地编写、预览和分享你的代码,让创意不再受设备限制。
CatNative API 文档
CatNative 是 WebCatX 自研的安卓原生桥接框架:项目页面通过全局对象 window.webcat 直接调用安卓能力,预览与打包 APK 均支持,无需引入任何库。以下是 window.webcat 的完整 API 参考。
同步方法直接返回对象;异步方法返回 Promise,用
await调用。失败时:同步返回{ok:false,error},异步 Reject(Error.message)。
window.webcat由原生在页面开始 / 完成时异步注入,可能晚于load事件;建议在 DOMContentLoaded / load 之后再调用,若此时仍取不到,请重试或轮询 window.webcat 后再调用。
webcat.fs —— 私有文件(应用私有目录,两端一致)
| 接口 | 类型 | 入参 | 出参 |
|---|---|---|---|
fs.readFile 读取文件 | 异步 | path, encoding?('utf8'/'base64') | {ok, content} |
fs.writeFile 写入文件 | 异步 | path, content, encoding? | {ok, size}(自动建父目录) |
fs.list 列出目录 | 异步 | dir? | {ok, items:[{name, isDir, size, modified}]} |
fs.delete 删除 | 异步 | path | {ok}(文件 / 空目录) |
fs.mkdir 创建目录 | 同步 | dir | {ok} |
fs.exists 判断存在 | 同步 | path | {ok, exists, isDir} |
fs.stat 文件信息 | 同步 | path | {ok, size, isDir, modified} |
fs.readAsset 读取项目文件 | 异步 | path(项目内相对) | {ok, content}(打包后只读) |
fs.openFile 系统打开文件 | 同步 | path | {ok} |
fs.zip 压缩 | 异步 | source, targetZip(.zip) | {ok, size} |
fs.unzip 解压 | 异步 | zipPath, outDir | {ok, count} |
fs.move 移动 / 重命名 | 异步 | source, target | {ok} |
fs.copy 复制 | 异步 | source, target | {ok} |
webcat.ext —— 公开文件(target = "root" 全授权 / SAF handle)
| 接口 | 类型 | 入参 | 出参 |
|---|---|---|---|
ext.isFullAccess 是否全授权 | 同步 | — | {ok, granted} |
ext.requestFullAccess 开启全授权 | 同步 | — | {ok}(跳本应用设置) |
ext.pickDirectory 选择目录 | 异步 | — | {ok, handle}(授权持久化) |
ext.revoke 撤销授权 | 同步 | handle | {ok} |
ext.list 列出 | 异步 | target, path? | {ok, items} |
ext.readFile 读取 | 异步 | target, path, encoding? | {ok, content} |
ext.writeFile 写入 | 异步 | target, path, content, encoding? | {ok, size} |
ext.delete 删除 | 异步 | target, path | {ok} |
ext.mkdir 建目录 | 同步 | target, path | {ok} |
ext.exists 存在 | 同步 | target, path | {ok, exists, isDir} |
ext.stat 信息 | 同步 | target, path | {ok, size, isDir, modified} |
ext.openFile 系统打开 | 同步 | target, path | {ok} |
ext.zip 压缩 | 异步 | target, source, targetZip | {ok, size} |
ext.unzip 解压 | 异步 | target, zipPath, outDir | {ok, count} |
ext.move 移动 | 异步 | target, source, dst | {ok} |
ext.copy 复制 | 异步 | target, source, dst | {ok} |
target 取 "root"(需“所有文件访问”,path 为绝对路径)或 SAF handle(path 相对授权目录)。
webcat.media —— 媒体保存 / 选择
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
media.saveImage 存相册 | 异步 | name, data, path? | {ok, uri?, path?},默认存 Pictures |
media.saveToDownload 存下载 | 异步 | name, data, path? | {ok, uri?, path?},默认存 Download |
media.save 自动 | 异步 | name, data, path? | 按扩展名自动:图片 → 相册,其它 → Download |
media.pickImage 选图 | 异步 | format?, maxBytes? | {ok, name, mime, size, base64 | uri | path} |
media.pickFile 选文件 | 异步 | mime?, format?, maxBytes? | 同上,mime 默认 */*
|
data支持:裸 base64 /data:image/png;base64,.../ http(s) URL(自动下载)。path:省略 = 默认集合;非/开头 = 该集合下子目录(如 myapp/cards);/开头 = 绝对路径写文件(需全盘权限)。format默认 base64;uri 返回 content uri;path 尽力解析本地绝对路径,失败会提示改用 uri / base64。maxBytes默认 15MB(仅 base64 上限),超限请改用format=uri。
webcat.camera —— 相机
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
camera.takePhoto | 异步 | save? | {ok, name, size, base64};调系统相机(免 CAMERA 权限),save=true 同时存相册 |
webcat.audio —— 音频播放
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
audio.play | 异步 | {url | source, path, volume?, seek?} | 播完 resolve {ok, end:true};本地支持 fs / root / SAF,或 http(s) |
audio.stop / pause / resume | 异步 | — | {ok} |
audio.setVolume | 异步 | volume(0~1) | {ok} |
audio.seek | 异步 | position(ms) | {ok} |
audio.isSupported | 同步 | — | {ok, supported} |
webcat.tts —— 文字转语音(无需权限)
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
tts.isSupported | 同步 | — | {ok, supported, speaking} |
tts.speak | 异步 | text, {lang?, rate?, pitch?, volume?, queue?} | 读完 resolve {ok, end:true};lang 如 "zh-CN";rate / pitch 默认 1;volume 0~1;queue=true 追加不打断
|
tts.stop | 异步 | — | {ok} 停止并结束当前朗读 |
tts.pause | 异步 | — | {ok} 暂停(系统 TTS 无原生暂停,等价停止) |
tts.resume | 异步 | — | {ok} 从头继续最近一次内容 |
webcat.record —— 录音(需打包勾选“录音 / 麦克风”)
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
record.start | 异步 | {source:'fs'/'root'/handle, path?, format?:'aac'|'amr'} | {ok, started, path};缺省 fs records/rec_时间戳.*
|
record.stop | 异步 | — | {ok, path, size, durationMs, format} |
record.cancel | 异步 | — | {ok} 丢弃 |
record.state | 同步 | — | {ok, recording} |
webcat.scan —— 扫码(需打包勾选“相机”)
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
scan.scan | 异步 | — | {ok, text, format}(实时扫码;预览返回引导) |
scan.decodeImage | 异步 | {uri | base64 | source, path} | {ok, text} |
scan.generate | 同步 | text, size? | {ok, base64} 生成二维码 PNG |
webcat.clipboard —— 剪切板(同步)
| 接口 | 类型 | 入参 | 出参 |
|---|---|---|---|
clipboard.get 读取剪切板 | 同步 | — | {ok, text} |
clipboard.set 写入剪切板 | 同步 | text | {ok} |
webcat.share —— 分享
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
share.text 分享文本 | 同步 | text, title? | 系统分享面板 |
share.file 分享文件 | 同步 | source('fs'/'root'/handle), path, mime? | fs / root 走 FileProvider |
webcat.app —— 应用
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
app.info 应用信息 | 同步 | — | {ok, packageName, versionName, versionCode, appName} |
app.quit 退出应用 | 同步 | — | {ok} |
app.openFile 系统打开文件 | 同步 | source('fs'/'root'/handle), path | {ok} |
app.install 安装 | 同步 | source, path | 需允许未知来源;SAF 先复制到 cache |
app.openAppSettings | 同步 | — | 跳本应用设置 |
app.isInstalled | 同步 | packageName | {ok, installed, appName?, versionName?, limited}(limited = 未勾选“应用列表”,可能误报未安装) |
app.listApps | 异步 | — | 需打包勾选 QUERY_ALL_PACKAGES;{ok, apps:[{packageName, appName, versionName, versionCode, isSystem}]} |
app.launchPackage | 同步 | packageName | {ok, launched};按包名打开(勾选“应用列表”更准) |
app.openUri | 同步 | uri | {ok, launched};任意 scheme / url |
webcat.permission —— 权限
| 接口 | 类型 | 入参 | 出参 |
|---|---|---|---|
permission.check 检查权限 | 同步 | perms: string[] | {ok, granted[], denied[], undeclared[]} |
permission.request 申请权限 | 异步 | perms: string[] | {ok, granted[], denied[], undeclared[]} |
undeclared = 未声明权限(打包时未勾选 / 不支持)→ 应提示“打包时勾选”或“系统授予”。特殊权限 MANAGE 走 ext.requestFullAccess。
webcat.sys —— 系统信息(同步)
| 接口 | 类型 | 入参 | 出参 |
|---|---|---|---|
sys.info 系统信息 | 同步 | — | {ok, brand, model, androidVersion, sdkInt, screenWidth, screenHeight, density} |
sys.network 网络状态 | 同步 | — | {ok, online, type: wifi / mobile / none} |
sys.battery 电池 | 同步 | — | {ok, level, charging} |
webcat.screen —— 屏幕(同步)
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
screen.keepOn 屏幕常亮 | 同步 | on | 窗口 FLAG_KEEP_SCREEN_ON 开 / 关
|
screen.setOrientation | 同步 | mode | auto / portrait / landscape / sensor |
webcat.phone —— 电话短信(同步,免权限跳系统)
| 接口 | 类型 | 入参 | 出参 |
|---|---|---|---|
phone.dial 拨号 | 同步 | number | {ok} |
phone.sms 发短信 | 同步 | number, text? | {ok} |
webcat.qq —— QQ 跳转(同步)
| 接口 | 类型 | 入参 | 出参 |
|---|---|---|---|
qq.openProfile 打开 QQ 名片 | 同步 | qq: QQ号 | {ok, launched} |
qq.openGroup 打开 QQ 群 | 同步 | qq: 群号 | {ok, launched} |
webcat.browser —— 系统浏览器(同步)
| 接口 | 类型 | 入参 | 出参 |
|---|---|---|---|
browser.open 用浏览器打开网页 | 同步 | url | {ok} |
webcat.contacts —— 通讯录
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
contacts.list 读取通讯录 | 异步 | — | {ok, contacts:[{name, phones:[]}]}(需 READ_CONTACTS,打包勾选) |
webcat.location —— 定位
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
location.get 获取定位 | 异步 | — | {ok, latitude, longitude, accuracy, provider}(需定位权限,打包勾选) |
webcat.download —— 下载
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
download.download 下载文件 | 异步 | url, source('fs'/'root'/handle), path | {ok, path, size} |
download.stream 流式下载 | 异步 | {url, source, path, headers?, onProgress} | {ok, path, size};带进度(中间帧 {progress:{loaded, total}}) |
webcat.network —— 原生网络(无 CORS 限制)
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
network.request | 异步 | {method, url, headers, body, timeout?, returnBase64?} | {ok, status, body};body 支持 base64: 前缀
|
network.upload | 异步 | {url, method?, headers?, fieldName?, files:[{source:'fs'/'root'/handle, path, name?}], fields?, onProgress} | {ok, status, body};multipart,已知路径原生直传 |
webcat.page —— 页面窗口
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
page.open | 同步 | {url | path, params?} | {ok};打开新页面窗口(path 相对当前网页根,或 http(s) / file 绝对路径) |
page.params | 同步 | — | 本页收到的参数 |
page.end | 同步 | — | 关闭当前页面 |
webcat.store —— 会话共享(同步,仅内存,App 退出即清)
| 接口 | 类型 | 入参 | 出参 |
|---|---|---|---|
store.set | 同步 | key, value | {ok} |
store.get | 同步 | key | {ok, value} |
store.remove | 同步 | key | {ok} |
store.keys | 同步 | — | {ok, keys[]} |
webcat.statusBar —— 状态栏
默认状态下,状态栏 / 底部导航栏由原生自动避让,普通页面无需任何调用,也不要加 padding(会重复留白)。
statusBar.setAdaptive(true)沉浸时原生不再避让、状态栏覆盖页面;仅当顶部有需要完整显示的内容(标题 / 按钮等)才加 padding,且height是设备 px,CSS 要用height / density。
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
statusBar.setColor | 同步 | color, darkIcon? | 自定义状态栏颜色(#RRGGBB / #AARRGGBB 或整数),导航栏同色;darkIcon 省略时按亮度自动判断 |
statusBar.setDarkIcon | 同步 | darkIcon | true = 深色图标,false = 浅色图标 |
statusBar.hide | 同步 | — | 隐藏状态栏(导航栏保留;焦点变化后保持隐藏) |
statusBar.show | 同步 | — | 显示状态栏 |
statusBar.setAdaptive | 同步 | enabled | true = 沉浸(原生不再避让),false = 恢复自动避让(默认)。别名 setOverlay
|
statusBar.height | 同步 | — | {ok, height, density};height 为设备 px(非 dp),CSS 用 height / density |
webcat.statusBar.setAdaptive(true);
const { height, density } = webcat.statusBar.height();
document.querySelector(".app-header").style.paddingTop = (height / density) + "px";
setAdaptive 作用于整个预览 / 打包窗口(含原生顶栏);导航栏颜色始终跟随状态栏。
webcat.ui —— 提示(同步)
| 接口 | 类型 | 入参 | 出参 / 说明 |
|---|---|---|---|
ui.toast 提示 | 同步 | msg, long? | {ok} |
ui.vibrate 震动 | 同步 | ms | {ok};需打包勾选“震动”(VIBRATE) |
安全限制
- 网页不可访问 WebCatX 自身目录(
/WebCatX/project、/WebCatX/cache):ext.list命中返回空列表,其余读 / 写 / 删 / 开 / 分享命中返回“路径受保护,不允许网页访问 WebCatX 目录”;SAF 授权目录不受限。
调用示例
// 私有文件
await webcat.fs.writeFile("data.json", JSON.stringify({a:1}));
const { content } = await webcat.fs.readFile("data.json");
// 压缩 / 解压(私有)
await webcat.fs.zip("js", "backup.zip");
await webcat.fs.unzip("backup.zip", "restore");
// 剪切板 / 系统信息(同步)
const { text } = webcat.clipboard.get();
const info = webcat.sys.info();
// 权限(异步)
const r = await webcat.permission.request(["android.permission.CAMERA"]);
if (r.undeclared.length) { webcat.ui.toast("该权限未在打包时勾选"); }
// 文字转语音
await webcat.tts.speak("你好,这是 WebCatX", { lang: "zh-CN" });
// 外部跳转
webcat.qq.openProfile("123456");
webcat.browser.open("https://example.com");
// 下载到私有目录
await webcat.download.download("https://x.com/a.png", "fs", "img/a.png");
// 通讯录 / 定位
const { contacts } = await webcat.contacts.list();
const loc = await webcat.location.get();
// 公开目录
if (webcat.ext.isFullAccess().granted) {
await webcat.ext.writeFile("root", "/storage/emulated/0/Download/a.txt", "hi");
} else {
webcat.ext.requestFullAccess();
}
const { handle } = await webcat.ext.pickDirectory();
await webcat.ext.writeFile(handle, "data.json", "{}");