# 桌面应用

## Purpose

TBD - 提供跨平台桌面应用支持，将后端服务与前端静态资源打包为单一可执行文件

## Requirements

### Requirement: 桌面应用启动

系统 SHALL 支持作为桌面应用启动，将后端服务与前端静态资源打包为单一可执行文件。

#### Scenario: 双击启动

- **WHEN** 用户双击桌面应用可执行文件
- **THEN** 系统使用 `gofrs/flock` 尝试获取排他文件锁
- **AND** 锁文件路径为系统临时目录下的 `nex-gateway.lock`
- **AND** 系统启动后端服务
- **AND** 系统托盘图标出现
- **AND** 浏览器自动打开 `http://localhost:9826` 显示管理界面

#### Scenario: 单实例检查

- **WHEN** 用户尝试启动第二个实例
- **THEN** 系统检测到已有实例持有文件锁
- **AND** 显示错误提示"已有 Nex 实例运行"
- **AND** 新实例退出

#### Scenario: 退出释放锁

- **WHEN** 用户点击托盘菜单"退出"
- **THEN** 系统释放文件锁
- **AND** 应用进程退出

### Requirement: 系统托盘

系统 SHALL 提供跨平台系统托盘功能，支持托盘图标和菜单。图标格式 SHALL 根据平台自动选择。

#### Scenario: 托盘图标显示

- **WHEN** 桌面应用启动成功
- **THEN** 系统根据平台加载正确的图标格式
- **AND** 在 Windows 上加载 ICO 格式图标（`assets/icon.ico`）
- **AND** 在 macOS 和 Linux 上加载 PNG 格式图标（`assets/icon.png`）
- **AND** 托盘图标 tooltip 显示"AI Gateway"

#### Scenario: 托盘菜单显示

- **WHEN** 用户点击托盘图标（左键或右键）
- **THEN** 显示托盘菜单
- **AND** 菜单包含"打开管理界面"选项
- **AND** 菜单包含"状态: 运行中"选项（禁用状态）
- **AND** 菜单包含"端口: 9826"选项（禁用状态）
- **AND** 菜单包含"退出"选项

#### Scenario: 打开管理界面

- **WHEN** 用户点击托盘菜单"打开管理界面"
- **THEN** 系统在浏览器中打开 `http://localhost:9826`

#### Scenario: 浏览器打开失败

- **WHEN** 系统无法打开浏览器（浏览器未安装等）
- **THEN** 托盘菜单仍可正常使用
- **AND** 用户可手动访问 `http://localhost:9826`

#### Scenario: 退出应用

- **WHEN** 用户点击托盘菜单"退出"
- **THEN** 系统优雅关闭后端服务
- **AND** 托盘图标消失
- **AND** 应用进程退出

### Requirement: 静态文件服务

系统 SHALL 通过 Gin 同时服务 API 和前端静态资源。

#### Scenario: API 请求路由

- **WHEN** 请求路径以 `/api/` 或 `/v1/` 开头
- **THEN** 请求由现有业务 handler 处理

#### Scenario: 静态资源路由

- **WHEN** 请求路径为 `/assets/*`
- **THEN** 返回嵌入的前端静态资源文件

#### Scenario: SPA 路由回退

- **WHEN** 请求路径不匹配任何 API 或静态资源路由
- **THEN** 返回 `index.html`（支持前端 SPA 路由）

### Requirement: 端口冲突检测

系统 SHALL 在启动前检测端口是否可用。

#### Scenario: 端口可用

- **WHEN** 端口 9826 未被占用
- **THEN** 服务正常启动

#### Scenario: 端口被占用

- **WHEN** 端口 9826 已被其他程序占用
- **THEN** 显示错误提示"端口 9826 已被占用"
- **AND** 应用退出

### Requirement: 跨平台构建

系统 SHALL 支持跨平台构建和打包。构建 target SHALL 按平台分离，产物文件名 SHALL 使用 `nex-{os}-{arch}[.exe]` 格式。

#### Scenario: macOS 构建

- **WHEN** 执行 `desktop-build-mac` 构建命令
- **THEN** 生成 `nex-mac-arm64` 和 `nex-mac-amd64` 可执行文件
- **AND** 可打包为 `.app` bundle

