OCI Image Format 规范深度解析:镜像格式标准的构成、运行工作流与在 Swarm Classic 中的落地实践
云原生后端微服务【免费下载链接】classicswarmSwarm Classic: a container clustering system. Not to be confused with Docker Swarm which is at https://github.com/docker/swarmkit项目地址https://gitcode.com/gh_mirrors/cl/classicswarm点击查看免费下载导读本文围绕当前仓库内嵌的 OCI Image Format Specification 项目位于 vendor/github.com/opencontainers/image-spec展开系统讲解 OCI 镜像格式的定位、从镜像到 Runtime Bundle 的标准运行工作流以及 Manifest、Index、Config、Descriptor 等核心数据结构同时结合 specs-go 的 Go 类型实现与 Swarm Classicclassicswarm集群调度中的实际调用链说明这一开放规范在真实容器编排系统中的落地方式。读完本文你将掌握 OCI 镜像格式的完整构成并能在源码层面追踪镜像平台信息如何驱动容器调度决策。一、OCI Image Format 项目定位软件交付的镜像格式标准OCI Image FormatOpen Container Initiative Image Format是一个开放标准项目其使命是创建并维护软件交付software shipping所用的容器镜像格式规范。它回答的是一个基础问题容器镜像到底应该长什么样、由哪些内容组成、如何被解析和复用。从当前仓库的 vendor.conf 可以看到classicswarm 将github.com/opencontainers/image-spec以指定 commitf03dbe35d449c54915d235f1a3cf8f585a24babe的形式锁定为构建依赖并在vendor/github.com/opencontainers/image-spec目录下内嵌了该项目的部分源码。这份 vendored 内容包含三块资产资产说明本仓库落地情况规范正文spec.md镜像格式的完整文字规范位于上游仓库未内嵌于 vendor 目录Go 类型specs-go规范结构的 Go 语言类型定义供开发者直接编解码完整内嵌于 specs-go 目录校验工具与 JSON Schemaschema用于 blob 内部intra-blob校验的验证工具与 JSON Schema未内嵌于本仓库 vendor 目录其中 Go 类型与校验工具明确要求兼容当前 Go release更早的 Go 版本不在支持范围内——这是消费该规范时需要注意的前提条件。在 specs-go/version.go 中可以看到该内嵌版本自报的规范版本号为1.0.0-rc5-dev由VersionMajor1、VersionMinor0、VersionPatch0与开发分支标识拼接而成这属于 v1.0.0 正式发布前的候选开发版本。二、运行 OCI 镜像的标准工作流从 Image 到 Runtime Bundle规范 README 中描述了一条贯穿镜像格式与运行时规范的完整工作流理解它是理解整个 OCI 生态的关键下载 OCI Image符合 OCI Image Format 的镜像 ↓ 解包unpack到磁盘上的 Runtime Filesystem Bundle ↓ 由 OCI Runtime 运行该 Bundle这条链路由两个互补的规范共同支撑OCI Image Format Spec定义镜像的打包格式保证镜像可以被下载、被校验、被解包OCI Runtime Spec合作项目定义如何运行已解包到磁盘上的 filesystem bundle即一个可运行容器的目录结构与其运行约束。支撑这套工作流的用户体验是容器引擎用户早已习惯的零附加参数直接运行镜像# Docker 引擎 docker run example.com/org/app:v1.0.0 # rkt 引擎 rkt run example.com/org/app,versionv1.0.0要支持这种 UXOCI Image Format 中必须携带足以在目标平台上启动应用的完整信息——包括启动命令command、参数arguments、环境变量environment variables等。这正是下面要讲的ImageConfig结构体所承载的内容Entrypoint、Cmd、Env、User、WorkingDir、ExposedPorts、Volumes、Labels、StopSignal等字段为运行时提供了启动容器的全部执行参数基准。三、镜像格式的四大核心对象结合 specs-go 源码逐层拆解OCI 镜像格式的核心设计思想是以内容寻址content-addressed的 Descriptor 为纽带将布局Layout、索引Index、清单Manifest、配置Config组织成一个可验证的层级结构。以下逐一结合本仓库 specs-go/v1 的 Go 类型定义展开。3.1 Descriptor 与 Platform一切引用的统一入口descriptor.go 定义了镜像格式中最基础、复用度最高的结构——Descriptor它描述目标内容的处置方式describes the disposition of targeted content字段JSON 键说明MediaTypemediaType目标对象的媒体类型标识该 blob 是 manifest、config、layer 还是其他内容Digestdigest目标内容的摘要由github.com/opencontainers/go-digest提供类型是内容寻址的核心Sizesize目标 blob 的字节大小URLsurls可从中下载该对象的 URL 列表可选Annotationsannotations关于目标内容的任意元数据可选Platformplatform目标镜像所运行的平台描述仅当引用的是 manifest 时使用Platform结构体精确刻画这个镜像跑在什么平台上type Platform struct { Architecture string // CPU 架构如 amd64、ppc64 OS string // 操作系统如 linux、windows OSVersion string // 操作系统版本可选如 10.0.10586 OSFeatures []string // 必需的 OS 特性可选如 Windows 的 win32k Variant string // CPU 变体可选如 ppc64le Features []string // 必需的 CPU 特性可选如 sse4、aes }Platform.OS字段在 Swarm Classic 中有着非常实际的用途详见本文第五节。3.2 Manifest单平台镜像的内容清单manifest.go 定义了序列化后媒体类型为application/vnd.oci.image.manifest.v1json的Manifest结构它是一个平台上的一个镜像的直接清单type Manifest struct { specs.Versioned // 内嵌 schemaVersion Config Descriptor // 按 digest 引用容器配置对象JSON blob运行时据此设置容器 Layers []Descriptor // 按顺序索引的镜像层列表 Annotations map[string]string // 镜像清单的任意元数据 }注意这里Config和Layers都是Descriptor而非内嵌内容——实际数据通过 digest 另行存放清单本身只是指针列表。这种设计使得清单可以做得非常小且内容可被独立校验。3.3 Index多平台镜像的入口聚合index.go 定义了媒体类型为application/vnd.oci.image.index.v1json的Index结构type Index struct { specs.Versioned Manifests []Descriptor // 引用各平台专属的 manifest Annotations map[string]string // 索引的任意元数据 }Index解决的是多架构multi-arch镜像问题一个镜像名可以对应多个平台的多个 manifest客户端如 Docker按自身平台选择合适的 manifest 下载。这正是docker pull同一个 tag 在不同架构机器上拿到不同层的底层机制。3.4 Image / ImageConfig / RootFS / History运行期配置与层账本config.go 定义了媒体类型为application/vnd.oci.image.config.v1json的Image结构及其配套类型是整个镜像可运行性的最终落脚点ImageConfig容器启动的执行参数基准。User运行进程的用户名/UID、ExposedPorts暴露端口集合、Env环境变量列表、Entrypoint启动命令、CmdEntrypoint 的默认参数、Volumes数据卷目录集合、WorkingDir工作目录、Labels容器元数据、StopSignal停止容器时发送的信号。这回答了零参数运行时引擎该执行什么的问题。RootFSTyperootfs 类型与DiffIDs自底向上排列的层内容哈希数组构成镜像层的完整账本。History每层的构建历史——CreatedRFC 3339 时间、CreatedBy创建该层的命令、Author、Comment、EmptyLayer该历史条目是否产生了文件系统 diff。Image总装结构Created、Author、Architecture、OS、Config、RootFS、History。其中Architecture/OS与 Descriptor 中的Platform互相印证保证清单与配置的平台信息一致。3.5 Layout 与 MediaType目录组织与类型注册layout.go 定义了 OCI Image Layout 目录的约定根目录下的oci-layout文件ImageLayoutFile内含imageLayoutVersion字段版本号固定为1.0.0ImageLayoutVersion。mediatype.go 以常量形式注册了全套官方媒体类型是解析镜像时必须对照的类型表常量媒体类型值MediaTypeDescriptorapplication/vnd.oci.descriptor.v1jsonMediaTypeLayoutHeaderapplication/vnd.oci.layout.header.v1jsonMediaTypeImageManifestapplication/vnd.oci.image.manifest.v1jsonMediaTypeImageIndexapplication/vnd.oci.image.index.v1jsonMediaTypeImageLayerapplication/vnd.oci.image.layer.v1.tarMediaTypeImageLayerGzipapplication/vnd.oci.image.layer.v1.targzipMediaTypeImageLayerNonDistributableapplication/vnd.oci.image.layer.nondistributable.v1.tarMediaTypeImageLayerNonDistributableGzipapplication/vnd.oci.image.layer.nondistributable.v1.targzipMediaTypeImageConfigapplication/vnd.oci.image.config.v1json3.6 Versioned版本兼容的第一道闸门versioned.go实际定义见 versioned.go提供了被Manifest、Index等结构共同内嵌的Versioned基类型type Versioned struct { SchemaVersion int json:schemaVersion // 本镜像遵循的 manifest schema 版本 }其注释明确说明设计意图未知 schema 版本的内容可以先按此结构解码通过schemaVersion判断版本后再决定如何处理——这是镜像格式向前兼容的第一道防线。四、FAQ 解读distribution 为何不在规范范围规范 README 的 FAQ 部分回答了两个关键设计问题值得深入理解Q1为什么本项目不涉及分发distributionA镜像分发例如 Docker v2.2 与 AppC 目前采用的基于 HTTP 的拉取当时不在 OCI Scope Table 的范围内。将分发作为可选层纳入一直是进行中的讨论work in progress但尚未定案。这意味着 OCI Image Format 聚焦于镜像长什么样而把镜像怎么传输留给生态中的既有实现。Q2AppC 或 Docker 镜像格式何去何从A已有格式可以继续作为技术试验场。OCI Image Format 的目标是提供一份可在不同工具间共享、可被长期数年乃至数十年演进兼容的可靠开放规范——正如 deb 与 rpm 格式那样。这一回答明确了规范的长期主义定位不与既有生态对立而是提供公共底座。五、规范落地Swarm Classic 如何用 OCI 平台信息驱动调度OCI 规范在 Swarm Classic 中并非只读不用的 vendored 依赖而是直接参与了容器调度决策。整条调用链可以从三个文件完整追踪。5.1 第一步引擎向 Registry 查询镜像平台cluster/engine.go在 cluster/engine.go#L1673-L1697 中Engine.GetImagePlatforms通过底层 Docker Engine API 的DistributionInspect接口查询镜像的可用平台// GetImagePlatforms calls the DistributionInspect method on the underlying // engine and returns the valid platforms for the image. func (e *Engine) GetImagePlatforms(config *ContainerConfig, authConfig *types.AuthConfig) ([]v1.Platform, error) { ... result, err : e.apiClient.DistributionInspect(context.Background(), img, encodedAuth) ... return result.Platforms, nil }这里直接使用了本仓库 vendored 的github.com/opencontainers/image-spec/specs-go/v1包v1.Platform正是上节介绍的Platform结构体result.Platforms由 Registry 依据 OCI Image Index / Manifest 中的平台描述返回。5.2 第二步把平台信息翻译成调度约束cluster/swarm/cluster.go在 cluster/swarm/cluster.go#L185-L251 中Cluster.setOSTypeConstraint负责将平台列表转化为容器约束constraint若容器已存在ostype约束直接返回不做覆盖随机选择一台引擎调用GetImagePlatforms获取平台列表遍历平台列表用 map 提取并去重p.OS注释明确说明去重是为避免同一平台的多架构重复只有一个 OS 类型时添加简单约束ostypeos存在多个 OS 类型时先给每个 OS 加括号(linux)再用|连接最后包进/.../表示正则生成ostype/(linux)|(windows)/形式的多值约束0 个有效 OS 类型时直接返回不加任何约束。生成的ostype约束随后会交给调度器的过滤与策略模块确保容器只被调度到操作系统类型匹配的节点上——这使 Swarm Classic 在混合 Linux/Windows 节点的集群中能够正确路由任务。5.3 第三步四种场景的测试验证cluster/swarm/cluster_test.gocluster/swarm/cluster_test.go#L295-L403 通过 mock API 客户端mockClientWithInit对setOSTypeConstraint做了四个子测试完整覆盖了边界情况子测试模拟返回的 Platforms期望结果NoPlatforms空平台列表不产生任何 ostype 约束OnePlatform仅{OS: windows}约束为ostypewindowsTwoPlatforms{OS:linux}与{OS:windows}约束为ostype/(linux)|(windows)/或顺序颠倒的正则DuplicatePlatforms两个{OS:linux}去重后约束为ostypelinux测试代码中getOSTypeConstraint辅助函数负责从配置的约束列表中反查ostype前缀的取值用于断言。这套测试验证了从 OCIPlatform结构到调度约束的完整转换逻辑是规范 → 实现 → 验证闭环的典型范例。六、参与规范维护的协作约定最后简要说明原文档中关于项目协作的约定这些约定同样适用于任何希望向 OCI 规范提交改动的人设计先行对规范的非平凡改动应先在邮件列表讨论设计而非直接提交 pull request拼写与语法错误可直提 PR。DCO 签名规范与代码采用 Apache 2.0 许可见 vendor 目录内 LICENSE。提交者需在每条 commit message 末尾添加Signed-off-by: Your Name email声明其对提交内容拥有贡献权利可用git commit -s自动添加。Commit 规范subject 与 body 空行分隔、subject 不超过 50 字符、首字母大写、不以句号结尾、用祈使语气、body 每行 72 字符内、说明 what/why 而非 how、必要时使用作用域关键字如README: ...。Markdown 风格规范仓库统一采用每行一句的排版便于 git diff 与避免换行争论。小结OCI Image Format 用一组精简而自洽的数据结构Layout → Index → Manifest → Config → Layer全部通过 Descriptor 按 digest 串联解决了容器镜像如何标准化打包与运行的问题并以开放规范的姿态与 Runtime Spec 分工协作。在 Swarm Classic 中这一规范不只停留在 vendor 目录而是经由 cluster/engine.go 的GetImagePlatforms与 cluster/swarm/cluster.go 的setOSTypeConstraint真正进入调度决策路径再经 cluster/swarm/cluster_test.go 的多场景测试验证——这为理解开放标准如何被工业级系统消费提供了一个完整的参考样本。延伸阅读可继续在仓库中查看 specs-go 目录下的全部类型定义config.go、descriptor.go、index.go、layout.go、manifest.go、mediatype.go以及 vendor.conf 中该依赖的版本锁定情况。赞分享云原生后端微服务【免费下载链接】classicswarmSwarm Classic: a container clustering system. Not to be confused with Docker Swarm which is at https://github.com/docker/swarmkit项目地址https://gitcode.com/gh_mirrors/cl/classicswarm点击查看免费下载相关推荐3步掌握Sniffles2长读测序结构变异检测的终极解决方案3步掌握Sniffles2长读测序结构变异检测的终极解决方案 在基因组学研究领域 结构变异检测 对于理解遗传多样性和疾病机制至关重要。Sniffles2作为Crossplane Package Format v2 解析从 crossplane.yaml 到 OCI 镜像 /package.yaml 的包格式规范Crossplane Package Format v2 解析从 crossplane.yaml 到 OCI 镜像 /package.yaml 的包格式规范云原生后端深入解析OpenContainers镜像规范(OCI Image-Spec)深入解析OpenContainers镜像规范 OCI Image Spec 前言 容器技术已成为现代云计算和DevOps实践的核心组件。作为容器技术的基石镜像云原生存储上一篇codeforces-go 算法模板库实战力扣双周赛 163 Q4「带传送的最小成本路径」——网格图 DP 与后缀最小值优化下一篇wiliwili代码模板快速创建新模块创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

