Documentation
¶
Overview ¶
Package kit 的进度条行渲染 + diff 输出。
渲染模型(学 indicatif 的 diff 渲染,消除 mpb 的 \e[J 整块清屏闪烁):
- 每帧把所有 bar 渲染成行字符串数组
- 与上一帧逐行重写:光标上移 + \r(回车行首) + \e[K(清当前行,非整屏) + 写新行
- 整个 diff 结果单次 Write,终端原子渲染,无中间空白态
关键:\e[K 只清当前行(不像 mpb 的 \e[J 清整屏),无空白帧 → 无闪烁。
Package kit 是 kite 命令层的共享工具包。
封装 engine 调用、登录会话读写、输出与确认交互,各领域命令包(song/playlist/...) 只依赖本包,不直接碰 engine 细节。
Package kit config.go 实现 config.toml 的加载/校验/写入(PRD-0017)。
设计原则(决策见 CONTEXT.md「不可逆决策」表):
- config key 只收「已存在 flag 或硬编码默认值」(当前六项,见 configKeys);
- 文件不存在 = 正常态(全部内置默认);文件存在但坏 = 硬错误,静默回退会掩盖用户设置;
- set 写盘前完成全部校验,读取侧复用同一套校验(手改文件同样被拦);
- tmp+rename 原子写,文件 0600、目录 0700(与 session 一致)。
Package kit 的进度条视觉实现 —— 盲文点阵 + true color 渐变。
设计核心:每个盲文字符是 2×4 的 8 点位像素(U+2800..U+28FF),一个终端 cell 承载 8 个亚像素。进度边界用「逐点点亮」平滑过渡,而非整块跳变;配合 24-bit true color 渐变,达到接近 LED 点阵屏的细腻度。
点位编码(Unicode U+2800 基址 + 8 位偏移):
┌──┬──┐ │1 │4 │ 1=0x01 2=0x02 3=0x04 4=0x08 │2 │5 │ 5=0x10 6=0x20 7=0x40 8=0x80 │3 │6 │ rune(0x2800 | dots) │7 │8 │ ⣿ = 全 8 点亮(0xFF),⠀ = 全灭(0x00) └──┴──┘
布局原则(单行):
- 已完成段:全亮 ⣿,渐变色填充
- 边界字符:按亚进度点亮 0..8 个点,平滑锯齿
- 未完成段:稀疏锚点(每隔几列底部一个点),形成「轨道」指引而非空白
Package kit 的进度数值格式化纯函数。
所有函数无副作用、无 I/O,期望值参照 PRD-0013 的进度显示示例:
- 字节:1024 进制(KiB/MiB/GiB),显示用 KB/MB/GB(PRD 行 80:3.4 MB)
- 时长:mm:ss,≥1h 用 h:mm:ss(PRD 行 80:0:01)
- 速度:字节/秒,同字节单位(PRD 行 80:1.8 MB/s)
- 百分比:整数%,分母零时 0%(PRD 行 80:62%)
位置参数 helper:--id flag 与位置参数(args[0])的统一解析。
动机(issue #24 / PRD 便捷性):`song download 347230` ≡ `song download --id 347230`, 对齐 git/kubectl 习惯。规则:位置参数与 --id 互斥(同时指定 → 用法错误)。
Package kit 的自实现进度条渲染器。
替代 mpb:直接控制每个字节,用 diff 渲染消除闪烁、EWMA 平滑速度、 假时钟注入确定性测试、✓ 完成态(Evil Martians/cli.r-lib 标准)。
核心循环(学 indicatif 的 steady tick):
- Start() 启动 100ms ticker goroutine,每次 tick 渲染一帧
- 渲染 = 渲染所有 bar → 与上一帧 diff → 单次 Write(原子,无空白帧)
- 独立于 Incr 调用频率:即使某 bar 不增长,spinner 照转、ETA 照更新
Package kit proxy.go 实现代理 URL 的解析与校验(PRD-0018)。
flag(--proxy)与 config(proxy key)共用本函数:单一校验真相。 空串 = 未设置(返回 nil,nil,回落环境变量层——那是 Go 默认 transport 的 ProxyFromEnvironment 行为,本包不做任何注入)。
Package kit 的人类可读渲染层。
RenderHuman 是输出层唯一 seam:proto.Message → 文本的纯函数。 含非空 repeated message 字段的响应渲染为分段表格,否则渲染为键值对。
Spinner 独立的不可量化等待指示器(缓冲、初始化)。
用途:进度不可量化时显示转圈(PRD-0013 行 164「缓冲中 ⠼ 4.2s / 5s」)。 与 Progress 的区别:Progress 管 N 个可量化 bar,Spinner 是单行不可量化等待。
三态抑制(遵 PRD-0012 输出层规矩):
- TTY:渲染 spinner 帧转圈到 err writer(stderr)
- 非 TTY(管道):完全静默,不刷屏(管道里转圈是垃圾)
- --json:由调用方不创建 Spinner 实现(此处不查 JSON,保持单一职责)
Start/Stop API:Start 启动 tick 转圈,Stop(msg) 停止并输出终态行(覆盖 spinner)。
Index ¶
- Constants
- Variables
- func AnnotateRpcs(c *cobra.Command, rpcs ...string)
- func ConfigDir() (string, error)
- func ConfigKeys() []string
- func ConfigPath() (string, error)
- func Exec[Req, Resp any](k *Kit, ctx context.Context, ep *engine.Endpoint[Req, Resp], req Req) (Resp, error)
- func FilenameTemplatePlaceholders() []string
- func HistoryPath() (string, error)
- func IsKnownConfigKey(key string) bool
- func MaskCookie(cookie string) string
- func MountCompletion(root *cobra.Command, k *Kit)
- func ParseProxyURL(raw string) (*url.URL, error)
- func ParseRpcs(s string) []string
- func PrintJSON(msg proto.Message) error
- func PrintRaw(raw json.RawMessage)
- func RenderBar(current, total int64, width int, color bool) string
- func RenderExec[Req any, Resp proto.Message](k *Kit, ep *engine.Endpoint[Req, Resp], req Req) error
- func RenderHuman(msg proto.Message) string
- func ResolveID(flagID int64, args []string) (int64, error)
- func ResolveKeyword(flagKeyword string, args []string) (string, error)
- func SessionPath() (string, error)
- func SetConfigKey(key, value string) error
- type Bar
- type BarState
- type Config
- type Kit
- func (k *Kit) ClearSession() error
- func (k *Kit) ConfirmFatal(action string) error
- func (k *Kit) ConfirmWrite(action string) (bool, error)
- func (k *Kit) CookieCtx() context.Context
- func (k *Kit) CurrentCookie() string
- func (k *Kit) HTTPClient() *http.Client
- func (k *Kit) HumanOutput() bool
- func (k *Kit) LoadSession() (Session, error)
- func (k *Kit) NewProgress() *Progress
- func (k *Kit) NewSpinner(label string, opts ...SpinnerOption) *Spinner
- func (k *Kit) OutWriter() io.Writer
- func (k *Kit) RawDo(ctx context.Context, meta engine.Meta, params map[string]any) (json.RawMessage, string, error)
- func (k *Kit) RecallPool() *recall.Pool
- func (k *Kit) Record(id int64, name, artist string, src recall.Src)
- func (k *Kit) Render(msg proto.Message) error
- func (k *Kit) RequireLogin() error
- func (k *Kit) SaveSession(sess Session) error
- func (k *Kit) SetRecallPool(p *recall.Pool)
- func (k *Kit) UseProxy(u *url.URL, source string)
- func (k *Kit) WantJSON() bool
- func (k *Kit) Warnf(format string, args ...any)
- type Progress
- type ProgressOption
- type Session
- type Spinner
- type SpinnerOption
Constants ¶
const ( ProxySourceFlag = "flag" // --proxy 显式覆盖 ProxySourceConfig = "config" // config.toml 的 proxy key )
ProxySource 常量:UseProxy 注入时的来源标记,doctor 据此展示解析链。
const RpcsAnnotationKey = "kite/rpcs"
RpcsAnnotationKey 标记命令消费的 grpc rpc。
值为短形式 "Service/Method"(如 "SongService/GetSongDetail"),多 rpc 用逗号分隔 (如 "SongService/GetSongURL,SongService/GetSongDetail")。值存在(含空串)即表示 命令已被审视;无此 key 的叶子命令会被漏标守护捕获。
Variables ¶
var ErrCancelled = errors.New("用户已取消")
ErrCancelled 用户取消哨兵错误,Execute 静默吞掉(退出码 0,不打错误信息)。
var ErrNotLogin = errors.New("未登录")
ErrNotLogin 未登录哨兵错误,Execute 据此映射退出码 3。
var ErrUsage = errors.New("用法错误")
ErrUsage 用法哨兵错误(flag 解析失败、非 TTY 写操作无 --yes 等),Execute 映射退出码 2。
Functions ¶
func AnnotateRpcs ¶
AnnotateRpcs 给命令打 rpc 注解:把 rpcs 列表写入 c.Annotations[RpcsAnnotationKey]。 rpcs 为空时写入空串(标记「已审视,无 rpc」),用于本地命令(recent/doctor/...)。 供各组 NewCommand 在 AddCommand 后集中打标,避免散落。
func ConfigKeys ¶ added in v0.2.0
func ConfigKeys() []string
ConfigKeys 返回合法 key 列表(顺序即 config get 无参列表顺序,单一真相)。
func ConfigPath ¶ added in v0.2.0
ConfigPath 返回 config.toml 路径 <ConfigDir>/config.toml(只算路径不创建)。
func Exec ¶
func Exec[Req, Resp any](k *Kit, ctx context.Context, ep *engine.Endpoint[Req, Resp], req Req) (Resp, error)
Exec 执行一个声明式 endpoint(注入当前 cookie)。 是 service 包 executeOverride 的等价物:复制而非 import, 因为 service 包还装配了 gRPC 相关类型,这里只要纯执行逻辑。
func FilenameTemplatePlaceholders ¶ added in v0.2.0
func FilenameTemplatePlaceholders() []string
FilenameTemplatePlaceholders 返回 filename_template 的合法占位符(config schema 的 单一真相)。songdl 的执行侧(songdl.FormatFilename)与这里由 songdl 包的守护测试保持同步——公共层只定义 schema,不夹带下载领域逻辑。
func HistoryPath ¶
HistoryPath 返回召回池事件流文件路径 <ConfigDir>/history.jsonl。
召回池(#G,PRD-0014)尚未落地;本函数为它预留 seam,与 session 同源派生, 使 #G 落盘路径决策零额外成本。当前无调用者。
func IsKnownConfigKey ¶ added in v0.2.0
IsKnownConfigKey 判断 key 是否在 configKeys 枚举内(config get/set 命令层校验用)。
func MaskCookie ¶
MaskCookie 把 cookie 各段的值脱敏:保留首尾各 8 字符,中间省略; 短值(≤20 字符)整体打码。用于 login-status 人类模式防止凭证泄露。
func MountCompletion ¶
MountCompletion 在 root 构造后一次树遍历统一挂载参数补全。
表驱动(flag 名 → 数据源):
- "id"(单值歌曲 ID)→ 召回池候选(带「歌名 - 艺人」描述列),按 frecency 排序。
- "level"/"area"/"op" 等 → 固定枚举。
新命令(A 类 rpc 1:1 接入)带同名 flag 时自动获得补全,零登记。个别命令异构需求 可在 MountCompletion 后就地 RegisterFlagCompletionFunc 覆盖——本函数检测已注册 的 flag 会跳过,尊重命令就地覆盖。
**补全绝不触发网络**:--id 候选全部来自召回池(内存优先,磁盘兜底),枚举是静态值 (CONTEXT.md 补全只走缓存段)。
调用点:NewRootCommand 末尾(root 构造后、return 前)。
func ParseProxyURL ¶ added in v0.2.0
ParseProxyURL 校验并解析代理地址。
- 空串 → (nil, nil):未设置,不注入任何代理(环境变量层自然生效)。
- scheme 必须显式写(http:// / https:// / socks5://):裸 host:port 报错并 引导补前缀——猜测默认值是文档陷阱。
- host 非空;端口可缺省(用 scheme 默认端口)。
func PrintRaw ¶
func PrintRaw(raw json.RawMessage)
PrintRaw 直接 pretty 打印原始 JSON(动态 path 接口未经 proto 映射时用)。
func RenderBar ¶
RenderBar 渲染单条盲文进度条为字符串(纯函数,无副作用,无 I/O 依赖)。
current/total 决定进度;width 是盲文条占的字符数;color 控制是否输出 true color。 total ≤ 0 时按 0% 处理。视觉:已完成段全亮+渐变(青绿→天青→暖橙), 边界水位上涨(8 级亚像素),未完成段轨道锚点。
func RenderExec ¶
RenderExec 执行声明式 endpoint 并按三态规则输出(读命令通用)。
func RenderHuman ¶
RenderHuman 把 proto 响应渲染为人类可读文本(纯函数)。
含非空 repeated message 字段 → 每个字段一段表格(多段带 == 字段名 (数量) == 小标题); 否则按键值对逐行渲染,嵌套子结构退化为紧凑 JSON。
func ResolveID ¶
ResolveID 从 --id flag 或位置参数 args[0] 解析 id(歌曲/歌单通用)。
规则(PRD-0013 便捷性 / CONTEXT.md 位置参数术语):
- flagID 非 0 且 args 有值 → ErrUsage「不能同时指定 --id 和位置参数」
- 两者都缺 → ErrUsage「缺少 id」
- 仅 flagID → 返回 flagID
- 仅位置参数 → 解析为 int64;非数字 → ErrUsage
位置参数不进 tab 补全(补全只走召回池,见 CONTEXT.md「补全只走缓存」)。 调用方需先把 cobra.Args 设为 cobra.MaximumNArgs(1) 并 drop MarkFlagRequired("id")。
func ResolveKeyword ¶
ResolveKeyword 从 --keyword flag 或位置参数 args[0] 解析关键词(search 用)。 规则同 ResolveID:flag 与位置参数互斥(同时指定 → ErrUsage);两者都缺 → ErrUsage。
func SessionPath ¶
SessionPath 返回会话文件路径 <ConfigDir>/session.json。
func SetConfigKey ¶ added in v0.2.0
SetConfigKey 校验并写入单个 key(整文件重写,tmp+rename 原子落盘)。 只落显式设置过的 key——未设置的留空,读取侧回退内置默认, 「来源」列(config get 无参列表)才有区分度。 坏文件在手时同样报错——不允许绕过校验覆盖写。
Types ¶
type Bar ¶
type Bar struct {
Total int64 // 总量(字节数)
Current int64 // 已完成量
Label string // 显示名(如 "Beyond - 海阔天空")
State BarState // 当前状态
IsTotal bool // 是否总 bar(显示 ETA 而非速度)
// contains filtered or unexported fields
}
Bar 单个进度条的状态。并发安全:字段由自有 mu 保护, 写走 Incr/Complete/Fail,渲染读走 snapshot()。
type Config ¶ added in v0.2.0
type Config struct {
Level int // 默认音质,1=standard 2=exhigh 3=lossless 4=hires
Output string // "table" | "json"
DownloadDir string // 下载目录(--out)
FilenameTemplate string // 文件名模板,空 = {artist} - {title}
Workers int // playlist download 并发数(--workers)
Proxy string // 代理地址,空 = 未设置(回落环境变量层,PRD-0018)
}
Config 是用户偏好的生效值集合(字段与 configKeys 一一对应)。 零值不可直接使用(Workers=0 非法), 一律经 DefaultConfig() 或 LoadConfig() 获得。
func DefaultConfig ¶ added in v0.2.0
func DefaultConfig() Config
DefaultConfig 返回内置默认值(与各 flag 的硬编码默认对齐,独立真相)。
func LoadConfig ¶ added in v0.2.0
LoadConfig 读取 config.toml 并校验。文件不存在返回内置默认(正常态,非错误); 文件存在但解析失败/校验不过返回 error(硬错误,调用方应拒绝执行,PRD-0017)。
type Kit ¶
type Kit struct {
// JSON 为 true 时强制 protojson 输出(全局 --json);管道时无需设置,自动回退。
JSON bool
// Yes 为 true 时写操作跳过交互确认(全局 --yes)。
Yes bool
// Config 是生效的用户偏好(PRD-0017)。New() 填内置默认;root 装配时
// 用 LoadConfig() 的结果覆盖(坏文件在 root 层硬错误)。Output 参与
// Render 三态:优先级链 --json > 非TTY自动JSON > config > 内置默认。
Config Config
// ProxyURL 是当前生效的显式代理(nil = 未注入,环境变量层自然生效);
// ProxySource 记录来源("flag" / "config" / "")供 doctor 展示解析链(PRD-0018)。
ProxyURL *url.URL
ProxySource string
// Out 结果输出 writer,默认 os.Stdout(测试可替换)。
Out io.Writer
// Err 警告/进度输出 writer,默认 os.Stderr(测试可替换)。
// 进度类(ProgressBar/Spinner)和一次性警告(Warnf)都走这里,
// 不污染 stdout(结果数据流),脚本管道友好。
Err io.Writer
// contains filtered or unexported fields
}
Kit 持有命令层共享的运行时依赖与全局输出状态。
func New ¶
func New() *Kit
New 创建 Kit。engine 无缓存、无 session 池,纯转发到网易云。 召回池默认指向 kit.HistoryPath()(PRD-0015 #44 可注入路径)。
func (*Kit) ConfirmFatal ¶
ConfirmFatal 写操作确认的一行化形式:确认返回 nil; 取消返回 ErrCancelled(Execute 退出码 0);非交互未授权返回 ErrUsage(退出码 2)。 13 个写命令调用点统一用这个,不再重复 ok/err 三行判断。
func (*Kit) ConfirmWrite ¶
ConfirmWrite 写操作前确认。
三态:--yes 直通;stdin 非 TTY 且无 --yes 返回用法错误(退出码 2); TTY 提示 y/N,取消返回 false(打印「已取消」,退出码 0)。 提示走 stderr,结果输出不被污染。
func (*Kit) CurrentCookie ¶
CurrentCookie 返回当前生效的 cookie: 环境变量 NETEASE_COOKIE 优先,其次本地会话文件。
func (*Kit) HTTPClient ¶ added in v0.2.0
HTTPClient 返回直连类路径(音频下载等)应使用的 client:显式代理时 与 engine 同一代理决策派生;未注入时退回 http.DefaultClient(其默认 transport 保留 ProxyFromEnvironment 环境变量行为)。
func (*Kit) HumanOutput ¶
HumanOutput 当前是否走人类可读渲染(WantJSON 的否定)。 命令需要按输出形态调整内容时用(如 login-status 脱敏),与 Render 同源。
func (*Kit) LoadSession ¶
SessionPath/ConfigDir/HistoryPath 见 paths.go(同包,唯一路径 seam)。
LoadSession 读本地会话。文件不存在或损坏时返回 error。
读前惰性触发旧路径迁移:首次发现新路径无文件时,把 ~/.kite/session.json 搬到新路径(若旧路径存在),见 migrateLegacySession。迁移失败不阻塞——按新路径 无文件处理(未登录)。
func (*Kit) NewProgress ¶
NewProgress 创建进度条渲染器,内部封装三态规矩(PRD-0012 输出层)。
命令层调 k.NewProgress() 即可,不用自己判断 TTY/--json:
- --json:输出走 io.Discard,完全静默(结果走 protojson,进度文本是污染)。
- 非 TTY(管道):tty=false,Start 不启动 tick,Wait 输出终态(脚本看最终结果)。
- TTY:正常渲染,输出走 err(stderr),探测终端宽度 + true color。
进度类走 stderr 不污染 stdout(结果数据流),与 Warnf 一致。
func (*Kit) NewSpinner ¶
func (k *Kit) NewSpinner(label string, opts ...SpinnerOption) *Spinner
NewSpinner 创建转圈指示器,内部封装三态规矩(同 NewProgress)。
- --json:返回的 Spinner 输出走 io.Discard,完全静默。
- 非 TTY:tty=false,Start 无操作,Stop 终态仍输出(有用信息)。
- TTY:正常转圈,输出走 err(stderr)。
opts 透传给底层 NewSpinner(如 WithSpinnerLabelFunc 动态 label)。
func (*Kit) OutWriter ¶
OutWriter 返回结果输出 writer(导出版本,供命令层直接写 stdout)。 大多数命令走 Render(自动三态);少数非 proto 结果(如 download 文件信息) 用这个直接写,配合 --json 自行处理双态。
func (*Kit) RawDo ¶
func (k *Kit) RawDo(ctx context.Context, meta engine.Meta, params map[string]any) (json.RawMessage, string, error)
RawDo 执行一个 Meta + 参数 形式的原始调用(动态 path / 写接口用)。
func (*Kit) RecallPool ¶
RecallPool 返回召回池(供 recent/补全消费)。无池返回 nil。
func (*Kit) Record ¶
Record 把一个成功消费的歌曲事件记入召回池(隐式埋点)。
机制选型(PRD-0014 G 片段,方案 c):kit 层提供统一的 Record helper,命令层在 --id 成功消费后显式调用一行。kit.Exec 是 generic,拿不到具体 id/name 字段 (SongId 在各 proto req struct 里),无法在 generic 层自动埋点;故选择显式 helper 而非 (a) interface+wrapper(要为每个 song req 写 wrapper)或 (b) endpoint 层埋点 (改 engine 抽象,被 service 包共用,高风险)。决策记录在 CONTEXT.md。
**只记成功**:调用方负责仅在命令成功(且写操作经 --yes 确认)后调用。 失败/取消不调本函数。Record 自身失败不阻塞主命令——仅 Warnf 告警(召回池是 辅助设施,损坏不应影响播放/下载主流程)。
歌曲类 id 进池(SongId);album/artist/playlist 的 id 不进召回池(召回池语义是 「歌曲」候选,补全 --id 用)。
func (*Kit) Render ¶
Render 按优先级链输出响应:WantJSON → protojson;其余 TTY → 人类可读。 管道 JSON 契约优先于 config(机器本地偏好不覆盖全局脚本契约)。
func (*Kit) RequireLogin ¶
RequireLogin 检查当前有可用的登录态(环境变量或本地会话文件)。 未登录返回包装了 ErrNotLogin 的错误,Execute 映射退出码 3。
func (*Kit) SaveSession ¶
SaveSession 把会话写盘(目录 0700 / 文件 0600)。
func (*Kit) SetRecallPool ¶
SetRecallPool 注入召回池(测试用:替换为指向临时目录的 Pool)。 生产代码用 New() 默认初始化的池,不调本方法。
func (*Kit) UseProxy ¶ added in v0.2.0
UseProxy 注入显式代理:engine 的 API 路径与下载路径同时生效(PRD-0018 的 双路径不变式)。source 记录来源供 doctor 展示。调用方(root)已保证 flag > config 解析出唯一 URL——决策单一来源。
type Progress ¶
type Progress struct {
// contains filtered or unexported fields
}
Progress 多 bar 渲染器。
func NewProgress ¶
NewProgress 创建渲染器。width 为 0 时按 80 兜底(调用方应传 term.GetSize 结果)。
渲染宽度预留最后一列(width-1):immediate-wrap 终端(Terminal.app 等) 在写满最后一列时立刻折行,行宽顶满会导致帧块每帧多占一行 → 逐帧抖动(闪烁); xterm 系 deferred-wrap 终端虽不受影响,预留一列对两类终端都安全。
func (*Progress) AddBar ¶
AddBar 添加一个进度条,返回引用供调用方 Incr/Complete。 初始状态 StateWaiting;首次 Incr 切到 StateActive。
func (*Progress) RenderForTest ¶
func (p *Progress) RenderForTest()
RenderForTest 测试钩子:同步渲染一帧(绕过 ticker,确定性)。 仅供测试用,生产代码用 Start/Wait 驱动异步渲染。 默认按中间帧处理(final=false),测试非 TTY 抑制行为。
func (*Progress) SetWidth ¶
SetWidth 运行期更新渲染宽度(终端拉伸响应),已 Start 时立即重绘一帧。 调用方负责监听 SIGWINCH 并传入新的 term.GetSize 结果; kit 不依赖 signal/fd,保持 io.Writer 层面的纯粹。
缩窄时支持 reflow 的终端(iTerm2/VTE/kitty/WezTerm 等)会把已绘制的 宽行重新折行,旧帧块行数变多,若仍按 len(prev) 上移会留残影(重复)。 因此置 widthDirty:下一帧按新宽度估算旧帧实际占用行数,上移 + \e[J 整块清。 已知取舍:不 reflow 的终端(xterm/Alacritty)缩窄时该估算会多清 块上方几行历史输出——重复残留与误清历史之间,选择适配多数现代终端。
type ProgressOption ¶
type ProgressOption func(*Progress)
ProgressOption 配置 NewProgress。
func WithProgressClock ¶
func WithProgressClock(now func() time.Time) ProgressOption
WithProgressClock 注入假时钟(测试用,保证 ETA 确定性)。
func WithProgressColor ¶
func WithProgressColor(c bool) ProgressOption
WithProgressColor 启用 true color 渐变。
func WithProgressWidthSource ¶
func WithProgressWidthSource(f func() int) ProgressOption
WithProgressWidthSource 轮询式宽度源:每帧渲染前查询终端宽度。
变化时按「去抖」处理:候选宽度连续稳定 3 帧(~300ms)才应用—— 拖动拉伸期间冻结渲染(旧块原地不动),松手后一次性清 reflow 残影 并重绘。为什么不去抖不行:部分终端(如 Wave)的 winsize 更新与 显示层 reflow 不同步,滞后窗口内任何基于行数估算的清除都会估错, 留下残影(重复),且拖动时每帧整块清绘在 Electron 管线下会闪烁。 冻结期间 Incr/EWMA 照常累积,只是画面暂停,拖拽本就短暂。 传 term.GetSize 的薄封装即可,非 TTY 无需设置。
type Session ¶
type Session struct {
Cookie string `json:"cookie"`
UserID int64 `json:"user_id,omitempty"`
SavedAt string `json:"saved_at"`
}
Session 是落盘的登录会话。cookie 属于敏感信息: 目录 0700、文件 0600,且不把完整 cookie 打印到终端。
type Spinner ¶
type Spinner struct {
// contains filtered or unexported fields
}
Spinner 单行转圈指示器。
func NewSpinner ¶
NewSpinner 创建转圈指示器。tty=false 时所有渲染静默(非 TTY 抑制)。
type SpinnerOption ¶
type SpinnerOption func(*Spinner)
SpinnerOption 配置 NewSpinner。
func WithSpinnerClock ¶
func WithSpinnerClock(now func() time.Time) SpinnerOption
WithSpinnerClock 注入假时钟(测试确定性)。
func WithSpinnerLabelFunc ¶
func WithSpinnerLabelFunc(fn func() string) SpinnerOption
WithSpinnerLabelFunc 动态 label:每帧调用 fn 取最新文本(如缓冲秒数/水位), 替代静态 label 与 elapsed 后缀。用于内容随外部状态变化的等待(PRD-0013 行 164 「缓冲中 ⠼ 4.2s / 5s」中的 4.2s 是 Player.Progress 的已缓冲量,不是耗时)。