vermink 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vermink-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TSVMV
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
vermink-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,153 @@
1
+ Metadata-Version: 2.4
2
+ Name: vermink
3
+ Version: 0.1.0
4
+ Summary: Zero-dependency terminal theme engine: compile text themes into Zsh/Bash/PowerShell prompts
5
+ License-Expression: MIT
6
+ Keywords: terminal,prompt,theme,zsh,bash,powershell
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Provides-Extra: test
15
+ Requires-Dist: pytest; extra == "test"
16
+ Dynamic: license-file
17
+
18
+ # vermink
19
+
20
+ 零依赖终端美化引擎:把纯文本主题编译成 Zsh / Bash / PowerShell 的提示符与状态栏。
21
+
22
+ ## 安装
23
+
24
+ ```bash
25
+ pip install vermink
26
+ ```
27
+
28
+ ## 快速开始
29
+
30
+ ```bash
31
+ # 生成默认 dark 主题并安装到当前 shell
32
+ vermink init
33
+
34
+ # 预览主题效果(不修改任何文件)
35
+ vermink preview dark
36
+
37
+ # 列出所有主题
38
+ vermink list
39
+ ```
40
+
41
+ ## 子命令
42
+
43
+ | 命令 | 说明 |
44
+ | --- | --- |
45
+ | `vermink init [--shell zsh\|bash\|pwsh]` | 生成默认 dark 主题并安装 |
46
+ | `vermink preview <主题名> [--plain]` | 终端预览渲染效果 |
47
+ | `vermink install <主题名> --shell zsh\|bash\|pwsh` | 安装到 shell 启动文件(自动备份) |
48
+ | `vermink uninstall --shell zsh\|bash\|pwsh` | 卸载并还原备份 |
49
+ | `vermink list [--shell zsh\|bash\|pwsh]` | 列出主题与当前生效主题 |
50
+ | `vermink show <主题名>` | 打印主题源配置 |
51
+
52
+ ## 主题文件
53
+
54
+ 主题文件使用 INI 格式,包含以下区块:
55
+
56
+ ```ini
57
+ ## vermink theme mytheme
58
+
59
+ [palette]
60
+ user = bright_green
61
+ host = bright_cyan
62
+ dir = bright_blue
63
+ git = bright_magenta
64
+ git_state = yellow
65
+ venv = bright_green
66
+ time = grey
67
+ exit_code = red
68
+
69
+ [prompt]
70
+ user
71
+ host
72
+ dir
73
+ git
74
+ git_state
75
+
76
+ [status]
77
+ venv
78
+ time
79
+ exit_code
80
+
81
+ [format]
82
+ user = user
83
+ host = host
84
+ dir = dir
85
+ git = git
86
+ git_state = git_state
87
+ venv = venv
88
+ time = time
89
+ exit_code = exit_code
90
+ ```
91
+
92
+ ### 区块说明
93
+
94
+ - **[palette]**:定义颜色名称,支持命名色(如 `red`、`bright_green`)、256 色(如 `208`)、HEX 色(如 `#ff8800`)、修饰符(如 `bold`、`dim`)
95
+ - **[prompt]**:左侧提示符显示的片段列表
96
+ - **[status]**:状态栏显示的片段列表(Zsh 为 RPROMPT,Bash/Pwsh 合并到主提示符)
97
+ - **[format]**:将片段映射到配色名,设为 `none` 表示不着色
98
+
99
+ ### 可用片段
100
+
101
+ | 片段 | 说明 |
102
+ | --- | --- |
103
+ | `user` | 当前用户名 |
104
+ | `host` | 主机名 |
105
+ | `dir` | 当前目录(~ 缩写) |
106
+ | `git` | Git 分支名 |
107
+ | `git_state` | Git 状态(`*` 有修改,`+` 有未跟踪文件) |
108
+ | `venv` | 虚拟环境名 |
109
+ | `time` | 当前时间(HH:MM) |
110
+ | `exit_code` | 上一条命令的退出码(非零时显示) |
111
+
112
+ ### 内置主题
113
+
114
+ - **dark**:深色终端主题(默认)
115
+ - **light**:浅色终端主题
116
+
117
+ ## 自定义主题
118
+
119
+ ```bash
120
+ # 保存主题到配置目录
121
+ vermink init --theme dark --name mytheme --no-install
122
+
123
+ # 编辑主题
124
+ vim ~/.config/vermink/themes/mytheme.conf
125
+
126
+ # 安装自定义主题
127
+ vermink install mytheme --shell zsh
128
+ ```
129
+
130
+ ## Shell 支持
131
+
132
+ | Shell | 提示符 | 状态栏 |
133
+ | --- | --- | --- |
134
+ | Zsh | PS1 | RPROMPT(右侧) |
135
+ | Bash | PS1(PROMPT_COMMAND) | 合并到主提示符 |
136
+ | PowerShell | prompt 函数 | 合并到主提示符 |
137
+
138
+ ## 开发
139
+
140
+ ```bash
141
+ # 安装开发依赖
142
+ pip install -e ".[test]"
143
+
144
+ # 运行测试
145
+ pytest tests/
146
+
147
+ # 代码检查
148
+ ruff check .
149
+ ```
150
+
151
+ ## 许可证
152
+
153
+ MIT
@@ -0,0 +1,136 @@
1
+ # vermink
2
+
3
+ 零依赖终端美化引擎:把纯文本主题编译成 Zsh / Bash / PowerShell 的提示符与状态栏。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pip install vermink
9
+ ```
10
+
11
+ ## 快速开始
12
+
13
+ ```bash
14
+ # 生成默认 dark 主题并安装到当前 shell
15
+ vermink init
16
+
17
+ # 预览主题效果(不修改任何文件)
18
+ vermink preview dark
19
+
20
+ # 列出所有主题
21
+ vermink list
22
+ ```
23
+
24
+ ## 子命令
25
+
26
+ | 命令 | 说明 |
27
+ | --- | --- |
28
+ | `vermink init [--shell zsh\|bash\|pwsh]` | 生成默认 dark 主题并安装 |
29
+ | `vermink preview <主题名> [--plain]` | 终端预览渲染效果 |
30
+ | `vermink install <主题名> --shell zsh\|bash\|pwsh` | 安装到 shell 启动文件(自动备份) |
31
+ | `vermink uninstall --shell zsh\|bash\|pwsh` | 卸载并还原备份 |
32
+ | `vermink list [--shell zsh\|bash\|pwsh]` | 列出主题与当前生效主题 |
33
+ | `vermink show <主题名>` | 打印主题源配置 |
34
+
35
+ ## 主题文件
36
+
37
+ 主题文件使用 INI 格式,包含以下区块:
38
+
39
+ ```ini
40
+ ## vermink theme mytheme
41
+
42
+ [palette]
43
+ user = bright_green
44
+ host = bright_cyan
45
+ dir = bright_blue
46
+ git = bright_magenta
47
+ git_state = yellow
48
+ venv = bright_green
49
+ time = grey
50
+ exit_code = red
51
+
52
+ [prompt]
53
+ user
54
+ host
55
+ dir
56
+ git
57
+ git_state
58
+
59
+ [status]
60
+ venv
61
+ time
62
+ exit_code
63
+
64
+ [format]
65
+ user = user
66
+ host = host
67
+ dir = dir
68
+ git = git
69
+ git_state = git_state
70
+ venv = venv
71
+ time = time
72
+ exit_code = exit_code
73
+ ```
74
+
75
+ ### 区块说明
76
+
77
+ - **[palette]**:定义颜色名称,支持命名色(如 `red`、`bright_green`)、256 色(如 `208`)、HEX 色(如 `#ff8800`)、修饰符(如 `bold`、`dim`)
78
+ - **[prompt]**:左侧提示符显示的片段列表
79
+ - **[status]**:状态栏显示的片段列表(Zsh 为 RPROMPT,Bash/Pwsh 合并到主提示符)
80
+ - **[format]**:将片段映射到配色名,设为 `none` 表示不着色
81
+
82
+ ### 可用片段
83
+
84
+ | 片段 | 说明 |
85
+ | --- | --- |
86
+ | `user` | 当前用户名 |
87
+ | `host` | 主机名 |
88
+ | `dir` | 当前目录(~ 缩写) |
89
+ | `git` | Git 分支名 |
90
+ | `git_state` | Git 状态(`*` 有修改,`+` 有未跟踪文件) |
91
+ | `venv` | 虚拟环境名 |
92
+ | `time` | 当前时间(HH:MM) |
93
+ | `exit_code` | 上一条命令的退出码(非零时显示) |
94
+
95
+ ### 内置主题
96
+
97
+ - **dark**:深色终端主题(默认)
98
+ - **light**:浅色终端主题
99
+
100
+ ## 自定义主题
101
+
102
+ ```bash
103
+ # 保存主题到配置目录
104
+ vermink init --theme dark --name mytheme --no-install
105
+
106
+ # 编辑主题
107
+ vim ~/.config/vermink/themes/mytheme.conf
108
+
109
+ # 安装自定义主题
110
+ vermink install mytheme --shell zsh
111
+ ```
112
+
113
+ ## Shell 支持
114
+
115
+ | Shell | 提示符 | 状态栏 |
116
+ | --- | --- | --- |
117
+ | Zsh | PS1 | RPROMPT(右侧) |
118
+ | Bash | PS1(PROMPT_COMMAND) | 合并到主提示符 |
119
+ | PowerShell | prompt 函数 | 合并到主提示符 |
120
+
121
+ ## 开发
122
+
123
+ ```bash
124
+ # 安装开发依赖
125
+ pip install -e ".[test]"
126
+
127
+ # 运行测试
128
+ pytest tests/
129
+
130
+ # 代码检查
131
+ ruff check .
132
+ ```
133
+
134
+ ## 许可证
135
+
136
+ MIT
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vermink"
7
+ version = "0.1.0"
8
+ description = "Zero-dependency terminal theme engine: compile text themes into Zsh/Bash/PowerShell prompts"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ keywords = ["terminal", "prompt", "theme", "zsh", "bash", "powershell"]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+
20
+ [project.scripts]
21
+ vermink = "vermink.cli:main"
22
+
23
+ [project.optional-dependencies]
24
+ test = ["pytest"]
25
+
26
+ [tool.setuptools.packages.find]
27
+ include = ["vermink*"]
28
+
29
+ [tool.ruff]
30
+ line-length = 100
31
+ target-version = "py311"
32
+
33
+ [tool.ruff.lint]
34
+ ignore = ["B905"]
35
+
36
+ [tool.ruff.lint.per-file-ignores]
37
+ "tests/*" = ["E501"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,155 @@
1
+ """Tests for vermink.backends module."""
2
+
3
+ import pytest
4
+
5
+ from vermink.backends import bash, preview, pwsh, zsh
6
+ from vermink.config import parse
7
+ from vermink.themes import BUILT_INS
8
+
9
+
10
+ @pytest.fixture
11
+ def dark():
12
+ return parse(BUILT_INS["dark"], name="dark")
13
+
14
+
15
+ @pytest.fixture
16
+ def light():
17
+ return parse(BUILT_INS["light"], name="light")
18
+
19
+
20
+ @pytest.fixture
21
+ def minimal():
22
+ return parse("[palette]\nuser = green\n[prompt]\nuser\n", name="minimal")
23
+
24
+
25
+ class TestZsh:
26
+ def test_name(self):
27
+ assert zsh.name == "zsh"
28
+
29
+ def test_render_contains_ps1(self, dark):
30
+ text = zsh.render(dark)
31
+ assert "PS1=" in text
32
+
33
+ def test_render_contains_rprompt(self, dark):
34
+ text = zsh.render(dark)
35
+ assert "RPROMPT=" in text
36
+
37
+ def test_render_has_precmd(self, dark):
38
+ text = zsh.render(dark)
39
+ assert "_j_precmd" in text
40
+ assert "add-zsh-hook" in text
41
+
42
+ def test_render_has_functions(self, dark):
43
+ text = zsh.render(dark)
44
+ assert "__j_git_state" in text
45
+ assert "__j_venv" in text
46
+ assert "__j_exit_code" in text
47
+
48
+ def test_render_minimal(self, minimal):
49
+ text = zsh.render(minimal)
50
+ assert "PS1=" in text
51
+ assert "RPROMPT=" not in text
52
+
53
+ def test_no_status_no_rprompt(self, minimal):
54
+ text = zsh.render(minimal)
55
+ assert "RPROMPT" not in text
56
+
57
+
58
+ class TestBash:
59
+ def test_name(self):
60
+ assert bash.name == "bash"
61
+
62
+ def test_render_contains_ps1(self, dark):
63
+ text = bash.render(dark)
64
+ assert "PS1=" in text
65
+
66
+ def test_render_contains_prompt_command(self, dark):
67
+ text = bash.render(dark)
68
+ assert "PROMPT_COMMAND=" in text
69
+
70
+ def test_render_has_functions(self, dark):
71
+ text = bash.render(dark)
72
+ assert "__j_git_branch" in text
73
+ assert "__j_git_state" in text
74
+ assert "__j_update_ps1" in text
75
+
76
+ def test_render_has_exit_code_capture(self, dark):
77
+ text = bash.render(dark)
78
+ assert "_J_EXIT=$?" in text
79
+
80
+
81
+ class TestPwsh:
82
+ def test_name(self):
83
+ assert pwsh.name == "pwsh"
84
+
85
+ def test_render_has_prompt_function(self, dark):
86
+ text = pwsh.render(dark)
87
+ assert "function prompt" in text
88
+
89
+ def test_render_has_default_color(self, dark):
90
+ text = pwsh.render(dark)
91
+ assert "$__j_default_color" in text
92
+
93
+ def test_render_has_psreadline(self, dark):
94
+ text = pwsh.render(dark)
95
+ assert "Set-PSReadLineOption" in text
96
+
97
+ def test_render_has_exit_capture(self, dark):
98
+ text = pwsh.render(dark)
99
+ assert "$__j_last_exit = $LASTEXITCODE" in text
100
+
101
+ def test_render_uses_consolecolor(self, dark):
102
+ text = pwsh.render(dark)
103
+ assert "[ConsoleColor]::" in text
104
+
105
+ def test_render_has_functions(self, dark):
106
+ text = pwsh.render(dark)
107
+ assert "__j_set_color" in text
108
+ assert "__j_seg" in text
109
+ assert "__j_sep" in text
110
+
111
+
112
+ class TestPreview:
113
+ def test_plain_output(self, dark):
114
+ text = preview.render(dark, plain=True)
115
+ assert "预览渲染" in text
116
+ assert "\x1b[" not in text
117
+
118
+ def test_contains_sample_data(self, dark):
119
+ text = preview.render(dark, plain=True)
120
+ assert "main" in text
121
+ assert "demo" in text
122
+
123
+ def test_contains_status_info(self, dark):
124
+ text = preview.render(dark, plain=True)
125
+ assert "x1" in text
126
+
127
+ def test_color_disabled_in_pipe(self, dark):
128
+ assert preview.color_enabled() is False
129
+
130
+ def test_note_present(self, dark):
131
+ text = preview.render(dark, plain=True)
132
+ assert "未修改任何文件" in text
133
+
134
+ def test_status_alignment_note(self, dark):
135
+ text = preview.render(dark, plain=True)
136
+ assert "RPROMPT" in text or "状态栏" in text
137
+
138
+
139
+ class TestBackendConsistency:
140
+ def test_all_backends_render_same_segments(self, dark):
141
+ for backend in (zsh, bash, pwsh):
142
+ text = backend.render(dark)
143
+ assert len(text) > 0
144
+
145
+ def test_all_backends_handle_empty_status(self, minimal):
146
+ for backend in (zsh, bash, pwsh):
147
+ text = backend.render(minimal)
148
+ assert len(text) > 0
149
+
150
+ def test_all_builtins_render(self):
151
+ for name, source_text in BUILT_INS.items():
152
+ theme = parse(source_text, name=name)
153
+ for backend in (zsh, bash, pwsh):
154
+ text = backend.render(theme)
155
+ assert len(text) > 0, f"{backend.name} failed for {name}"
@@ -0,0 +1,98 @@
1
+ """Tests for vermink.cli module."""
2
+
3
+ import pytest
4
+
5
+ from vermink.cli import main
6
+
7
+
8
+ class TestVersion:
9
+ def test_version(self, capsys):
10
+ with pytest.raises(SystemExit) as exc:
11
+ main(["--version"])
12
+ assert exc.value.code == 0
13
+ assert "0.1.0" in capsys.readouterr().out
14
+
15
+
16
+ class TestList:
17
+ def test_list_includes_builtins(self, capsys):
18
+ main(["list", "--shell", "zsh"])
19
+ out = capsys.readouterr().out
20
+ assert "dark" in out
21
+ assert "light" in out
22
+ assert "内置" in out
23
+
24
+ def test_list_no_rc(self, capsys, tmp_path):
25
+ main(["list", "--shell", "zsh", "--rc", str(tmp_path / ".zshrc")])
26
+ out = capsys.readouterr().out
27
+ assert "未安装" in out
28
+
29
+
30
+ class TestShow:
31
+ def test_show_builtin(self, capsys):
32
+ main(["show", "dark"])
33
+ out = capsys.readouterr().out
34
+ assert "[palette]" in out
35
+ assert "bright_green" in out
36
+
37
+ def test_show_unknown(self, capsys):
38
+ result = main(["show", "nonexistent"])
39
+ assert result == 1
40
+ assert "找不到" in capsys.readouterr().err
41
+
42
+
43
+ class TestPreview:
44
+ def test_preview_plain(self, capsys):
45
+ main(["preview", "dark", "--plain"])
46
+ out = capsys.readouterr().out
47
+ assert "预览渲染" in out
48
+ assert "main" in out
49
+
50
+
51
+ class TestInit:
52
+ def test_init_no_install(self, capsys, tmp_path):
53
+ theme_dir = tmp_path / "themes"
54
+ theme_dir.mkdir()
55
+ result = main(["init", "--theme", "dark", "--no-install", "--dir", str(theme_dir)])
56
+ assert result == 0
57
+ out = capsys.readouterr().out
58
+ assert "已保存" in out
59
+
60
+ def test_init_with_install(self, capsys, tmp_path):
61
+ rc = tmp_path / ".zshrc"
62
+ result = main(["init", "--theme", "dark", "--shell", "zsh", "--rc", str(rc)])
63
+ assert result == 0
64
+ assert rc.exists()
65
+ out = capsys.readouterr().out
66
+ assert "已安装" in out
67
+
68
+
69
+ class TestInstall:
70
+ def test_install_theme(self, capsys, tmp_path):
71
+ rc = tmp_path / ".zshrc"
72
+ result = main(["install", "dark", "--shell", "zsh", "--rc", str(rc)])
73
+ assert result == 0
74
+ assert rc.exists()
75
+ out = capsys.readouterr().out
76
+ assert "已安装" in out
77
+
78
+
79
+ class TestUninstall:
80
+ def test_uninstall(self, capsys, tmp_path):
81
+ rc = tmp_path / ".zshrc"
82
+ rc.write_text("# >>> vermink theme theme=dark\ncontent\n# <<< vermink theme\n")
83
+ result = main(["uninstall", "--shell", "zsh", "--rc", str(rc)])
84
+ assert result == 0
85
+ out = capsys.readouterr().out
86
+ assert "已卸载" in out
87
+
88
+
89
+ class TestErrorHandling:
90
+ def test_unknown_command(self, capsys):
91
+ with pytest.raises(SystemExit) as exc:
92
+ main(["badcmd"])
93
+ assert exc.value.code == 2
94
+
95
+ def test_missing_theme_arg(self, capsys):
96
+ with pytest.raises(SystemExit) as exc:
97
+ main(["show"])
98
+ assert exc.value.code == 2
@@ -0,0 +1,100 @@
1
+ """Tests for vermink.colors module."""
2
+
3
+ import pytest
4
+
5
+ from vermink.colors import (
6
+ COLORS,
7
+ MODIFIERS,
8
+ POWERSHELL_COLORS,
9
+ name_for_sgr,
10
+ powershell_color,
11
+ resolve,
12
+ )
13
+
14
+
15
+ class TestResolve:
16
+ def test_named_color(self):
17
+ assert resolve("red") == "31"
18
+
19
+ def test_bright_color(self):
20
+ assert resolve("bright_green") == "92"
21
+
22
+ def test_modifier(self):
23
+ assert resolve("bold") == "1"
24
+
25
+ def test_256_color(self):
26
+ assert resolve("208") == "38;5;208"
27
+
28
+ def test_hex_color(self):
29
+ assert resolve("#ff8800") == "38;2;255;136;0"
30
+
31
+ def test_composite(self):
32
+ assert resolve("bright_blue,bold") == "94;1"
33
+
34
+ def test_empty(self):
35
+ assert resolve("") == ""
36
+
37
+ def test_none_keyword(self):
38
+ assert resolve("none") == ""
39
+
40
+ def test_case_insensitive(self):
41
+ assert resolve("RED") == "31"
42
+ assert resolve("Bright_Green") == "92"
43
+
44
+ def test_unknown_color_raises(self):
45
+ with pytest.raises(Exception, match="未知颜色"):
46
+ resolve("not_a_color")
47
+
48
+ def test_invalid_hex_raises(self):
49
+ with pytest.raises(Exception, match="未知颜色"):
50
+ resolve("#gg0000")
51
+
52
+ def test_context_in_error(self):
53
+ with pytest.raises(Exception, match="配色 foo"):
54
+ resolve("bad", context="配色 foo")
55
+
56
+ def test_path_and_line_in_error(self):
57
+ with pytest.raises(Exception, match="theme.conf:42"):
58
+ resolve("bad", path="theme.conf", line=42)
59
+
60
+
61
+ class TestPowershellColor:
62
+ def test_named_color(self):
63
+ assert powershell_color("red") == "DarkRed"
64
+
65
+ def test_bright_color(self):
66
+ assert powershell_color("bright_green") == "Green"
67
+
68
+ def test_alias(self):
69
+ assert powershell_color("grey") == "Gray"
70
+
71
+ def test_unknown_returns_empty(self):
72
+ assert powershell_color("208") == ""
73
+
74
+ def test_hex_returns_empty(self):
75
+ assert powershell_color("#ff0000") == ""
76
+
77
+ def test_composite_returns_first(self):
78
+ assert powershell_color("bright_blue,bold") == "Blue"
79
+
80
+
81
+ class TestNameForSgr:
82
+ def test_basic_colors(self):
83
+ assert name_for_sgr("31") == "red"
84
+ assert name_for_sgr("92") == "bright_green"
85
+
86
+ def test_unknown_returns_empty(self):
87
+ assert name_for_sgr("999") == ""
88
+
89
+ def test_composite_returns_empty(self):
90
+ assert name_for_sgr("94;1") == ""
91
+
92
+
93
+ class TestConstants:
94
+ def test_colors_is_subset_of_powershell(self):
95
+ for name in COLORS:
96
+ assert name in POWERSHELL_COLORS, f"{name} missing from POWERSHELL_COLORS"
97
+
98
+ def test_modifiers_not_in_colors(self):
99
+ for name in MODIFIERS:
100
+ assert name not in COLORS