openJiuwen DeepSearch 新增任务处理(new_task)全解析:章节级增量研究与报告扩展实战指南

openJiuwen DeepSearch 新增任务处理(new_task)全解析:章节级增量研究与报告扩展实战指南

人工智能大模型AI AgentRAG深度研究搜索引擎后端代码智能体 【免费下载链接】deepsearch openJiuwen DeepSearch是一款知识增强的深度搜索与研究框架,有业界领先的片段级引用和溯源推理能力,提供精准Agentic搜索与研究能力 项目地址: https:/…

2026/10/12 1:30:49 阅读更多 →
一条命令接入本地逆向工程:5 分钟跑通 REA 的第一次应用分析

一条命令接入本地逆向工程:5 分钟跑通 REA 的第一次应用分析

一条命令接入本地逆向工程:5 分钟跑通 REA 的第一次应用分析 【免费下载链接】rea Reverse engineer anything with agents, from app behavior down to native binaries. 项目地址: https://gitcode.com/GitHub_Trending/rea2/rea REA(Reverse E…

2026/10/12 1:30:49 阅读更多 →
毕业设计选题指南:单片机与物联网五大方向深度对比与避坑清单

毕业设计选题指南:单片机与物联网五大方向深度对比与避坑清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 1:29:49 阅读更多 →