#### Scenario: Windows 构建

- **WHEN** 执行 `desktop-build-win` 构建命令
- **THEN** 生成 `nex-win-amd64.exe` 可执行文件
- **AND** 使用 `-H=windowsgui` linker flag 隐藏控制台窗口

#### Scenario: Linux 构建

- **WHEN** 执行 `desktop-build-linux` 构建命令
- **THEN** 生成 `nex-linux-amd64` 可执行文件

### Requirement: macOS .app 打包

系统 SHALL 支持打包为 macOS .app bundle。

#### Scenario: .app 结构

- **WHEN** 执行打包脚本
- **THEN** 生成 `Nex.app` 目录结构
- **AND** 包含 `Contents/Info.plist` 元数据
- **AND** 包含 `Contents/MacOS/nex` 可执行文件
- **AND** 包含 `Contents/Resources/icon.icns` 图标
- **AND** `Info.plist` 中 `LSUIElement` 为 `true`（不显示 Dock 图标）

### Requirement: Windows 原生对话框

系统 SHALL 在 Windows 上使用 `user32.dll` 的 `MessageBoxW` API 显示错误对话框，替代 `msg *` 命令。

#### Scenario: 错误提示对话框

- **WHEN** 应用在 Windows 上遇到启动错误（端口占用、配置加载失败等）
- **THEN** 使用 `MessageBoxW` 显示模态对话框
- **AND** 对话框标题栏显示应用名称
- **AND** 对话框包含错误描述文本
- **AND** 对话框显示错误图标（MB_ICONERROR）

#### Scenario: 非 Windows 平台不受影响

- **WHEN** 应用运行在 macOS 或 Linux 上
- **THEN** 错误对话框仍使用平台原有实现（osascript / zenity）

### Requirement: Linux 对话框降级策略

系统 SHALL 在 Linux 上按优先级检测并使用可用的对话框工具，确保在不同桌面环境下都能显示对话框。

#### Scenario: zenity 可用
- **WHEN** 系统检测到 `zenity` 命令可用
- **THEN** 使用 zenity 显示 GTK 风格对话框
- **AND** 错误对话框使用 `zenity --error` 命令
- **AND** 信息对话框使用 `zenity --info` 命令

#### Scenario: kdialog 可用
- **WHEN** zenity 不可用且 `kdialog` 命令可用
- **THEN** 使用 kdialog 显示 KDE 风格对话框
- **AND** 错误对话框使用 `kdialog --error` 命令
- **AND** 信息对话框使用 `kdialog --msgbox` 命令

#### Scenario: notify-send 可用
- **WHEN** zenity 和 kdialog 均不可用且 `notify-send` 命令可用
- **THEN** 使用 notify-send 显示系统通知
- **AND** 错误通知使用 `-u critical` 参数
- **AND** 信息通知使用默认参数

#### Scenario: xmessage 可用
- **WHEN** zenity、kdialog、notify-send 均不可用且 `xmessage` 命令可用
- **THEN** 使用 xmessage 显示基础 X11 对话框
- **AND** 对话框居中显示（`-center` 参数）

#### Scenario: 无对话框工具可用
- **WHEN** 所有对话框工具均不可用
- **THEN** 降级到标准错误输出
- **AND** 输出格式为 `错误: <title>: <message>`

#### Scenario: 工具检测缓存
- **WHEN** 应用启动
- **THEN** 系统检测一次可用对话框工具
- **AND** 检测结果缓存在包级变量
- **AND** 后续对话框调用直接使用缓存结果，不重复检测

### Requirement: macOS AppleScript 字符转义

系统 SHALL 对 AppleScript 对话框中的特殊字符进行转义，确保脚本正确执行。

#### Scenario: 转义反斜杠
- **WHEN** 对话框消息包含反斜杠字符 `\`
- **THEN** 转义为 `\\`

#### Scenario: 转义双引号
- **WHEN** 对话框消息包含双引号字符 `"`
- **THEN** 转义为 `\"`

#### Scenario: 多行文本处理
- **WHEN** 对话框消息包含换行符 `\n`
- **THEN** AppleScript 正确显示多行文本
