# 桌面 API

## 功能
`bt_app` 会向 `static`、`server` 和 `remote` 页面注入 `window.bt`。第一版桌面 API 提供 BT 后端调用、窗口控制、系统对话框、托盘、剪贴板、通知、文件拖入和应用信息能力。

页面只应使用 `window.bt`。运行器不会公开全局 `window.__TAURI__`，也不会把本地 `fs`、`net`、`process` 能力直接暴露给前端。

## 语法
```text
window.bt.call(name, ...args)
window.bt.window.set_title(title)
window.bt.window.set_size(width, height)
window.bt.window.set_close_mode(mode)
window.bt.dialog.open_file(options)
window.bt.tray.enable(options)
window.bt.clipboard.read_text()
window.bt.notify.show(options)
window.bt.drag.on_files(callback)
window.bt.app.version()
```

所有异步方法返回 `Promise`。成功时直接返回数据；失败时 Promise 会 reject，错误值是字符串或可转为字符串的错误对象。

## 参数
### bt.call
| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| name | string | 是 | `main.bt` 中的全局函数名 |
| ...args | any | 否 | 传给 BT 函数的参数，会按 JSON 值转换 |

### bt.window
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| set_title | title:string | void | 设置窗口标题 |
| set_size | width:number, height:number | void | 设置窗口逻辑尺寸 |
| set_resizable | resizable:boolean | void | 设置是否允许调整大小 |
| minimize / maximize / restore / close / hide / show / focus / center | 无 | void | 常用窗口动作 |
| set_fullscreen | fullscreen:boolean | void | 设置全屏 |
| is_fullscreen / is_maximized / is_minimized / is_visible / is_always_on_top | 无 | boolean | 读取窗口状态 |
| set_always_on_top | enabled:boolean | void | 设置窗口置顶 |
| set_decorations | visible:boolean | void | 设置系统标题栏和边框 |
| set_skip_taskbar | enabled:boolean | void | 设置是否跳过任务栏 |
| set_close_mode | mode:string | void | `exit`、`hide` 或 `tray` |
| drag | 无 | void | 自定义标题栏拖动窗口 |
| start_resize | edge:string | void | `top`、`bottom`、`left`、`right`、`top_left`、`top_right`、`bottom_left`、`bottom_right` |
| flash | 无 | void | 请求系统提醒用户关注窗口 |

### bt.dialog
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| open_file | options | string 或 null | 选择单个文件 |
| open_files | options | string[] | 选择多个文件 |
| open_dir | options | string 或 null | 选择目录 |
| save_file | options | string 或 null | 选择保存路径 |
| message | message:string, options | void | 显示消息框 |
| confirm | message:string, options | boolean | 显示确认框 |

`options.title` 为对话框标题，`options.default_path` 为默认路径，`options.filters` 为文件过滤器数组。消息框 `options.kind` 支持 `info`、`warning`、`error`。

### bt.tray
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| enable | options | void | 启用托盘图标，重复调用会更新现有托盘 |
| disable | 无 | void | 关闭托盘图标 |
| set_icon | icon:string | void | 设置托盘图标路径 |
| set_tooltip | text:string | void | 设置托盘提示 |
| set_menu | menu:array | void | 设置托盘菜单 |
| on_menu_click | callback | function | 监听菜单点击，返回取消监听函数 |

菜单项格式为 `{id:'show', text:'显示窗口', enabled:true}`；分隔线格式为 `{type:'separator'}`。同一应用默认只保留一个托盘图标，重复调用 `window.bt.tray.enable()` 会更新图标、提示文本和菜单，不会新增多个托盘入口。

`window.bt.window.set_close_mode('tray')` 只设置窗口关闭按钮的行为：之后用户点击窗口关闭按钮或代码调用 `window.bt.window.close()` 时，窗口会隐藏到托盘，程序继续常驻运行。如果需要按钮点击后立刻关闭到托盘，应先设置 `tray` 模式，再调用 `window.bt.window.close()`。

### bt.clipboard
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| read_text | 无 | string | 读取文本剪贴板 |
| write_text | text:string | void | 写入文本 |
| clear | 无 | void | 清空剪贴板 |

### bt.notify
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| permission_state | 无 | string | `granted`、`denied` 或 `prompt` |
| request_permission | 无 | string | 请求通知权限 |
| show | options | void | 显示通知 |

`options.title` 为空时使用应用标题，`options.body` 为通知正文。

`request_permission()` 返回的是系统通知权限状态；`show()` 才是真正发送系统通知。Windows 便携 exe 会使用兼容的 toast 发送路径，不依赖安装器创建的应用通知注册。通知是否弹出桌面横幅仍由操作系统通知设置、勿扰模式和应用通知策略决定，未弹出时通常仍可在系统通知中心查看。

### bt.drag
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| on_files | callback | function | 监听拖入文件或目录，回调参数为绝对路径数组 |

### bt.app
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| version | 无 | string | 当前应用版本 |
| engine_version | 无 | string | bt_app 引擎版本 |
| platform | 无 | string | `windows`、`macos`、`linux` 或其他系统名 |
| open_url | url:string | void | 用系统默认浏览器打开 HTTP/HTTPS 地址 |
| open_path | path:string | void | 用系统默认程序打开文件或目录 |
| reveal_path | path:string | void | 在文件管理器中定位路径 |
| quit | 无 | void | 退出程序 |
| args | 无 | string[] | 启动参数 |

## 返回值
`bt.call()` 直接返回 BT 函数的返回值。BT 函数返回对象时，前端收到普通 JSON 对象；返回字符串、数字、布尔或数组时，前端收到对应 JSON 值。

如果 BT 函数不存在、执行报错、VM 调用队列已满或桌面 API 参数无效，Promise 会 reject，不会返回 `{error,message,data}` 包装对象。

## 代码示例
`main.bt`：

```bt
/**
 * 返回前端传入的数据。
 *
 * @param data 前端 JSON 参数。
 * @return 响应对象。
 */
fn inspect(data) {
    {
        ok: true,
        message: 'BT 已收到',
        data: data
    }
}
```

页面调用：

```js
try {
  const result = await window.bt.call('inspect', {name: 'BT'})
  await window.bt.window.set_title(result.message)
} catch (err) {
  console.log('调用失败', String(err))
}
```

托盘菜单：

```js
await window.bt.tray.enable({
  tooltip: 'BT 应用',
  menu: [
    {id: 'show', text: '显示窗口'},
    {type: 'separator'},
    {id: 'quit', text: '退出程序'}
  ]
})

window.bt.tray.on_menu_click(async (id) => {
  if (id == 'show') {
    await window.bt.window.show()
    await window.bt.window.focus()
  }
  if (id == 'quit') {
    await window.bt.app.quit()
  }
})

await window.bt.window.set_close_mode('tray')
await window.bt.window.close()
```

## 注意事项
- `bt.call()` 是长期 VM 业务通道；窗口、托盘、剪贴板、通知等桌面能力走独立 command，不占用 BT VM。
- VM 调用队列有上限。前端高频调用时应节流，避免无控增长。
- `remote` 页面同样拥有完整 `window.bt` 能力，只应加载可信地址。
- 前端不能直接访问本地文件系统、网络监听或进程能力，需要通过 `main.bt` 中受控函数封装。
- 完整示例位于 `examples/desktop-api`。