最新新闻

PLC基本指令详解:从位逻辑到定时计数,掌握梯形图编程核心

PLC基本指令详解:从位逻辑到定时计数,掌握梯形图编程核心

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 3:19:56 阅读更多 →
柔性上料盘如何替代振动盘:从换型瓶颈到视觉引导的产线升级指南

柔性上料盘如何替代振动盘:从换型瓶颈到视觉引导的产线升级指南

简介:《全球与中国柔性上料盘市场现状及未来发展趋势(2024版)》是一份QYResearch出品的专业市场研究报告,面向柔性上料与自动化产线设备从业者、工业机器人厂商、市场分析师及投资研究人员。报告以2019至2023年为历史期、2024至20…

2026/10/12 3:19:56 阅读更多 →
WiFi分析工具设计实战:从数据采集到信道优化与故障排查

WiFi分析工具设计实战:从数据采集到信道优化与故障排查

1. 从一个标题说起:这个工具到底在解决什么问题第一次看到“Jev powered WiFi analysis tool”这个标题,我的直觉是:这大概率是一个把无线网络分析能力封装成轻量级工具的项目,名字里的“Jev”可能是作者自定的代号、模块名或者某…

2026/10/12 3:19:56 阅读更多 →
SpringBoot+Vue美食网站系统:前后端分离全栈项目架构与部署详解

