# Scripting 文档站点项目
## 项目介绍
这是 **Scripting App** 的官方文档网站生成器项目。Scripting 是一款 iOS 应用程序,允许开发者使用 **TypeScript/TSX**(类 React 语法)编写 UI 组件和脚本,这些代码会被编译为原生 iOS 界面(基于 SwiftUI)。该项目基于 Rspress 静态站点生成器,从结构化的 JSON 配置和 Markdown 文件自动生成中英文双语文档网站,为 Scripting 平台提供完整的 API 参考、教程指南和版本更新日志。
项目的核心功能包括:通过自动化脚本从 `Scripting Documentation` 资源包读取文档配置,生成 325+ 个 API 文档页面;支持中英文双语切换和搜索;提供 RSS 订阅源用于更新日志;自动部署到 GitHub Pages。文档涵盖了 Scripting 的所有功能模块,包括交互式组件(Widget、Live Activity、Control Widget)、设备能力访问(蓝牙、健康数据、位置、相机等 25+ 个 API)、50+ 个 UI 视图组件、以及 AI 助手集成等高级特性。
## 核心 API 和功能
### 文档自动生成脚本
自动从 JSON 配置文件生成完整的双语文档站点
```javascript
// scripts/docs.js - 文档生成器核心逻辑
import fs from "fs";
import path from "path";
const resourcePath = path.join(__dirname, "..", "Scripting Documentation");
const docsPath = path.join(__dirname, "..", "docs");
// 处理文档项,支持文件夹、README 和示例代码
const processDocItem = (item, parentPath = "", language = "en") => {
const { pro, title, subtitle, keywords, example, readme, children } = item;
const folderName = title.en;
const basePath = path.join(docsPath, language, "guide", "doc_v2", parentPath, folderName);
// 生成 _meta.json 导航配置
const metaPath = path.join(docsPath, language, "guide", "doc_v2", parentPath, "_meta.json");
if (children && children.length > 0) {
// 递归处理子项
children.forEach(child =>
processDocItem(child, path.join(parentPath, folderName), language)
);
} else {
if (readme) {
// 读取 Markdown 文档
const readmePath = path.join(resourcePath, readme, language + ".md");
const readmeContent = fs.readFileSync(readmePath, "utf-8");
const readmeMd = `# ${title[language]}\n\n${readmeContent}`;
writeFile(basePath + ".mdx", readmeMd);
}
if (example) {
// 读取 TSX 示例代码
const tsxContent = fs.readFileSync(path.join(resourcePath, example + ".tsx"), "utf-8");
const exampleMd = `# ${language === "en" ? "Example" : "示例"}\n\n\`\`\`tsx\n${tsxContent}\n\`\`\``;
writeFile(path.join(basePath, exampleName + ".mdx"), exampleMd);
}
}
};
// 处理双语版本
const processLanguages = (docItem) => {
processDocItem(docItem, "", "en");
processDocItem(docItem, "", "zh");
};
// 主执行流程
const processDocs = () => {
const docJson = JSON.parse(fs.readFileSync(path.join(resourcePath, "doc.json"), "utf-8"));
docJson.forEach(item => processLanguages(item));
};
processDocs();
// 运行命令:
// bun run generate:docs
// 清空现有文档目录并重新生成所有 325+ 个文档文件
```
### Rspress 站点配置
配置双语文档站点、RSS 订阅和 GitHub Pages 部署
```typescript
// rspress.config.ts - 站点核心配置
import { pluginRss } from "@rspress/plugin-rss";
import { defineConfig } from "rspress/config";
import ghPages from "rspress-plugin-gh-pages";
const siteUrl = "https://scriptingapp.github.io";
export default defineConfig({
title: "Scripting",
icon: "/icon.png",
logo: "/logo.png",
logoText: "Scripting",
// 路由配置
route: {
cleanUrls: true,
extensions: [".md", ".mdx"],
},
// 搜索配置 - 支持代码块搜索
search: {
codeBlocks: true,
},
// Markdown 配置
markdown: {
showLineNumbers: true,
defaultWrapCode: false,
},
// 插件配置
plugins: [
// GitHub Pages 自动部署
ghPages({
repo: "https://github.com/ScriptingApp/ScriptingApp.github.io.git",
branch: "deploy",
}),
// RSS 订阅源 - 中英文更新日志
pluginRss({
siteUrl: siteUrl,
feed: [
{
id: "changelog",
test: "/guide/changelog/",
title: "Scripting Changelog",
language: "en-US",
},
{
id: "changelog_zh",
test: "/zh/guide/changelog/",
title: "Scripting 更新日志",
language: "zh-CN",
},
],
output: {
dir: "feeds",
type: "rss",
},
}),
],
// 主题配置 - 双语支持
themeConfig: {
locales: [
{
lang: "en",
label: "English",
searchPlaceholderText: "Search",
searchNoResultsText: "No results for",
},
{
lang: "zh",
label: "简体中文",
searchPlaceholderText: "搜索",
searchNoResultsText: "没有搜索结果",
},
],
// 社交链接
socialLinks: [
{
icon: "github",
mode: "link",
content: "https://github.com/ScriptingApp",
},
{
icon: "X",
mode: "link",
content: "https://x.com/thomfang",
},
// RSS 订阅链接(英文和中文)
],
},
// 语言配置
lang: "en",
locales: [
{
lang: "en",
label: "English",
title: "Scripting",
description: "Build native iOS apps with TypeScript",
},
{
lang: "zh",
label: "简体中文",
title: "Scripting",
description: "使用 TypeScript 构建原生 iOS 应用",
},
],
});
// 构建命令:
// bun run build (生产构建,分配 16GB 内存)
// bun run dev (开发服务器)
```
### NPM 脚本和依赖管理
项目构建、开发和文档生成的完整工作流
```json
{
"name": "scripting-doc",
"version": "1.0.0",
"private": true,
"scripts": {
"setup": "bun install",
"dev": "rspress dev",
"build": "NODE_OPTIONS='--max-old-space-size=16384' rspress build",
"build:fun": "NODE_OPTIONS='--max-old-space-size=16384' rspress build -c rspress.config.fun.ts",
"generate:docs": "rm -rf docs/en/guide/doc_v2/* && rm -rf docs/zh/guide/doc_v2/* && bun scripts/docs.js"
},
"dependencies": {
"rspress": "^1.45.6"
},
"devDependencies": {
"@rspress/plugin-rss": "^1.45.6",
"@types/node": "^24.7.2",
"rspress-plugin-gh-pages": "^0.3.0"
}
}
// 常用命令:
// bun run setup - 安装所有依赖
// bun run generate:docs - 从源文件重新生成所有文档
// bun run dev - 启动开发服务器(热重载)
// bun run build - 构建生产版本(输出到 doc_build/)
```
### GitHub Actions 自动部署
持续集成和自动部署到 GitHub Pages
```yaml
# .github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install dependencies
run: bun install
- name: Build with Rspress
run: NODE_OPTIONS='--max-old-space-size=16384' bun run build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./doc_build
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
# 工作流说明:
# 1. 当代码推送到 main 分支时自动触发
# 2. 使用 Bun 作为包管理器进行构建
# 3. 分配 16GB 内存进行大规模文档构建
# 4. 构建产物上传到 GitHub Pages
# 5. 自动部署到 https://scriptingapp.github.io
```
### Script API - 脚本运行时和元数据
管理脚本执行环境、生命周期和 URL Scheme
```typescript
// Script 模块示例 - docs/en/guide/doc_v2/Script.mdx
// 1. 获取脚本运行环境
if (Script.env === "widget") {
// 在 Home Screen Widget 中运行(入口: widget.tsx)
Widget.present();
} else if (Script.env === "index") {
// 在主应用中运行(入口: index.tsx)
Navigation.present({ element: });
} else if (Script.env === "control_widget") {
// 在 Control Center 控件中运行(iOS 18+)
ControlWidget.present();
} else if (Script.env === "notification") {
// 在通知扩展中运行(入口: notification.tsx)
Notification.present();
} else if (Script.env === "app_intents") {
// 在 AppIntents 扩展中运行(入口: app_intents.tsx)
// 处理 Widget/LiveActivity 的交互逻辑
} else if (Script.env === "keyboard") {
// 在自定义键盘中运行(入口: keyboard.tsx)
CustomKeyboard.present();
}
// 2. 读取脚本元数据
console.log(Script.name); // "MyScript"
console.log(Script.directory); // "/private/var/mobile/..."
console.log(Script.metadata.version); // "1.2.0"
console.log(Script.metadata.localizedName); // "天气助手"
console.log(Script.metadata.author.name); // "John Doe"
// 3. 处理 URL Scheme 参数
// URL: scripting://run/MyScript?user=John&id=123
console.log(Script.queryParameters.user); // "John"
console.log(Script.queryParameters.id); // "123"
// 4. 运行其他脚本并获取返回值
const result = await Script.run({
name: "ProcessData",
queryParameters: { input: "abc" },
singleMode: true // 确保只有一个实例运行
});
console.log(result); // 从 Script.exit(result) 返回的数据
// 5. 终止脚本并返回结果
Script.exit("Done");
// 或返回结构化数据给 Shortcuts
Script.exit(Intent.json({
status: "ok",
data: processedData
}));
// 6. 创建 URL Schemes
// 运行脚本
const runUrl = Script.createRunURLScheme("MyScript", { user: "Alice" });
// "scripting://run/MyScript?user=Alice"
// 单实例运行
const singleUrl = Script.createRunSingleURLScheme("MyScript", { id: "1" });
// "scripting://run_single/MyScript?id=1"
// 打开脚本编辑器
const openUrl = Script.createOpenURLScheme("MyScript");
// "scripting://open/MyScript"
// 打开文档页面
const docUrl = Script.createDocumentationURLScheme("Widgets");
// "scripting://doc?title=Widgets"
// 导入脚本
const importUrl = Script.createImportScriptsURLScheme([
"https://github.com/schl3ck/scripting-app-lib",
"https://example.com/my-script.zip",
]);
// "scripting://import_scripts?urls=..."
// 7. 检查 PRO 功能访问权限
if (Script.hasFullAccess()) {
// 使用 Scripting PRO 功能
await Assistant.requestStructuredData(prompt, schema);
}
```
### AppIntent - 交互式 Widget 和控件
注册和管理 Widget、Live Activity 和 Control Widget 的交互逻辑
```typescript
// app_intents.tsx - AppIntent 注册文件
// 1. 注册一个简单的 AppIntent
export const RefreshDataIntent = AppIntentManager.register({
name: "RefreshDataIntent",
protocol: AppIntentProtocol.AppIntent, // 通用操作
perform: async () => {
// 执行数据刷新逻辑
const newData = await fetchDataFromAPI();
await Storage.set("cachedData", JSON.stringify(newData));
// 刷新所有 Widget
Widget.reloadAll();
}
});
// 2. 注册带参数的 Toggle Intent
export const ToggleDoorIntent = AppIntentManager.register<{
id: string;
newState: boolean;
}>({
name: "ToggleDoorIntent",
protocol: AppIntentProtocol.AppIntent,
perform: async ({ id, newState }) => {
// 调用智能家居 API 切换门状态
await fetch(`https://api.smarthome.com/doors/${id}`, {
method: "POST",
body: JSON.stringify({ open: newState }),
headers: { "Content-Type": "application/json" }
});
// 更新本地状态
await Storage.set(`door_${id}_state`, newState.toString());
// 刷新 Control Widget 的 Toggle 状态
ControlWidget.reloadToggles();
}
});
// 3. 注册音频播放 Intent(用于媒体控制)
export const PlayPauseIntent = AppIntentManager.register<{
action: "play" | "pause";
}>({
name: "PlayPauseIntent",
protocol: AppIntentProtocol.AudioPlaybackIntent, // 音频播放协议
perform: async ({ action }) => {
if (action === "play") {
await AudioPlayer.play();
} else {
await AudioPlayer.pause();
}
// 更新 Live Activity
await LiveActivity.update({
activityId: "music_player",
content: { isPlaying: action === "play" }
});
}
});
// 4. 注册音频录制 Intent(iOS 18+,需要 Live Activity)
export const RecordAudioIntent = AppIntentManager.register<{
action: "start" | "stop";
}>({
name: "RecordAudioIntent",
protocol: AppIntentProtocol.AudioRecordingIntent, // 音频录制协议
perform: async ({ action }) => {
if (action === "start") {
// 必须先启动 Live Activity
await LiveActivity.start({
activityId: "audio_recording",
content: { isRecording: true, duration: 0 }
});
await AudioRecorder.startRecording();
} else {
await AudioRecorder.stopRecording();
// 结束 Live Activity
await LiveActivity.end({ activityId: "audio_recording" });
}
}
});
// 5. 在 Widget 中使用 AppIntent (widget.tsx)
Widget.present(
智能家居控制
{/* 简单按钮 */}
{/* 带参数的 Toggle */}
);
// 6. 在 Control Widget 中使用 (control_widget_toggle.tsx)
ControlWidget.present(
);
// 7. 在 Live Activity 中使用
LiveActivity.present({
activityId: "music_player",
content: {
正在播放: {songTitle}
}
});
```
### Assistant - AI 助手集成 (PRO)
使用多种 AI 提供商从自然语言提取结构化数据
```typescript
// Assistant API 示例 - 需要 Scripting PRO
// 1. 检查可用性
if (!Assistant.isAvailable) {
console.log("请配置 API Key 以使用 Assistant 功能");
Script.exit();
}
// 2. 定义 JSON Schema
const billSchema = {
type: "object",
properties: {
totalAmount: {
type: "number",
required: true,
description: "账单总金额"
},
category: {
type: "string",
required: true,
description: "账单类别(如:停车、餐饮、交通)"
},
date: {
type: "string",
required: false,
description: "账单日期(ISO 格式)"
},
location: {
type: "string",
required: false,
description: "消费地点"
},
items: {
type: "array",
required: false,
description: "账单项目明细",
items: {
type: "object",
properties: {
name: { type: "string", description: "项目名称" },
price: { type: "number", description: "项目价格" },
quantity: { type: "number", description: "数量" }
}
}
}
}
};
// 3. 解析账单文本
const billText = `
停车场收据
日期: 2024-03-11 14:30
地点: 城市中心停车场
停车费: $15.00
类别: 停车
`;
try {
const parsedBill = await Assistant.requestStructuredData(
`请解析以下账单信息并提取结构化数据:\n${billText}`,
billSchema,
{
provider: "openai", // 可选: openai, gemini, anthropic, deepseek
modelId: "gpt-4-turbo" // 可选,不指定则使用默认模型
}
);
console.log("解析结果:", JSON.stringify(parsedBill, null, 2));
/*
{
"totalAmount": 15.00,
"category": "停车",
"date": "2024-03-11T14:30:00",
"location": "城市中心停车场"
}
*/
// 4. 保存到数据库或存储
await Storage.set("last_bill", JSON.stringify(parsedBill));
} catch (error) {
console.error("解析失败:", error);
}
// 5. 使用其他 AI 提供商
// Gemini
const geminiResult = await Assistant.requestStructuredData(
prompt,
schema,
{ provider: "gemini", modelId: "gemini-1.5-pro" }
);
// Anthropic (Claude)
const claudeResult = await Assistant.requestStructuredData(
prompt,
schema,
{ provider: "anthropic", modelId: "claude-3-opus-20240229" }
);
// DeepSeek
const deepseekResult = await Assistant.requestStructuredData(
prompt,
schema,
{ provider: "deepseek", modelId: "deepseek-chat" }
);
// 自定义提供商
const customResult = await Assistant.requestStructuredData(
prompt,
schema,
{ provider: { custom: "my-api-provider" } }
);
// 6. 复杂数据提取示例 - 从邮件提取事件信息
const emailSchema = {
type: "object",
properties: {
subject: { type: "string", required: true, description: "邮件主题" },
events: {
type: "array",
required: true,
description: "提取的事件列表",
items: {
type: "object",
properties: {
title: { type: "string", description: "事件标题" },
startDate: { type: "string", description: "开始时间(ISO)" },
endDate: { type: "string", description: "结束时间(ISO)" },
location: { type: "string", description: "地点" },
attendees: {
type: "array",
description: "参与者列表",
items: { type: "string" }
}
}
}
}
}
};
const emailContent = await Mail.getLatestEmail();
const extractedEvents = await Assistant.requestStructuredData(
`从以下邮件中提取所有会议和事件信息:\n${emailContent}`,
emailSchema,
{ provider: "openai" }
);
// 7. 自动添加到日历
for (const event of extractedEvents.events) {
await Calendar.addEvent({
title: event.title,
startDate: new Date(event.startDate),
endDate: new Date(event.endDate),
location: event.location,
notes: `从邮件自动提取: ${extractedEvents.subject}`
});
}
console.log(`成功添加 ${extractedEvents.events.length} 个事件到日历`);
```
### 设备能力访问 - 蓝牙和健康数据
访问 iOS 设备的硬件功能和传感器
```typescript
// 1. 蓝牙中心管理器 - 扫描和连接设备
// 扫描蓝牙设备
const manager = new BluetoothCentralManager();
await manager.startScanning({
serviceUUIDs: ["180D"], // 心率服务
allowDuplicates: false
});
manager.onDiscover = (peripheral) => {
console.log(`发现设备: ${peripheral.name}, RSSI: ${peripheral.rssi}`);
if (peripheral.name?.includes("HeartRate")) {
manager.stopScanning();
connectToDevice(peripheral);
}
};
// 连接并读取特征值
async function connectToDevice(peripheral: BluetoothPeripheral) {
await manager.connect(peripheral);
console.log("已连接到设备");
// 发现服务
const services = await peripheral.discoverServices(["180D"]);
const heartRateService = services[0];
// 发现特征
const characteristics = await heartRateService.discoverCharacteristics(["2A37"]);
const heartRateMeasurement = characteristics[0];
// 订阅通知
await heartRateMeasurement.setNotifyValue(true);
heartRateMeasurement.onUpdate = (data) => {
const heartRate = parseHeartRateData(data);
console.log(`心率: ${heartRate} BPM`);
// 保存到 HealthKit
saveHeartRateToHealth(heartRate);
};
}
// 2. 蓝牙外设管理器 - 创建 GATT 服务器
const peripheralManager = new BluetoothPeripheralManager();
// 定义自定义服务和特征
const customService = new BluetoothService({
uuid: "12345678-1234-1234-1234-123456789012",
primary: true
});
const dataCharacteristic = new BluetoothCharacteristic({
uuid: "12345678-1234-1234-1234-123456789013",
properties: ["read", "write", "notify"],
permissions: ["readable", "writeable"],
value: Data.fromString("Hello BLE")
});
customService.addCharacteristic(dataCharacteristic);
peripheralManager.addService(customService);
// 开始广播
await peripheralManager.startAdvertising({
localName: "MyScriptingDevice",
serviceUUIDs: [customService.uuid]
});
// 处理写入请求
dataCharacteristic.onWrite = (data, request) => {
console.log("收到数据:", data.toString());
request.respond({ success: true });
// 发送通知给订阅者
dataCharacteristic.notify(Data.fromString("Acknowledged"));
};
// 3. HealthKit - 读取健康数据
// 请求权限
await HealthKit.requestAuthorization({
read: [
HealthQuantityType.heartRate,
HealthQuantityType.stepCount,
HealthQuantityType.activeEnergyBurned
],
write: [HealthQuantityType.heartRate]
});
// 读取步数(最近 7 天)
const stepSamples = await HealthKit.querySamples({
type: HealthQuantityType.stepCount,
startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
endDate: new Date(),
limit: 100
});
let totalSteps = 0;
for (const sample of stepSamples) {
totalSteps += sample.quantity.value;
console.log(`${sample.startDate}: ${sample.quantity.value} 步`);
}
console.log(`7 天总步数: ${totalSteps}`);
// 查询统计数据(每日平均心率)
const heartRateStats = await HealthKit.queryStatistics({
type: HealthQuantityType.heartRate,
startDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
endDate: new Date(),
options: ["discreteAverage", "discreteMin", "discreteMax"]
});
console.log(`平均心率: ${heartRateStats.averageQuantity?.value} BPM`);
console.log(`最低心率: ${heartRateStats.minimumQuantity?.value} BPM`);
console.log(`最高心率: ${heartRateStats.maximumQuantity?.value} BPM`);
// 写入心率数据
await HealthKit.saveSample({
type: HealthQuantityType.heartRate,
quantity: {
value: 75,
unit: HealthUnit.beatsPerMinute
},
startDate: new Date(),
endDate: new Date()
});
// 4. 位置服务
// 请求权限并获取当前位置
await Location.requestAuthorization("whenInUse");
const currentLocation = await Location.requestCurrent({
forceRequest: true, // 强制获取最新位置(不使用缓存)
accuracy: "best"
});
console.log(`纬度: ${currentLocation.latitude}`);
console.log(`经度: ${currentLocation.longitude}`);
console.log(`海拔: ${currentLocation.altitude}m`);
console.log(`精度: ${currentLocation.horizontalAccuracy}m`);
// 5. 相机和相册
// 拍照
const photo = await Photos.takePhoto({
cameraDevice: "back",
flashMode: "auto"
});
// 选择多张照片
const selectedPhotos = await Photos.pickPhotos({
selectionLimit: 10,
filter: "images"
});
for (const photo of selectedPhotos) {
const imageData = await photo.getData();
const base64 = imageData.toBase64();
// 上传或处理图片
await uploadPhoto(base64);
}
// 6. 通知
// 发送本地通知
await Notification.schedule({
title: "提醒",
body: "您的心率异常,请注意休息",
trigger: {
type: "timeInterval",
timeInterval: 60 // 60 秒后
},
sound: "default",
badge: 1
});
// 7. 生物识别认证
const authResult = await LocalAuth.authenticate({
reason: "验证身份以访问健康数据",
policy: "deviceOwnerAuthenticationWithBiometrics"
});
if (authResult.success) {
console.log("认证成功");
// 访问敏感数据
} else {
console.log("认证失败:", authResult.error);
}
```
### UI 组件和布局
使用类 SwiftUI 的 TSX 语法构建原生界面
```typescript
// 1. 基础布局组件
Navigation.present({
element: (
{/* 垂直堆栈 */}
智能家居控制
{/* 水平堆栈 */}
我的家
{/* 网格布局 */}
{/* 列表 */}
setTemperature(val)}
/>
{temperature}°C
{/* 按钮 */}
)
});
// 2. 图表组件
每日步数统计
{/* 柱状图 */}
{/* 折线图 */}
{/* 饼图 */}
// 3. 表单控件
// 4. 导航和页面切换
// Tab 导航
// 页面跳转
Navigation.push({
element:
});
// 模态页面
Navigation.presentSheet({
element: ,
detents: ["medium", "large"]
});
// 5. 视图修饰符链式调用
Hello World
.modifiers(
ViewModifiers.padding(20),
ViewModifiers.background("blue"),
ViewModifiers.foregroundStyle("white"),
ViewModifiers.cornerRadius(12),
ViewModifiers.shadow({ radius: 10, x: 0, y: 5 })
)
```
### 网络请求和实时通信
HTTP、WebSocket 和 Socket.IO 通信
```typescript
// 1. 基础 HTTP 请求
// GET 请求
const response = await fetch("https://api.example.com/users", {
method: "GET",
headers: {
"Authorization": "Bearer " + apiToken,
"Content-Type": "application/json"
}
});
if (response.ok) {
const users = await response.json();
console.log("用户列表:", users);
}
// POST 请求
const createResponse = await fetch("https://api.example.com/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: "张三",
email: "zhangsan@example.com"
})
});
const newUser = await createResponse.json();
console.log("创建的用户:", newUser);
// 2. Request API - 高级请求
// 表单数据上传
const formData = new FormData();
formData.append("file", imageData);
formData.append("title", "我的照片");
const uploadRequest = new Request("https://api.example.com/upload", {
method: "POST",
body: formData,
allowInsecureRequest: false // 禁止不安全请求
});
const uploadResponse = await fetch(uploadRequest);
// 文件下载
const downloadResponse = await fetch("https://example.com/file.pdf");
const fileData = await downloadResponse.blob();
await FileManager.writeData(fileData, "Downloads/file.pdf");
// 3. WebSocket 实时通信
const ws = new WebSocket("wss://api.example.com/realtime");
ws.onopen = () => {
console.log("WebSocket 已连接");
// 发送消息
ws.send(JSON.stringify({
type: "subscribe",
channel: "device_status"
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("收到消息:", data);
if (data.type === "device_update") {
// 更新设备状态
updateDeviceUI(data.deviceId, data.status);
}
};
ws.onerror = (error) => {
console.error("WebSocket 错误:", error);
};
ws.onclose = () => {
console.log("WebSocket 已断开");
// 重连逻辑
setTimeout(() => reconnect(), 5000);
};
// 发送心跳包
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "ping" }));
}
}, 30000);
// 4. Socket.IO 客户端
const socket = io("https://api.example.com", {
transports: ["websocket"],
auth: {
token: apiToken
}
});
socket.on("connect", () => {
console.log("Socket.IO 已连接, ID:", socket.id);
// 加入房间
socket.emit("join_room", { roomId: "home_devices" });
});
// 监听自定义事件
socket.on("device_status_changed", (data) => {
console.log(`设备 ${data.deviceId} 状态变更:`, data.status);
Widget.reloadAll();
});
// 发送事件
socket.emit("control_device", {
deviceId: "light_1",
action: "turn_on"
});
// 请求-响应模式
socket.emit("get_devices", null, (response) => {
console.log("所有设备:", response.devices);
});
socket.on("disconnect", (reason) => {
console.log("Socket.IO 断开:", reason);
});
// 5. OAuth2 认证
const oauth = new OAuth2({
authorizationEndpoint: "https://oauth.example.com/authorize",
tokenEndpoint: "https://oauth.example.com/token",
clientId: "your_client_id",
clientSecret: "your_client_secret",
redirectUri: "scripting://oauth-callback",
scopes: ["read", "write", "profile"]
});
// 启动授权流程
const authResult = await oauth.authorize();
if (authResult.success) {
const accessToken = authResult.accessToken;
const refreshToken = authResult.refreshToken;
// 保存 token
await Keychain.set("oauth_access_token", accessToken);
await Keychain.set("oauth_refresh_token", refreshToken);
// 使用 token 访问 API
const apiResponse = await fetch("https://api.example.com/user", {
headers: { "Authorization": `Bearer ${accessToken}` }
});
}
// 刷新 token
const newToken = await oauth.refreshAccessToken(refreshToken);
// 6. SSH 连接
const ssh = new SSH({
host: "192.168.1.100",
port: 22,
username: "pi",
password: "raspberry"
});
await ssh.connect();
console.log("SSH 已连接");
// 执行命令
const result = await ssh.execute("ls -la /home/pi");
console.log("命令输出:\n", result.stdout);
// 上传文件
await ssh.uploadFile(
"/local/path/config.json",
"/home/pi/config.json"
);
// 下载文件
await ssh.downloadFile(
"/home/pi/data.txt",
"/local/path/data.txt"
);
await ssh.disconnect();
```
## 项目总结
### 主要用途
这个文档生成项目服务于 **Scripting App** 生态系统的核心需求:为开发者提供完整、准确、易于导航的中英文双语 API 参考文档。主要用途包括:(1) **自动化文档生成**:通过读取结构化 JSON 配置和 Markdown 源文件,自动生成 325+ 个 API 文档页面,大幅减少手动维护成本;(2) **多语言支持**:同时生成英文和简体中文版本,确保不同语言背景的开发者都能轻松学习 Scripting;(3) **持续集成部署**:通过 GitHub Actions 实现自动化构建和部署,每次代码推送后自动更新线上文档;(4) **版本管理**:维护清晰的更新日志(Changelog),通过 RSS 订阅让开发者及时了解新功能和 API 变更;(5) **开发者体验优化**:提供全文搜索、代码块语法高亮、行号显示、响应式设计等功能,提升文档阅读和使用体验。
该项目展示了现代文档工程的最佳实践:内容与结构分离(JSON + Markdown)、自动化生成流程、静态站点性能优化(Rspress)、以及 CI/CD 自动化。文档涵盖了 Scripting 的全部功能领域,包括脚本运行时环境、交互式 Widget/Live Activity、设备能力访问(蓝牙、健康数据、位置、相机等)、50+ 个 UI 组件、网络通信(HTTP、WebSocket、Socket.IO)、AI 助手集成等,为开发者提供了从快速入门到高级功能的完整学习路径。
### 集成模式
项目的集成模式体现了 **文档驱动开发(Documentation-Driven Development)** 的理念,核心集成点包括:(1) **源文件集成**:文档内容存储在独立的 `Scripting Documentation` 资源包中(doc.json 配置 + Markdown 文档 + TSX 示例代码),这个资源包同时也是 Scripting App 的默认内置脚本,实现了"文档即代码、代码即文档"的双向同步;(2) **构建流程集成**:使用 Bun 作为高性能 JavaScript 运行时,结合 Rspress 静态站点生成器,通过 Node.js 脚本(`scripts/docs.js`)将源文件转换为 MDX 格式,生成导航元数据(_meta.json),最终输出为静态 HTML/CSS/JS;(3) **部署流程集成**:GitHub Actions 监听 main 分支推送事件,自动触发构建流程,构建产物上传到 GitHub Pages,实现零配置自动部署;(4) **RSS 集成**:通过 `@rspress/plugin-rss` 插件自动生成 RSS 订阅源,支持开发者通过 RSS 阅读器订阅更新日志。
技术栈集成方面:(1) **TypeScript 类型系统**:项目使用 TypeScript 确保配置文件、构建脚本的类型安全;(2) **MDX 格式**:支持在 Markdown 中嵌入 JSX 组件,为未来的交互式文档提供扩展性;(3) **i18n 系统**:通过 `i18n.json` 和 Rspress 的多语言配置实现完整的双语支持;(4) **插件生态**:利用 Rspress 插件系统扩展功能(RSS、GitHub Pages),保持核心配置简洁。未来集成方向可能包括:版本化文档(支持多版本 API 并存)、交互式代码示例(在线运行 TSX 代码)、社区贡献系统(通过 PR 提交文档改进)、以及与 Scripting App 的深度链接(从 App 内直接跳转到对应文档页面)。