223 lines
5.3 KiB
Markdown
223 lines
5.3 KiB
Markdown
# 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 |
|