SpringBoot+Vue美食网站系统:前后端分离全栈项目架构与部署详解

做个人项目这些年,前后端分离的练手项目做了不少,但每次有人让我推荐一个既能完整跑起来、又能覆盖主流开发流程的学习项目,我第一反应往往是这套美食网站系统。为什么?因为它的技术选型非常贴近当下中小型项目的真实组合&#xf…

2026/10/12 3:19:56 阅读更多 →
高斯赛德尔迭代法:大规模稀疏线性方程组的工程解法与实战技巧

高斯赛德尔迭代法:大规模稀疏线性方程组的工程解法与实战技巧

线性方程组这东西,刚接触数值计算的时候,总觉得不是事——高斯消元一把梭,n100也就是眨眨眼的事。可等你真在工程里碰到几十万未知量、矩阵非零元稀稀落落排成带状或块状的时候,直接法的“快”就变成了一种幻觉:要么内…

2026/10/12 3:19:56 阅读更多 →
attrs 比较机制完全指南:默认相等性、排序生成与自定义比较(Comparison)

attrs 比较机制完全指南:默认相等性、排序生成与自定义比较(Comparison)

后端 【免费下载链接】attrs Python Classes Without Boilerplate 项目地址: https://gitcode.com/gh_mirrors/at/attrs 点击查看 免费下载 本文围绕 attrs 官方文档 docs/comparison.md 展开,系统讲解 attrs 类实例的相等性(equality&#…

2026/10/12 3:18:56 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →