尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
claude-skills 的 Python CLI 开发实战:基于 Typer、Click、Rich 与 questionary 构建专业命令行工具
claude-skills 的 Python CLI 开发实战基于 Typer、Click、Rich 与 questionary 构建专业命令行工具【免费下载链接】claude-skills67 Specialized Skills for Full-Stack Developers. Transform Claude Code into your expert pair programmer.项目地址: https://gitcode.com/GitHub_Trending/claud/claude-skillsclaude-skills 是面向全栈开发者的 Claude Code 技能仓库其中 cli-developer 技能专门覆盖 CLI 工具的设计与实现而本指南对应的 Python CLI 参考文档 是其中 Python 语言侧的完整实操手册。本文以该参考文档为主体骨架系统讲解如何用 Typer、Click、argparse 三种方式搭建命令行框架用 Rich、questionary、tqdm 提升终端体验并覆盖错误处理、配置分层、打包与自动化测试全流程。读完本文你将掌握一套可落地的 Python CLI 开发范式并能直接复用到自己的项目脚手架、部署工具或运维脚本中。技术选型Python CLI 框架全景对比参考文档在框架选择上给出了清晰的定位结合 SKILL.md 的 Reference Guide 可以整理出下表框架定位依赖适用场景Typer现代推荐首选FastAPI 作者出品需安装追求开发效率、类型提示完善、需要自动生成--helpClick广泛使用需安装老牌成熟、需要高度自定义、生态庞大argparse标准库零依赖不想引入第三方依赖的轻量工具Rich终端美化需安装表格、面板、语法高亮、进度条等富文本输出questionary交互提示需安装文本输入、单选、多选、确认、密码等交互tqdm进度条需安装循环进度、多级进度、下载场景其中 Rich 与 questionary 通常与 Typer/Click 搭配使用而非互相替代。Typer 被推荐的首要原因是FastAPI-style CLI framework with automatic help generation——它直接复用 FastAPI 的类型注解哲学用 Python 类型声明即得参数校验与帮助文本。Typer 快速入门现代 CLI 的标准写法Typer 的核心理念是用类型说话。参考文档给出了一个完整的init/deploy/config三层命令示例#!/usr/bin/env python3 import typer from typing import Optional from enum import Enum app typer.Typer() class Environment(str, Enum): dev development staging staging prod production app.command() def init( name: str typer.Argument(..., helpProject name), template: str typer.Option(default, helpProject template), force: bool typer.Option(False, --force, -f, helpOverwrite existing), ): Initialize a new project typer.echo(fCreating {name} from {template}) if force: typer.echo(Force mode enabled) app.command() def deploy( environment: Environment typer.Argument(..., helpTarget environment), dry_run: bool typer.Option(False, --dry-run, helpPreview only), config: Optional[typer.FileText] typer.Option(None, helpConfig file), ): Deploy to environment if dry_run: typer.echo(fWould deploy to: {environment.value}) else: typer.echo(fDeploying to {environment.value}...) # Nested commands config_app typer.Typer() app.add_typer(config_app, nameconfig, helpManage configuration) config_app.command(get) def config_get(key: str): Get config value typer.echo(fValue: {get_config(key)}) config_app.command(set) def config_set(key: str, value: str): Set config value set_config(key, value) typer.echo(fSet {key} {value}) if __name__ __main__: app()这段代码浓缩了 Typer 的五个关键能力逐一拆解位置参数与必填语义typer.Argument(...)中...表示必填与 FastAPI 的...语义一致environment参数使用Enum子类做类型注解Typer 会自动把命令行输入映射为枚举成员并给出合法性校验。选项的短长格式typer.Option(False, --force, -f)中的False是默认值--force/-f是长短两种写法布尔选项天然支持出现即 True的 flag 语义。文件句柄类型Optional[typer.FileText]声明可选的文本文件参数Typer 会代为打开/关闭文件避免手写文件管理样板代码。嵌套子命令通过app.add_typer(config_app, nameconfig)将独立的 Typer 实例挂载为子命令组实现mycli config get key、mycli config set key value的命令层级。自动帮助所有help文本都会自动汇总进--help输出这也是 SKILL.md 中运行cli --help验证帮助文本渲染约束的直接体现见 SKILL.md 的 Core Workflow 第 3 步。Click经典成熟的组合式框架Click 是 Python CLI 领域使用最广泛的框架之一其装饰器风格在需要细粒度控制时更具表现力。参考文档给出了与 Typer 示例功能对等的 Click 版本import click click.group() click.version_option() def cli(): My awesome CLI tool pass cli.command() click.argument(name) click.option(--template, defaultdefault, helpProject template) click.option(--force, -f, is_flagTrue, helpOverwrite existing) def init(name, template, force): Initialize a new project click.echo(fCreating {name} from {template}) cli.command() click.argument(environment, typeclick.Choice([dev, staging, prod])) click.option(--dry-run, is_flagTrue, helpPreview only) click.option(--config, typeclick.File(r), helpConfig file) def deploy(environment, dry_run, config): Deploy to environment if dry_run: click.secho(fWould deploy to: {environment}, fgyellow) else: click.secho(fDeploying to {environment}..., fggreen) # Nested groups cli.group() def config(): Manage configuration pass config.command(get) click.argument(key) def config_get(key): Get config value click.echo(get_config(key)) config.command(set) click.argument(key) click.argument(value) def config_set(key, value): Set config value set_config(key, value) if __name__ __main__: cli()与 Typer 的对应关系清晰可循click.group()等价于 Typer 的typer.Typer()cli.group()定义嵌套命令组等价于add_typer(nameconfig)。click.argument()对应typer.Argumentclick.option()对应typer.Optionis_flagTrue是 Click 表达布尔开关的标准方式。typeclick.Choice([...])提供枚举约束与 Typer 的Enum注解作用一致typeclick.File(r)对应typer.FileText。click.secho(msg, fgyellow)与click.secho(msg, fggreen)提供带色输出且 Click 会自动检测非 TTY 环境并关闭颜色。click.version_option()一行即可获得--version支持满足 SKILL.md 的 MUST DO 约束见 SKILL.md。Typer 底层实际上就是基于 Click 构建的因此两者的命令模型完全同构选择 Typer 可以获得更少的样板代码选择 Click 则保留更大的装饰器自定义空间。argparse零依赖的标准库方案当项目希望完全避免第三方依赖时标准库 argparse 依然可以胜任代价是需要手写命令分发逻辑。参考文档给出了一个约 40 行的完整实现import argparse import sys def main(): parser argparse.ArgumentParser( progmycli, descriptionMy awesome CLI tool, ) parser.add_argument(--version, actionversion, version1.0.0) subparsers parser.add_subparsers(destcommand, requiredTrue) # Init command init_parser subparsers.add_parser(init, helpInitialize project) init_parser.add_argument(name, helpProject name) init_parser.add_argument(--template, defaultdefault, helpTemplate) init_parser.add_argument(-f, --force, actionstore_true) # Deploy command deploy_parser subparsers.add_parser(deploy, helpDeploy) deploy_parser.add_argument( environment, choices[dev, staging, prod], helpTarget environment ) deploy_parser.add_argument(--dry-run, actionstore_true) deploy_parser.add_argument(--config, typeargparse.FileType(r)) args parser.parse_args() if args.command init: init(args.name, args.template, args.force) elif args.command deploy: deploy(args.environment, args.dry_run, args.config) if __name__ __main__: main()需要注意的几个 argparse 细节add_subparsers(destcommand, requiredTrue)dest用于记录用户输入了哪个子命令requiredTrue强制必须指定子命令Python 3.7 支持。actionstore_true是 argparse 的布尔 flag 写法等价于 Click 的is_flagTrue。actionversion提供--version输出。choices[dev, staging, prod]与typeargparse.FileType(r)分别对应 Click 的Choice与File类型。分发逻辑是手动的if/elif分支这正是参考文档标注verbose but no dependencies的原因——功能完整但代码量明显多于 Typer/Click。如果 CLI 只服务于内部脚本且没有第三方依赖约束argparse 是可接受的但一旦命令层级超过两层建议优先 Typer 或 Click。Rich为终端输出注入专业质感CLI 的可读性直接决定用户体验。参考文档用 Rich 覆盖了风格化文本、表格、面板、语法高亮与进度指示五类常见输出from rich.console import Console from rich.table import Table from rich.progress import Progress, SpinnerColumn, TextColumn from rich.panel import Panel from rich.syntax import Syntax from rich import print as rprint console Console() # Styled output console.print([bold blue]Info:[/] Starting deployment...) console.print([bold green]Success:[/] Deployment complete!) console.print([bold yellow]Warning:[/] Deprecated flag used) console.print([bold red]Error:[/] Deployment failed) # Tables table Table(titleDeployments) table.add_column(Environment, stylecyan) table.add_column(Status, stylemagenta) table.add_column(Time, stylegreen) table.add_row(Production, ✓ Success, 2m 34s) table.add_row(Staging, ✗ Failed, 1m 12s) console.print(table) # Panels console.print(Panel.fit( Deploy to production?, titleConfirmation, border_stylered )) # Syntax highlighting code def deploy(env: str): print(fDeploying to {env}) console.print(Syntax(code, python, thememonokai)) # Progress bars with Progress() as progress: task progress.add_task([cyan]Deploying..., total100) for i in range(100): do_work() progress.update(task, advance1) # Spinners with Progress( SpinnerColumn(), TextColumn([progress.description]{task.description}), ) as progress: task progress.add_task(Installing dependencies...) install_dependencies()语义化颜色规范参考文档示例中的[bold blue]、[bold green]等颜色用法与仓库的 UX 模式参考 中的语义颜色约定完全一致Red错误、失败、破坏性操作Yellow警告、弃用提示、非关键问题Green成功、完成、正面反馈Blue信息、提示、中性消息Cyan命令、代码、技术细节Magenta高亮、特殊条目进度指示器的选择标准参考 UX 模式参考进度指示器应按需选型确定性进度已知总数如文件操作、下载、批量处理用Progress带total的进度条显示百分比与当前/总数。不确定进度时长未知如 API 调用、数据库查询用SpinnerColumn()转圈动画。多阶段流程构建、部署等多阶段任务用✓/⏳等符号标注各阶段状态。Rich 的Progress上下文管理器会自动处理 TTY 重绘无需手动刷新。questionary交互式提示的完整方案参考文档用 questionary 覆盖了 CLI 交互的五种经典形态import questionary # Text input name questionary.text( Project name:, defaultmy-project, validatelambda x: len(x) 0 or Name required ).ask() # Select from list environment questionary.select( Select environment:, choices[development, staging, production], defaultdevelopment ).ask() # Checkbox (multi-select) features questionary.checkbox( Select features:, choices[ questionary.Choice(TypeScript, checkedTrue), questionary.Choice(ESLint, checkedTrue), questionary.Choice(Prettier, checkedTrue), questionary.Choice(Jest, checkedFalse), ] ).ask() # Confirmation confirmed questionary.confirm( Deploy to production?, defaultFalse ).ask() if confirmed: deploy() # Password password questionary.password(Enter password:).ask()对照 UX 模式参考 的交互规范这段代码践行了三条核心准则即时校验validatelambda x: len(x) 0 or Name required在输入阶段即校验返回字符串即错误提示。合理默认值defaultmy-project、defaultdevelopment、defaultFalse预选了最常见选项确认框默认False否更安全。默认预勾选Choice(TypeScript, checkedTrue)为多选框提供默认选中项减少用户按键次数。同时必须警惕 SKILL.md 的 MUST NOT DO不得在 CI/CD 环境强制交互输入。交互提示只应在 TTY 环境激活非交互场景必须提供--flag或环境变量兜底详见后文错误处理与退出码一节。tqdm简单而强大的进度条对于纯循环场景tqdm 是比 Rich 更轻量的选择参考文档给出了三种典型用法from tqdm import tqdm import time # Simple progress bar for i in tqdm(range(100), descProcessing): process_item(i) # Custom format with tqdm(total100, descDownloading, unitMB) as pbar: for chunk in download_chunks(): pbar.update(len(chunk)) # Multiple progress bars from tqdm import trange for epoch in trange(10, descEpochs): for batch in trange(100, descBatches, leaveFalse): train_batch(batch)要点拆解tqdm(range(100))自动以可迭代长度作为totaldesc设置描述文本。totalupdate(n)模式适合按不定步长推进的场景如按 MB 累加的下载任务unitMB让进度条显示单位。嵌套trange展示多级进度内层加leaveFalse使子进度条完成后自动清除避免刷屏。参考 UX 模式参考 的进度条设计标准优秀进度条应包含可视化条、百分比、当前/总数、速率可选与 ETA可选等要素宽度保持在 2040 字符避免只有 Processing...这种零反馈写法。错误处理与退出码规范错误处理是 CLI 专业度的分水岭。参考文档给出了一个完整的 Typer 错误处理模板import typer import sys from pathlib import Path app typer.Typer() app.command() def deploy(): try: perform_deploy() except PermissionError as e: typer.secho(Permission denied, fgtyper.colors.RED, errTrue) typer.echo(Try running with sudo or check file permissions) raise typer.Exit(code77) except FileNotFoundError as e: typer.secho(fFile not found: {e.filename}, fgtyper.colors.RED, errTrue) raise typer.Exit(code127) except Exception as e: typer.secho(fDeployment failed: {e}, fgtyper.colors.RED, errTrue) if os.getenv(DEBUG): import traceback traceback.print_exc() raise typer.Exit(code1) # Handle KeyboardInterrupt (CtrlC) def main(): try: app() except KeyboardInterrupt: typer.echo(\nOperation cancelled) sys.exit(130) if __name__ __main__: main()退出码语义这段代码使用的退出码与 设计模式参考 中的 POSIX 约定表完全对应退出码含义触发场景0成功正常执行1一般错误未分类异常2用法错误无效参数框架自动处理77权限拒绝PermissionError127未找到FileNotFoundError130SIGINTCtrlC用户中断错误信息的可操作准则参考 UX 模式参考错误处理应遵循[Context] → [Problem] → [Solution]模式做具体到Port 3000 already in use而非Port unavailable给出上下文哪个文件、哪一行直接建议解决方案Try running sudo mycli ...用平实的自然语言而非ENOENT这种系统术语。不做向普通用户输出堆栈保留给--debug/DEBUG环境变量使用行话让用户输入无效但不知为何无效。DEBUG 环境变量分级上述代码中if os.getenv(DEBUG):与 UX 模式参考 的分级输出策略一致正常模式只输出简洁结论verbose 模式输出步骤DEBUG 模式才打印堆栈与内部细节。SKILL.md 还要求优雅处理 SIGINT上述KeyboardInterrupt捕获即为此约束的实现。配置管理分层合并的推荐范式参考文档给出的Config类实现了系统 → 用户 → 项目 → 环境变量四层合并from pathlib import Path from typing import Any import json import os class Config: def __init__(self): self.config_paths [ Path(/etc/mycli/config.json), # System Path.home() / .config / mycli / config.json, # User Path.cwd() / mycli.json, # Project ] def load(self) - dict[str, Any]: config self._defaults() # Load from files (lowest to highest priority) for path in self.config_paths: if path.exists(): with path.open() as f: config.update(json.load(f)) # Override with environment variables for key in config.keys(): env_var fMYCLI_{key.upper()} if env_var in os.environ: config[key] os.environ[env_var] return config def _defaults(self) - dict[str, Any]: return { environment: development, verbose: False, timeout: 30, }完整的优先级链此实现覆盖了 设计模式参考 中配置分层的一部分。完整的优先级高→低应为命令行 flag——用户最明确的意图环境变量——运行时上下文项目配置文件如./mycli.json用户配置文件如~/.config/mycli/config.json系统配置文件如/etc/mycli/config.json硬编码默认值代码中的_defaults()充当第 6 层按config_paths顺序update()合并实现第 35 层MYCLI_{KEY}环境变量覆盖实现第 2 层。生产实践中可在parse_args后把 CLI 参数再update进结果补齐第 1 层。环境变量命名约定环境变量采用MYCLI_前缀 配置键大写的映射规则environment→MYCLI_ENVIRONMENT这一约定与 Go CLI 参考文档 中 Viper 的SetEnvPrefix(MYCLI)模式完全同构属于跨语言的一致设计。另外注意 SKILL.md 的 MUST NOT DO路径必须通过Path.home()Python、os.UserHomeDir()Go等方式获取禁止硬编码平台路径。打包发布pyproject.toml 与 console scripts参考文档给出了标准化的现代打包配置# pyproject.toml [build-system] requires [setuptools61.0] build-backend setuptools.build_meta [project] name mycli version 1.0.0 description My awesome CLI tool requires-python 3.10 dependencies [ typer[all]0.9.0, rich13.0.0, questionary2.0.0, ] [project.scripts] mycli mycli.cli:main [project.optional-dependencies] dev [ pytest7.0.0, pytest-cov4.0.0, ]关键点解读[project.scripts]中的mycli mycli.cli:main会在安装时自动生成名为mycli的可执行入口指向mycli.cli模块的main函数——用户安装后即可在终端直接敲mycli运行无需python -m mycli.cli。requires-python 3.10声明了最低 Python 版本也意味着使用了dict[str, Any]、Path等 3.9/3.10 特性dict[str, Any]类型注解需 3.9。typer[all]是可选依赖的聚合写法包含typer全家桶dev可选依赖组在安装时用pip install -e .[dev]引入分离运行时与开发依赖。安装该 CLI 后验证手段与 SKILL.md 的 Core Workflow 完全一致mycli --help检查帮助渲染、mycli --version确认版本号输出。自动化测试用 CliRunner 守护 CLI 行为参考文档给出了基于typer.testing.CliRunner的测试范式from typer.testing import CliRunner from mycli.cli import app runner CliRunner() def test_version(): result runner.invoke(app, [--version]) assert result.exit_code 0 assert 1.0.0 in result.stdout def test_init(): result runner.invoke(app, [init, my-project]) assert result.exit_code 0 assert Creating my-project in result.stdout def test_init_with_template(): result runner.invoke(app, [init, my-project, --template, react]) assert result.exit_code 0 assert react in result.stdout def test_invalid_command(): result runner.invoke(app, [invalid]) assert result.exit_code ! 0CliRunner.invoke(app, argv)在进程内直接驱动 Typer 应用无需真实子进程。每个测试的关键断言有两个维度退出码result.exit_code 0成功或! 0失败。标准输出result.stdout包含预期文本。test_invalid_command尤其重要——它锁定了非法命令必须非零退出的行为直接落实 SKILL.md 的 MUST DO 中的提供明确、可操作的错误信息与尽早校验用户输入。若改用 Clickclick.testing.CliRunner提供几乎相同的runner.invoke(cli, args)API若用 argparse则只能退化为对main()函数的单元测试配合capsys捕获输出。参考文档与 Go CLI 参考文档 的cobra测试思想一致以命令名 参数为输入单元、以退出码和输出为断言对象这本身就是对命令契约的回归保护。与 cli-developer 技能规范的衔接将本文的 Python 侧实现放回 cli-developer 技能 的整体约束中可以形成一套完整的交付标准开发流程Core Workflow先分析用户工作流与命令层级在设计阶段确认 flag 命名一致且不破坏既有签名实现后用--help、--version验证最后补齐补全脚本、错误信息、进度指示与 SIGINT 处理跨平台冒烟测试并关注启动时间目标低于 50ms。必须遵守MUST DO同时支持交互与非交互两种模式交互用 questionary非交互用 flag/env var 兜底支持--help与--version优雅处理 SIGINT退出码 130尽早校验输入提供可操作的错误信息测试覆盖 Windows、macOS、Linux禁止事项MUST NOT DO输出被管道重定向时不要向 stdout 打印日志——诊断信息写 stderr颜色输出前先做 TTY 检测sys.stdout.isatty()不要在 CI/CD 环境强制交互输入不要硬编码平台路径使用Path.home()不要把 flag/子命令重命名当小事——这是破坏性变更不要不带 shell 补全就发布——Typer 通过mycli --install-completion可生成 bash/zsh/fish 补全这些约束与参考文档中的错误处理、配置管理与测试章节互为表里共同构成 Python CLI 从能跑到专业的完整进阶路径。总结围绕 Python CLI 参考文档 的核心内容本文完整覆盖了六条主线选型Typer 现代推荐 / Click 广泛使用 / argparse 零依赖、实现参数、选项、枚举、嵌套命令、自动帮助、体验Rich 富文本、questionary 交互、tqdm 进度条、健壮性分级错误处理与 POSIX 退出码、配置多层合并与环境变量覆盖、交付pyproject.toml 打包 CliRunner 自动化测试。配合 cli-developer 技能 的流程规范、设计模式参考 的命令层级/配置分层/退出码约定以及 UX 模式参考 的颜色、进度条、错误信息与帮助文本标准你可以在项目中直接照搬这套代码模板快速交付一个参数规范、输出美观、错误可诊断、可测试可发布的 Python CLI 工具。【免费下载链接】claude-skills67 Specialized Skills for Full-Stack Developers. Transform Claude Code into your expert pair programmer.项目地址: https://gitcode.com/GitHub_Trending/claud/claude-skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

