新增: u-tabs 初始版本
Go TUI 项目启动器,基于 bubbletea v2 + lipgloss v2。 支持分组 Tab、多选启动、编号跳转、Windows Terminal 集成。
This commit is contained in:
222
docs/02-技术文档/架构设计.md
Normal file
222
docs/02-技术文档/架构设计.md
Normal file
@@ -0,0 +1,222 @@
|
||||
# u-tabs 架构设计
|
||||
|
||||
> 最后更新:2026-05-15 | 基于 u-tabs v0.3.0
|
||||
|
||||
## 1. 项目概述
|
||||
|
||||
u-tabs 是一个 TUI 工作空间启动器,用 Bubble Tea v2 实现。管理多个工作空间(YAML 配置驱动),通过 Windows Terminal + Claude Code 标签页启动。
|
||||
|
||||
**技术栈**: Go 1.26 / BubbleTea v2 / LipGloss v2
|
||||
|
||||
## 2. 架构
|
||||
|
||||
```
|
||||
main.go → Model (app.go)
|
||||
├── 分组 Tab 栏
|
||||
├── 左侧项目列表
|
||||
├── 右侧详情面板
|
||||
└── Enter → wt.exe → claude
|
||||
```
|
||||
|
||||
## 3. 配置系统
|
||||
|
||||
### 3.1 加载优先级
|
||||
|
||||
```
|
||||
~/.u-tabs/config.yaml → exe同目录/config.yaml → 内置默认值 (workspace.go)
|
||||
```
|
||||
|
||||
### 3.2 YAML 结构
|
||||
|
||||
```yaml
|
||||
groups:
|
||||
- label: CORE # 分组标签,决定编号基数
|
||||
desc: 核心业务
|
||||
items:
|
||||
- title: flux # 短名
|
||||
prompt: 描述 # AI prompt
|
||||
tech: 技术栈 # 精确到版本
|
||||
deploy: 部署 # 服务器/端口/域名
|
||||
dir: 目录路径 # 启动目录
|
||||
```
|
||||
|
||||
### 3.3 编号规则
|
||||
|
||||
由 `GroupConfig.Base` 字段决定,若未设置默认从 0 开始。
|
||||
|
||||
### 3.4 数据转换
|
||||
|
||||
`Config.ToInternal()` 将 YAML 配置转为运行时结构,自动生成:
|
||||
- `Index`: 全局索引 (0, 1, 2...)
|
||||
- `N`: 编号 (base + item索引)
|
||||
- `Group`: 继承父级 label
|
||||
- `Dir`: `~` 展开为用户主目录
|
||||
|
||||
## 4. 数据模型
|
||||
|
||||
### 4.1 Workspace
|
||||
|
||||
```go
|
||||
type Workspace struct {
|
||||
Index int // 全局索引
|
||||
N int // 编号
|
||||
Title string // 短名
|
||||
Prompt string // 描述
|
||||
Tech string // 技术栈
|
||||
Deploy string // 部署情况
|
||||
Dir string // 目录路径
|
||||
Group string // 分组标签
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 Group
|
||||
|
||||
```go
|
||||
type Group struct {
|
||||
Label string
|
||||
Desc string
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 运行时索引
|
||||
|
||||
| 变量 | 类型 | 用途 |
|
||||
|------|------|------|
|
||||
| `Groups` | `[]Group` | 分组定义 |
|
||||
| `AllWorkspaces` | `[]Workspace` | 全部工作空间 |
|
||||
| `wsByNum` | `map[int]*Workspace` | 编号→Workspace O(1) 索引 |
|
||||
|
||||
## 5. 主模型 (app.go)
|
||||
|
||||
### 5.1 Model
|
||||
|
||||
```go
|
||||
type Model struct {
|
||||
activeGroup int // 当前分组索引
|
||||
cursor int // 分组内光标
|
||||
selected map[int]bool // 多选标记
|
||||
inputBuf string // 数字快捷输入缓冲
|
||||
width, height int
|
||||
launched string // 启动提示
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Update 消息路由
|
||||
|
||||
```
|
||||
KeyPressMsg
|
||||
├── q, ctrl+c → tea.Quit
|
||||
├── tab, right, l → activeGroup++ → cursor=0
|
||||
├── shift+tab, left, h → activeGroup--
|
||||
├── 1, 2, 3, 4 → 跳转分组
|
||||
├── up, k → cursor--
|
||||
├── down, j → cursor++
|
||||
├── Space → toggleMultiSelect
|
||||
├── enter → inputBuf非空? launchByInput() : launchSelected()
|
||||
├── c → copyCommand()
|
||||
└── [0-9] → 追加到 inputBuf
|
||||
```
|
||||
|
||||
### 5.3 View 渲染
|
||||
|
||||
```
|
||||
View()
|
||||
└── 启动器模式:
|
||||
├─ 第1行: "u-tabs"标题 + 分组 Tab 栏
|
||||
├─ 分隔线
|
||||
├─ 左右布局 (JoinHorizontal):
|
||||
│ ├── 左侧列表: 编号 + 标题 + 描述 (CJK安全截断)
|
||||
│ └── 右侧详情: 目录/编号/描述/技术/部署/命令预览
|
||||
├─ 输入缓冲提示
|
||||
├─ 启动成功提示
|
||||
└─ 帮助栏 (快捷键高亮)
|
||||
```
|
||||
|
||||
### 5.4 CJK 安全截断
|
||||
|
||||
`truncateByWidth()` 按 rune 显示宽度截断,中文=2宽,英文=1宽,不会切断多字节字符:
|
||||
|
||||
```go
|
||||
func truncateByWidth(s string, maxW int) string {
|
||||
w := 0
|
||||
for i := 0; i < len(s); {
|
||||
_, size := utf8.DecodeRuneInString(s[i:])
|
||||
rw := runeWidth(rune(s[i]))
|
||||
if w+rw > maxW { return s[:i] }
|
||||
w += rw
|
||||
i += size
|
||||
}
|
||||
return s
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 启动逻辑
|
||||
|
||||
### 6.1 流程
|
||||
|
||||
```
|
||||
用户按 Enter
|
||||
↓
|
||||
buildLaunchScript(ws) → PS 脚本
|
||||
↓
|
||||
encodePSCommand(script) → UTF-16LE → Base64
|
||||
↓
|
||||
exec.Command("wt.exe", "-w", "0",
|
||||
"-d", ws.Dir,
|
||||
"--tabColor", randomColor,
|
||||
"pwsh", "-NoExit", "-EncodedCommand", encoded).Start()
|
||||
↓
|
||||
Windows Terminal 新标签页 → claude 交互模式
|
||||
```
|
||||
|
||||
### 6.2 PS 脚本模板
|
||||
|
||||
```powershell
|
||||
$Host.UI.RawUI.WindowTitle = "{Title}"
|
||||
Write-Host "=== {Title} ===" -ForegroundColor Cyan
|
||||
Write-Host "Prompt: {Prompt}" -ForegroundColor Yellow
|
||||
cd "{Dir}"
|
||||
claude --name "{Title}" --permission-mode bypassPermissions
|
||||
```
|
||||
|
||||
### 6.3 UTF-16LE 编码
|
||||
|
||||
```go
|
||||
func encodePSCommand(script string) string {
|
||||
u16 := utf16.Encode([]rune(script))
|
||||
b := make([]byte, len(u16)*2)
|
||||
for i, r := range u16 {
|
||||
b[i*2] = byte(r)
|
||||
b[i*2+1] = byte(r >> 8)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 样式系统 (Tokyo Night 主题)
|
||||
|
||||
### 7.1 色板
|
||||
|
||||
| 常量 | Hex | 用途 |
|
||||
|------|-----|------|
|
||||
| BgDark | #1a1b26 | 深底色 |
|
||||
| BgPanel | #292e42 | 面板/边框/分隔线 |
|
||||
| Dim | #565f89 | 次要文字 |
|
||||
| Fg | #a9b1d6 | 正文 |
|
||||
| Bright | #c0caf5 | 高亮文字 |
|
||||
| Accent | #7aa2f7 | 主强调 (蓝色) |
|
||||
| Success | #9ece6a | 绿色 (部署/标记) |
|
||||
| Warning | #e0af68 | 黄色 (输入提示) |
|
||||
| Cyan | #7dcfff | 青色 (编号/详情) |
|
||||
| Purple | #bb9af7 | 紫色 (选中行) |
|
||||
| Red | #f7768e | 红色 (CORE分组) |
|
||||
|
||||
### 7.2 分组颜色
|
||||
|
||||
| 分组 | Hex |
|
||||
|------|-----|
|
||||
| CORE | #f7768e |
|
||||
| LAB | #9ece6a |
|
||||
| TOOLS | #e0af68 |
|
||||
| ME | #bb9af7 |
|
||||
| TEMP | #7dcfff |
|
||||
Reference in New Issue
Block a user