Skip to content

Commit 01b1a57

Browse files
committed
feat(ui): 紧凑现代 CLI 输出 + 连接阶段状态刷新
- ProgressUI: barWidth 36→20, 移除 Stage 方法, 扫描/同步单行更紧凑 - Distribution: 精简为单行 hot/cold 摘要, 带颜色 - mount_strategy: 移除阶段横幅, printProgress 精简, merge 后单行提示 - main.go: 三处连接阶段统一为 "正在连接云主机 .... 成功/失败" 单行刷新
1 parent ed8d93e commit 01b1a57

3 files changed

Lines changed: 61 additions & 81 deletions

File tree

cmd/cloud-claude/main.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,21 +169,24 @@ func runEnvCheck(cmd *cobra.Command, args []string) error {
169169

170170
client := cloudclaude.NewEntryClient(cfg.Gateway)
171171

172-
fmt.Println("正在连接云主机...")
172+
stage := "正在连接云主机"
173+
fmt.Printf("%s .... ", stage)
173174

174175
authResp, err := client.AuthenticateAndWait(
175176
cmd.Context(),
176177
cfg.Username,
177178
cfg.Password,
178179
func(msg string) {
179-
fmt.Printf("\r%s", msg)
180+
fmt.Printf("\r\033[2K%s .... %s", stage, msg)
180181
},
181182
)
182183
if err != nil {
184+
fmt.Printf("\r\033[2K%s .... 失败\n", stage)
183185
return fmt.Errorf("认证失败: %w", err)
184186
}
187+
fmt.Printf("\r\033[2K%s .... 成功\n", stage)
185188

186-
fmt.Println("\r正在检测远端环境...")
189+
fmt.Println("正在检测远端环境...")
187190

188191
sshCfg := cloudclaude.SSHConfig{
189192
Host: authResp.SSHHost,
@@ -212,18 +215,24 @@ func runSSHDoctor(cmd *cobra.Command, args []string) error {
212215

213216
client := cloudclaude.NewEntryClient(cfg.Gateway)
214217

215-
fmt.Println("正在连接云主机...")
218+
stage := "正在连接云主机"
219+
fmt.Printf("%s .... ", stage)
220+
216221
authResp, err := client.AuthenticateAndWait(
217222
cmd.Context(),
218223
cfg.Username,
219224
cfg.Password,
220-
func(msg string) { fmt.Printf("\r%s", msg) },
225+
func(msg string) {
226+
fmt.Printf("\r\033[2K%s .... %s", stage, msg)
227+
},
221228
)
222229
if err != nil {
230+
fmt.Printf("\r\033[2K%s .... 失败\n", stage)
223231
return fmt.Errorf("认证失败: %w", err)
224232
}
233+
fmt.Printf("\r\033[2K%s .... 成功\n", stage)
225234

226-
fmt.Println("\r正在体检远端 /workspace/.ssh ...")
235+
fmt.Println("正在体检远端 /workspace/.ssh ...")
227236

228237
sshCfg := cloudclaude.SSHConfig{
229238
Host: authResp.SSHHost,
@@ -310,17 +319,19 @@ func runRoot(cmd *cobra.Command, args []string) error {
310319

311320
client := cloudclaude.NewEntryClient(cfg.Gateway)
312321

313-
fmt.Println("正在连接云主机...")
322+
stage := "正在连接云主机"
323+
fmt.Printf("%s .... ", stage)
314324

315325
authResp, err := client.AuthenticateAndWait(
316326
cmd.Context(),
317327
cfg.Username,
318328
cfg.Password,
319329
func(msg string) {
320-
fmt.Printf("\r%s", msg)
330+
fmt.Printf("\r\033[2K%s .... %s", stage, msg)
321331
},
322332
)
323333
if err != nil {
334+
fmt.Printf("\r\033[2K%s .... 失败\n", stage)
324335
errMsg := err.Error()
325336
fmt.Fprintln(os.Stderr, "错误: "+errMsg)
326337
switch {
@@ -339,8 +350,9 @@ func runRoot(cmd *cobra.Command, args []string) error {
339350
}
340351
return nil
341352
}
353+
fmt.Printf("\r\033[2K%s .... 成功\n", stage)
342354

343-
fmt.Println("\r正在映射工作目录并进入 Claude Code 会话...")
355+
fmt.Println("正在映射工作目录并进入 Claude Code 会话...")
344356

345357
sshCfg := cloudclaude.SSHConfig{
346358
Host: authResp.SSHHost,

internal/cloudclaude/mount_strategy.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -486,14 +486,12 @@ func tryModeReal(connA, connB *ssh.Client, mode Mode, cfg MountConfig, snapshot
486486
return nil, HotSyncStatus{}, hErr
487487
}
488488

489-
fmt.Fprintln(cfg.Logger, "\n━━ (2/3) 启动冷兜底 ━━")
490489
sCleanup, sErr := mountSSHFS(connA, cfg.Cwd, coldRoot)
491490
if sErr != nil {
492491
hCleanup()
493492
return nil, HotSyncStatus{}, sErr
494493
}
495494

496-
fmt.Fprintln(cfg.Logger, "\n━━ (3/3) 合并视图 ━━")
497495
cleanupStaleFUSE(connA, cfg.Cwd)
498496
branches := []string{hotRoot + "=RW", coldRoot + "=RO"}
499497
mergeCleanup, mergeErr := mountMerge(connA, branches, cfg.Cwd)
@@ -503,6 +501,8 @@ func tryModeReal(connA, connB *ssh.Client, mode Mode, cfg MountConfig, snapshot
503501
return nil, HotSyncStatus{}, mergeErr
504502
}
505503

504+
fmt.Fprintln(cfg.Logger, "冷兜底就绪 合并视图就绪")
505+
506506
// Phase 37: ColdPromoter 集成(仅在 Full 模式 + Linux + 非 NO_PROMOTION 时启动)
507507
noPromotion := os.Getenv("CLOUD_CLAUDE_NO_PROMOTION") == "1" || runtime.GOOS != "linux"
508508
var promoter *ColdPromoter
@@ -553,15 +553,11 @@ func tryModeReal(connA, connB *ssh.Client, mode Mode, cfg MountConfig, snapshot
553553
func printProgress(w io.Writer, mode Mode) {
554554
switch mode {
555555
case ModeFull:
556-
fmt.Fprintln(w, "\n━━ (1/3) 热同步 ━━")
556+
// Full 模式阶段标题由 ProgressUI 统一输出,此处不重复。
557557
case ModeHotOnly:
558-
fmt.Fprintln(w, "\n━━ (1/3) 热同步 ━━")
559-
fmt.Fprintf(w, " (2/3) 跳过 sshfs(模式: %s)\n", mode.String())
560-
fmt.Fprintf(w, " (3/3) 跳过 mergerfs(模式: %s)\n", mode.String())
558+
fmt.Fprintln(w, "同步代码库(hot-only)")
561559
case ModeSSHFSOnly:
562-
fmt.Fprintf(w, "\n━━ (1/3) 跳过热同步(模式: %s)━━\n", mode.String())
563-
fmt.Fprintln(w, "━━ (2/3) 启动冷兜底 ━━")
564-
fmt.Fprintf(w, " (3/3) 跳过 mergerfs(模式: %s)\n", mode.String())
560+
fmt.Fprintln(w, "跳过同步,直接挂载 sshfs")
565561
}
566562
}
567563

internal/cloudclaude/ui.go

Lines changed: 35 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
)
88

99
// ProgressUI 提供极客风终端进度展示。
10-
// 支持扫描阶段实时刷新、hot/cold 分布条、同步进度条
10+
// 核心约束:每阶段只保留最新一行,不刷屏
1111
type ProgressUI struct {
1212
w io.Writer
1313
enabled bool
@@ -24,75 +24,27 @@ func NewProgressUI(w io.Writer, noColor bool) *ProgressUI {
2424
return &ProgressUI{
2525
w: w,
2626
enabled: enabled,
27-
barWidth: 36,
27+
barWidth: 20,
2828
}
2929
}
3030

31-
// Stage 输出阶段标题(如 "━━ (1/3) 热同步 ━━")。
32-
func (p *ProgressUI) Stage(stage string) {
33-
fmt.Fprintf(p.w, "\n━━ %s ━━\n", stage)
34-
}
35-
36-
// Scanning 在单行实时刷新当前扫描到的文件和累计数量。
31+
// Scanning 在单行实时刷新扫描进度。
32+
// 格式:同步代码库 扫描 1,232 文件 src/utils.ts
3733
func (p *ProgressUI) Scanning(file string, count int) {
3834
if !p.enabled {
3935
return
4036
}
41-
fmt.Fprintf(p.w, "\r\033[2K 扫描中… %-40s 已发现 %s 个文件",
42-
truncate(file, 40), formatInt(count))
37+
fmt.Fprintf(p.w, "\r\033[2K同步代码库 扫描 %s 文件 %s",
38+
formatInt(count), truncate(file, 28))
4339
}
4440

45-
// ScanDone 结束扫描阶段,输出最终统计并换行
41+
// ScanDone 结束扫描,定格最终统计
4642
func (p *ProgressUI) ScanDone(total int) {
47-
fmt.Fprintf(p.w, "\r\033[2K 扫描完成,共 %s 个文件\n", formatInt(total))
43+
fmt.Fprintf(p.w, "\r\033[2K同步代码库 扫描完成 %s 文件\n", formatInt(total))
4844
}
4945

50-
// Distribution 输出 hot/cold 文件分布条(固定输出,不刷新)。
51-
func (p *ProgressUI) Distribution(hotFiles, hotBytes, coldFiles, coldBytes int64) {
52-
total := hotFiles + coldFiles
53-
hotPct := int64(0)
54-
if total > 0 {
55-
hotPct = hotFiles * 100 / total
56-
}
57-
58-
hotBlocks := int(hotPct) * p.barWidth / 100
59-
if hotBlocks > p.barWidth {
60-
hotBlocks = p.barWidth
61-
}
62-
coldBlocks := p.barWidth - hotBlocks
63-
if coldBlocks < 0 {
64-
coldBlocks = 0
65-
}
66-
67-
hotBar := strings.Repeat("█", hotBlocks)
68-
coldBar := strings.Repeat("█", coldBlocks)
69-
70-
// 空行 + 标题
71-
fmt.Fprintln(p.w)
72-
fmt.Fprintln(p.w, " 文件分布")
73-
74-
// 进度条
75-
if p.enabled {
76-
fmt.Fprintf(p.w, " [%s%s%s%s%s]\n",
77-
AnsiOrange, hotBar, AnsiBlue, coldBar, AnsiReset)
78-
} else {
79-
fmt.Fprintf(p.w, " [%s%s]\n", hotBar, coldBar)
80-
}
81-
82-
// 统计文字
83-
hotText := fmt.Sprintf("hot %d%% (%s files, %s)", hotPct, formatInt(int(hotFiles)), formatBytes(hotBytes))
84-
coldText := fmt.Sprintf("cold %d%% (%s files, %s)", 100-hotPct, formatInt(int(coldFiles)), formatBytes(coldBytes))
85-
86-
if p.enabled {
87-
fmt.Fprintf(p.w, " %s%s%s %s%s%s\n",
88-
AnsiOrange, hotText, AnsiReset,
89-
AnsiBlue, coldText, AnsiReset)
90-
} else {
91-
fmt.Fprintf(p.w, " %s %s\n", hotText, coldText)
92-
}
93-
}
94-
95-
// Syncing 在单行实时刷新 hot 同步进度。
46+
// Syncing 在单行实时刷新同步进度。
47+
// 格式:[████████░░] 98% (1,210/1,232) file.go
9648
func (p *ProgressUI) Syncing(done, total int, currentFile string) {
9749
if !p.enabled {
9850
return
@@ -104,18 +56,38 @@ func (p *ProgressUI) Syncing(done, total int, currentFile string) {
10456
filled := pct * p.barWidth / 100
10557
bar := strings.Repeat("█", filled) + strings.Repeat("░", p.barWidth-filled)
10658

107-
fmt.Fprintf(p.w, "\r\033[2K 同步中… [%s] %3d%% (%s/%s) %s",
108-
bar, pct, formatInt(done), formatInt(total), truncate(currentFile, 35))
59+
fmt.Fprintf(p.w, "\r\033[2K[%s] %3d%% (%s/%s) %s",
60+
bar, pct, formatInt(done), formatInt(total), truncate(currentFile, 20))
10961
}
11062

111-
// SyncDone 结束同步阶段,输出 100% 并换行
63+
// SyncDone 结束同步,定格 100%。
11264
func (p *ProgressUI) SyncDone(done, total int) {
11365
if p.enabled {
11466
bar := strings.Repeat("█", p.barWidth)
115-
fmt.Fprintf(p.w, "\r\033[2K [%s] 100%% (%s/%s) 同步完成\n",
67+
fmt.Fprintf(p.w, "\r\033[2K[%s] 100%% (%s/%s) 同步完成\n",
11668
bar, formatInt(done), formatInt(total))
11769
} else {
118-
fmt.Fprintf(p.w, " 热同步完成,共 %s 个文件\n", formatInt(done))
70+
fmt.Fprintf(p.w, " 同步完成 %s 个文件\n", formatInt(done))
71+
}
72+
}
73+
74+
// Distribution 输出 hot/cold 分布摘要(一行,不刷新)。
75+
func (p *ProgressUI) Distribution(hotFiles, hotBytes, coldFiles, coldBytes int64) {
76+
total := hotFiles + coldFiles
77+
hotPct := int64(0)
78+
if total > 0 {
79+
hotPct = hotFiles * 100 / total
80+
}
81+
82+
hotText := fmt.Sprintf("hot:%d%% %sf %s", hotPct, formatInt(int(hotFiles)), formatBytes(hotBytes))
83+
coldText := fmt.Sprintf("cold:%d%% %sf %s", 100-hotPct, formatInt(int(coldFiles)), formatBytes(coldBytes))
84+
85+
if p.enabled {
86+
fmt.Fprintf(p.w, " %s%s%s %s%s%s\n",
87+
AnsiOrange, hotText, AnsiReset,
88+
AnsiBlue, coldText, AnsiReset)
89+
} else {
90+
fmt.Fprintf(p.w, " %s %s\n", hotText, coldText)
11991
}
12092
}
12193

0 commit comments

Comments
 (0)