es-toolkit/compat 的 takeRight:从数组尾部安全截取元素的 Lodash 兼容实现

es-toolkit/compat 的 takeRight:从数组尾部安全截取元素的 Lodash 兼容实现

es-toolkit/compat 的 takeRight:从数组尾部安全截取元素的 Lodash 兼容实现 【免费下载链接】es-toolkit A modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash. 项目地址: https://gitcode.com/GitHu…

📅 2026/9/15 10:14:41
AI重排IT岗位:从AI Infra到Agent应用的四条破局路径

AI重排IT岗位:从AI Infra到Agent应用的四条破局路径

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

📅 2026/9/15 10:14:41
Django ABAC动态策略引擎实现零信任访问控制

Django ABAC动态策略引擎实现零信任访问控制

简介:本资源是一套基于Django框架实现的零信任网络访问控制系统完整源码,面向计算机、通信、人工智能及自动化等专业的学生与教师,适用于毕业设计、课程大作业或课程设计场景,尤其适合具备Python基础并希望深入理解零信任架构落地…

📅 2026/9/15 10:14:41
MORE NEWS

更多资讯

📰

Dagger TypeScript SDK 中 GitRefTreeOpts 类型别名详解:控制 GitRef.tree() 的克隆深度与 .git 目录行为

Dagger TypeScript SDK 中 GitRefTreeOpts 类型别名详解:控制 GitRef.tree() 的克隆深度与 .git 目录行为 【免费下载链接】dagger Automation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud 项目地址: https://g…

📰

为 Semantica 贡献代码、文档与测试:从开发环境搭建到插件扩展的完整指南

为 Semantica 贡献代码、文档与测试:从开发环境搭建到插件扩展的完整指南 【免费下载链接】semantica Graph-Native Infrastructure for Context and Accountable AI Systems 项目地址: https://gitcode.com/GitHub_Trending/sema/semantica Semantica 是一个…

📰

国产工业CAD替代,煤机厂硬件需求盘点与升级方案解析

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

📰

一拖五恒压供水控制系统:S7-200 SMART与昆仑通态的工程实战

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

📰

深入Archify黄金基线:v1兼容性与坐标稳定策略的完整设计

深入Archify黄金基线:v1兼容性与坐标稳定策略的完整设计 【免费下载链接】archify Agent skill for beautiful, verifiable architecture, workflow, sequence, data-flow, and lifecycle diagrams—self-contained HTML with motion and crisp export. 项目地址:…

📰

C++数组完全指南:一维二维、指针与性能优化实战

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

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