Skip to content

feat(icons): Docker 面板自报标签页图标 - #33

Merged
joesdu merged 5 commits into
mainfrom
dev
Sep 11, 2026
Merged

feat(icons): Docker 面板自报标签页图标#33
joesdu merged 5 commits into
mainfrom
dev

Conversation

@joesdu

@joesdu joesdu commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

上一趟(#31)给 Redis / 串口 / Telnet / S3 填了标签页图标,Docker 面板是并库(#32)时才进来的,标签上顶的仍是所有插件共用的那个通用插头 —— 与另外四个的标签并排时,图标那一格等于白占。这个 PR 补齐它。

用了什么

Docker 的鲸鱼品牌标(DockerIcon.cs,实心、视框 1024,经 PanelOptions.Icon 交给宿主)。lucide 里没有 Docker,而「容器」在那套字形里只有 container(货运集装箱)与 box 之类的近似物 —— 摆在标签条上认不出是它。16px 深色底渲染核过:与 Redis / Serial / Telnet / S3 / SSH / SFTP 并排不会混。

几处取舍

  • 实心 + 视框 1024 两位都要报:视框不报会被按 lucide 的 24 缩放(放大四十多倍,屏幕上什么也没有);用描边去画实心图形得到的是它的轮廓线,而这个标本来就是"空心鲸鱼 + 实心集装箱",再描一圈边会糊成一团。
  • 不必声明 F0 / F1:原图只有一段 path,鲸鱼肚子与那只眼睛靠反向绕向挖洞,evenodd 与 nonzero 两种填充规则画出来一模一样(两种各渲染一遍比过)。
  • 刻意不与面板头上那只鲸鱼统一:Docker.whale(DockerTheme.axaml)是描边、视框 24 的手绘字形,与面板里其余几十个 Docker.* 图标成套,它要的是融进界面;标签页这一个要的是在挤满标签的标签条上被一眼认出来。理由写进 DockerIcon 的注释,免得下一个人按「能统一就统一」的直觉合并掉。
  • minSdkVersion 不动(仍是 2.0.0):理由同 feat(icons): 四个第一方插件自报标签页图标 #31 —— 发布出来的 SDK 2.0.4 里 VelaPluginApi.SdkVersion 仍是 "2.0.2",而宿主的兼容闸门比的正是它,声明 2.0.4 会把插件标成 Incompatible。

测试

两条:headless 会话里把那一千多字符的路径真解析一遍并核边界 —— 宿主那边解析失败是静默退回通用插头,不报错也不抛,没有这一层守卫,路径里打错一个字符谁也不会发现;另一条核「实心 + 自报视框」两位。

⚠️ headless 宿主的 FillContains 结果不可信(整行报 true,连视框外的点也报),所以"镂空还是不是洞"验不了,这一条写在测试注释里,免得下一个人再试一遍。

验收

  • dotnet build VelaShell.Plugins.slnx -c Debug -warnaserror —— 0 警告 0 错误
  • dotnet test VelaShell.Plugins.slnx -c Debug —— Telnet 26 / 串口 77 / Docker 146 / S3 104 / Redis 155 通过,Redis 另有 84 条因本机没有 127.0.0.1:6379 跳过(既有口径)
  • dotnet build plugins/VelaShell.Plugin.DockerPanel -t:PackVpx —— 出包正常

🤖 Generated with Claude Code

https://claude.ai/code/session_01GXH7SxQFdYtgMWgfjY5TkS

joesdu and others added 5 commits September 11, 2026 17:17
SDK 2.0.4 带的是标签页图标那一面(PluginIcon:ProtocolDescriptor.Icon /
WorkspaceDescriptor.Icon / PanelOptions.Icon 三处共用一个类型),下一个提交要用它。

顺手把文件末尾缺的那个换行补回来(.editorconfig 的 insert_final_newline)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GXH7SxQFdYtgMWgfjY5TkS
宿主不维护「插件 id → 图标」的对照表(那张表第三方插件永远进不去),图标由插件经
描述符自报;不报就是所有插件共用的那个通用插头 —— 同时开着 Redis、S3、串口三种会话时,
三条标签顶着同一个插头,图标那一格等于白占。

- Redis:品牌标(RedisIcon.cs,实心、视框 1030)。lucide 里没有 Redis,拿「数据库」
  之类的近似字形凑,换来的是一排标签里认不出哪个是它。路径是原始 SVG 三段 path 的顺序
  拼接,前两段末尾导出工具留下的空指令 m0 0 去掉了。三段绕向相反,顶面那几处镂空在
  evenodd 与 nonzero 下画出来一模一样,因此不必声明 F0 / F1。
- 串口:lucide usb-c-port。
- Telnet:lucide ethernet-port。刻意不用终端字形 —— 宿主的 SSH 标签画的就是
  square-terminal,而标签上只有 12–16px,两个终端框并排等于没分。以太网口与串口的
  USB-C 口成对:一个网口一个串口,一眼分得开。
- S3:lucide cloud,会话标签页与桶管理器 / 对象检视器两扇工具窗共用(宿主现在也给
  窗口标题栏画这个图标)。不用存储那一类字形:宿主给 SFTP / FTP 画的正是 hard-drive,
  而三者并排躺在同一条标签条上。

测试:四个插件各钉住自己那份图标(路径、描边/实心、视框);RedisIconTests 另在 headless
会话里把那 2 KB 路径真解析一遍并核边界 —— 宿主那边解析失败是**静默**退回通用插头,
不报错也不抛,没有这一层守卫,路径里打错一个字符谁也不会发现。Telnet 与 S3 之前没有激活
测试,这次一并补上描述符那几条。

注:暂不在 plugin.json 声明 minSdkVersion "2.0.4" —— 发布出来的 SDK 2.0.4 里
VelaPluginApi.SdkVersion 仍是 "2.0.2"(2.0.3 / 2.0.4 两次发版没抬那个常量),而宿主的
兼容闸门比的正是它,声明了会把四个插件全部标成 Incompatible。等 SDK 那边把常量抬上去再补。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GXH7SxQFdYtgMWgfjY5TkS
VelaShellLabs/VelaShell.Plugin.DockerPanel 就此并入本仓库,与 Redis / S3 /
Telnet / 串口一样作为第一方插件统一维护、统一发版;原仓库封存。

插件 111 个文件进 plugins/VelaShell.Plugin.DockerPanel/,测试 13 个进
tests/VelaShell.Plugin.DockerPanel.Tests/。设计文件 .pen 与对着它 19 个画板
逐条核对的 plan.md 一并跟着插件走 —— 与 Redis 的 .pen 同一个位置。

## 并进来要改的三处

- **csproj 按本仓库口径重写**:删掉 TargetFramework / Nullable / 版本号这些
  由根 Directory.Build.props 统一给的属性(根 Directory.Build.targets 的
  CheckTargetFramework 本来就不许 csproj 自己写 TargetFramework);NuGet 版本
  改走中央包管理,Avalonia.AvaloniaEdit 12.0.0 记进 Directory.Packages.props。
- **plugin.json 的 version 0.3.1 → 2.0.0**:本仓库是一趟统一发布列车,
  版本落点由 scripts/Set-Version.ps1 统一写。Set-Version.ps1 动态枚举
  plugins/*/plugin.json,不用改脚本;CI 的版本同步体检已验过。
- **AvaloniaEdit 的版本核对补进 Directory.Build.targets**(VELAP1001)。
  原仓库有这道闸,本仓库此前没有 —— 它漂了一个测试都不会红,要等用户打开
  compose.yaml 才炸。基准取 SDK 导出的 VelaSdkPinnedAvaloniaEditVersion。

## MicroCom.Runtime 泄漏:一条两道防线都漏掉的路

按 plugins/README.md 的"不要写 ExcludeAssets"把那条删掉之后,
MicroCom.Runtime.dll 落进了插件目录 —— 构建 0 错 0 警告,包照常打出来。

那条纪律的前提是"插件不直接引 Avalonia 包"(前四个都不引)。DockerPanel 是
第一个引的,而 SDK 的 VelaExcludeSharedRuntimeAssets 排得掉 Avalonia.AvaloniaEdit
自己的运行时资产,排不掉它带进来的传递依赖:AvaloniaEdit → Avalonia →
MicroCom.Runtime。它不以 Avalonia 打头,于是装载器的共享前缀判定不认它
(插件会加载自己那一份),CI 那条只认 Avalonia*/VelaShell.PluginSdk.dll 的
泄漏体检也放它过去。

ExcludeAssets="runtime" 因此显式写回 csproj,并把 MicroCom.Runtime.dll 补进
ci.yml 与 release.yml 的泄漏体检 —— 下次别再靠肉眼逐个文件看出来。
plugins/README.md 新增一节写清这个例外。

## 确认闸门的三条测试:并库后 5 次里红 4 次

ConfirmGate.AskAsync 经 Ui.Post 落状态,而 Ui.Post 只在调用方已经在 UI 线程上
时才同步执行。生产路径上闸门总是从 UI 线程打开的,这条成立;测试线程不是,
那次 Post 排进一个没人泵的队列,Request 永远不落。

原仓库没露面是因为它没有 TestTimeout:MSTest 在当前线程上直接跑测试体,
全程一条线程,谁先碰 Dispatcher.UIThread 谁就是"UI 线程"。本仓库的
tests/velashell.runsettings 设了 60 秒单测超时,带超时的测试体被放到线程池
线程上跑 —— 每条测试落在哪条线程不再确定,这条隐式依赖就暴露了。

修的是测试而不是产品代码:给测试工程加 headless 宿主
(DockerPanelHeadlessApp,刻意不装主题 —— 这几条一个控件都不构造),
四条 Gate 测试经 HeadlessUnitTestSession.Dispatch 跑在真的在泵的 UI 线程上。
与 Redis 面板测试同一条路子。单独重跑 5 次全绿。

## 顺带扫掉的过期内容

plugins/README.md 的"规划中"两条都已落地(串口早已存在,容器插件就是本次);
插件表里的 HelloWorld 行、"复制 HelloWorld 目录"的新建步骤在 d3bf101 删掉那个
示例插件之后就一直悬着,一并改掉。

## 验证

dotnet build / dotnet test 全解决方案:0 错 0 警告,745 通过 84 跳过 0 失败。
Bundle + PackAllVpx 全跑通,泄漏体检过,五个 .vpx 齐(velashell.dockerpanel-2.0.0.vpx
616 KB)。Set-Version.ps1 -Check 通过。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbiPeXZjVhEmnDeNeEb28p
前四个插件上一趟已经填过图标,Docker 面板是并库时才进来的,标签上顶的仍是所有插件
共用的那个通用插头 —— 与 Redis / S3 / 串口的标签并排时,图标那一格等于白占。

用品牌标而不是 lucide 的某个字形:lucide 里没有 Docker,而「容器」在那套字形里只有
container(货运集装箱)与 box 之类的近似物,摆在标签条上认不出是它。实心、视框 1024,
两位都要报给宿主 —— 视框不报会被按 lucide 的 24 缩放(放大四十多倍,屏幕上什么也没有),
描边去画实心图形得到的是它的轮廓线。

原图只有一段 path,鲸鱼肚子与那只眼睛靠反向绕向挖洞,evenodd 与 nonzero 两种填充规则
画出来一模一样(两种各渲染一遍比过),因此不必在路径上声明 F0 / F1。

**刻意不与面板头上那只鲸鱼统一**:Docker.whale 是描边、视框 24 的手绘字形,与面板里其余
几十个 Docker.* 图标成套,它要的是融进界面;标签页这一个要的是在挤满标签的标签条上被
一眼认出来。理由写进 DockerIcon 的注释,免得下一个人按「能统一就统一」的直觉合并掉。

测试两条:headless 会话里把那一千多字符真解析一遍并核边界(宿主那边解析失败是**静默**
退回通用插头,不报错也不抛),另核「实心 + 自报视框」两位。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GXH7SxQFdYtgMWgfjY5TkS
main 上的 #31 / #32 都是 squash 合并,新造的提交与 dev 这边的原提交没有共同历史 ——
于是 Docker 面板那几个文件在合并基线看来是**两边各自新增**的(add/add),内容不同的
两个(PluginMain.cs / README.md)就成了冲突。两边逐字节比过:main 的那两份与 dev 上
「我改之前」的那两份完全一致,因此冲突取 dev 这一侧 = 保留本分支新加的图标改动。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GXH7SxQFdYtgMWgfjY5TkS
@joesdu
joesdu merged commit 1a77ff8 into main Sep 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant