Coverage for src / library_skills / ui.py: 100%
96 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-03 20:19 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-03 20:19 +0000
1from pathlib import Path 1KLMNOPQRS
3from rich.console import Console 1KLMNOPQRS
4from rich.table import Table 1KLMNOPQRS
5from rich.theme import Theme 1KLMNOPQRS
6from rich_toolkit import RichToolkit 1KLMNOPQRS
7from rich_toolkit.styles import MinimalStyle 1KLMNOPQRS
9RICH_THEME = { 1KLMNOPQRS
10 "active": "green",
11 "badge.action.install": "bold white on green",
12 "badge.action.remove": "bold white on red",
13 "badge.action.repair": "bold white on blue",
14 "badge.attention": "bold white on red",
15 "badge.context": "bold white on dark_cyan",
16 "badge.hint": "black on yellow",
17 "badge.result": "bold white on green",
18 "badge.status": "bold white on blue",
19 "badge.warning": "bold white on dark_orange",
20 "cancelled": "red italic",
21 "error": "red",
22 "hint": "dim",
23 "placeholder": "grey62",
24 "placeholder.cancelled": "indian_red strike",
25 "progress": "on green",
26 "result": "white",
27 "selected": "green",
28 "status.broken": "red",
29 "status.hand-authored": "dim",
30 "status.name-mismatch": "yellow",
31 "status.new": "green",
32 "status.orphaned": "red",
33 "status.outdated": "yellow",
34 "status.up-to-date": "dim",
35 "success": "green",
36 "tag": "bold",
37 "tag.title": "bold",
38 "text": "white",
39 "title.cancelled": "white",
40 "title.error": "white",
41 "warning": "yellow",
42}
44console = Console(theme=Theme(RICH_THEME)) 1KLMNOPQRS
47def _console(override: Console | None = None) -> Console: 1KLMNOPQRS
48 return override or console 2: ; B H * m d a e # y f Ub9bnb` { | Vb} .b/b:b~ ab;bFb$ T IbWbXbYbicjcU V + Lb!bC qb] W p q r X Y = ? D I , n g b h % z i Zb#bobbbcbdb0beb=b?b@bfbgb[bGb' Z Jb1b2b3bkclc0 1 - Mb$bE rb^ 2 s t u 3 4 @ [ F J . o j c k ( A l 4b%bpbhbibjb5bkb]b^b_blbmb`bHb) 5 Kb6b7b8bmcnc6 7 / Nb'bG sb_ 8 v w x 9 !
51def get_toolkit(*, console: Console | None = None) -> RichToolkit: 1KLMNOPQRS
52 style = MinimalStyle(theme=RICH_THEME) 29b!b{b|b#b$b}b~b%b'bacbc
53 style.console = _console(console) 29b!b{b|b#b$b}b~b%b'bacbc
54 return RichToolkit(style=style) 29b!b{b|b#b$b}b~b%b'bacbc
57def print_line(*, console: Console | None = None) -> None: 1KLMNOPQRS
58 _console(console).print() 2: ; B H * m d a e # y f nb` { | } $ T U V + C ] W p q r X Y = ? D I , n g b h % z i obbbcbdbeb' Z 0 1 - E ^ 2 s t u 3 4 @ [ F J . o j c k ( A l pbhbibjbkb) 5 6 7 / G _ 8 v w x 9 !
61def print_title( 1KLMNOPQRS
62 title: str,
63 *,
64 console: Console | None = None,
65 before: bool = True,
66) -> None:
67 target_console = _console(console) 1:;BH*mdae#yf$TUV+CWpqrXY=?DI,ngbh%zi'Z01-E2stu34@[FJ.ojck(Al)567/G8vwx9!
68 if before: 1:;BH*mdae#yf$TUV+CWpqrXY=?DI,ngbh%zi'Z01-E2stu34@[FJ.ojck(Al)567/G8vwx9!
69 target_console.print() 1:;BH*mdae#yf$TUV+CWpqrXY=?DI,ngbh%zi'Z01-E2stu34@[FJ.ojck(Al)567/G8vwx9!
70 target_console.print(_badge(title, title)) 1:;BH*mdae#yf$TUV+CWpqrXY=?DI,ngbh%zi'Z01-E2stu34@[FJ.ojck(Al)567/G8vwx9!
73def print_warning(message: str, *, console: Console | None = None) -> None: 1KLMNOPQRS
74 _console(console).print(f"{_badge('warning', 'Warning:')} {message}") 2* Ub+ ] p q r tbub, Zb- ^ s t u vbwb. 4b/ _ v w x xbyb
77def print_hint(message: str, *, console: Console | None = None) -> None: 1KLMNOPQRS
78 _console(console).print(f"{_badge('hint', 'Tip:')} {message}") 2d a ` { | } ] p q r tbubg b bbcbdbeb^ s t u vbwbj c hbibjbkb_ v w x xbyb
81def print_message(message: str, *, console: Console | None = None) -> None: 1KLMNOPQRS
82 _console(console).print(message) 2: ; H * m d a # y nb` { | } .b/b:b;b$ Ibicjc+ C ] p q r tbub= ? I , n g b % z obbbcbdbeb=b?b@b[b' Jbkclc- E ^ s t u vbwb@ [ J . o j c ( A pbhbibjbkb]b^b_b`b) Kbmcnc/ G _ v w x xbyb
85def print_action_header( 1KLMNOPQRS
86 action: str, *, console: Console | None = None, before: bool = True
87) -> None:
88 target_console = _console(console) 2IbLb(b)bJbMb*b+bKbNb,b-b
89 if before: 2IbLb(b)bJbMb*b+bKbNb,b-b
90 target_console.print() 2IbLb(b)bJbMb*b+bKbNb,b-b
91 target_console.print(f"{_badge(f'action.{action}', action)}") 2IbLb(b)bJbMb*b+bKbNb,b-b
94def print_warnings(warnings: list[str], *, console: Console | None = None) -> None: 1KLMNOPQRS
95 for warning in warnings: 2: ; B H * m d a e # y f nb` { | } .b/b:b~ ab;bFb$ T U V + C ] W p q r X tbY ub= ? D I , n g b h % z i obbbcbdbeb=b?b@bfbgb[bGb' Z 0 1 - E ^ 2 s t u 3 vb4 wb@ [ F J . o j c k ( A l pbhbibjbkb]b^b_blbmb`bHb) 5 6 7 / G _ 8 v w x 9 xb! yb
96 print_warning(warning, console=console) 2* + ] tbub, - ^ vbwb. / _ xbyb
99def context_table(rows: list[tuple[str, str]]) -> Table: 1KLMNOPQRS
100 table = Table.grid(padding=(0, 2), pad_edge=False) 1:;BH*mdae#yf$TUV+CWpqrXY=?DI,ngbh%zi'Z01-E2stu34@[FJ.ojck(Al)567/G8vwx9!
101 table.add_column("Field", style="bold") 1:;BH*mdae#yf$TUV+CWpqrXY=?DI,ngbh%zi'Z01-E2stu34@[FJ.ojck(Al)567/G8vwx9!
102 table.add_column("Value") 1:;BH*mdae#yf$TUV+CWpqrXY=?DI,ngbh%zi'Z01-E2stu34@[FJ.ojck(Al)567/G8vwx9!
103 for label, value in rows: 1:;BH*mdae#yf$TUV+CWpqrXY=?DI,ngbh%zi'Z01-E2stu34@[FJ.ojck(Al)567/G8vwx9!
104 table.add_row(label, value) 1:;BH*mdae#yf$TUV+CWpqrXY=?DI,ngbh%zi'Z01-E2stu34@[FJ.ojck(Al)567/G8vwx9!
105 return table 1:;BH*mdae#yf$TUV+CWpqrXY=?DI,ngbh%zi'Z01-E2stu34@[FJ.ojck(Al)567/G8vwx9!
108def skills_table(rows: list[tuple[str, str, str, str]]) -> Table: 1KLMNOPQRS
109 table = Table(show_header=True, header_style="bold", box=None) 2FbT U V W X Y GbZ 0 1 2 3 4 Hb5 6 7 8 9 !
110 table.add_column("Skill", style="bold") 2FbT U V W X Y GbZ 0 1 2 3 4 Hb5 6 7 8 9 !
111 table.add_column("Package") 2FbT U V W X Y GbZ 0 1 2 3 4 Hb5 6 7 8 9 !
112 table.add_column("Version") 2FbT U V W X Y GbZ 0 1 2 3 4 Hb5 6 7 8 9 !
113 table.add_column("Description") 2FbT U V W X Y GbZ 0 1 2 3 4 Hb5 6 7 8 9 !
114 for row in rows: 2FbT U V W X Y GbZ 0 1 2 3 4 Hb5 6 7 8 9 !
115 table.add_row(*row) 2FbT U V W X Y GbZ 0 1 2 3 4 Hb5 6 7 8 9 !
116 return table 2FbT U V W X Y GbZ 0 1 2 3 4 Hb5 6 7 8 9 !
119def status_table(rows: list[tuple[str, str, str, str, str]]) -> Table: 1KLMNOPQRS
120 table = Table(show_header=True, header_style="bold", box=None) 2B m d a e y f ~ abp q r zbAbD n g b h z i fbgbs t u BbCbF o j c k A l lbmbv w x DbEb
121 table.add_column("Target") 2B m d a e y f ~ abp q r zbAbD n g b h z i fbgbs t u BbCbF o j c k A l lbmbv w x DbEb
122 table.add_column("Skill", style="bold") 2B m d a e y f ~ abp q r zbAbD n g b h z i fbgbs t u BbCbF o j c k A l lbmbv w x DbEb
123 table.add_column("Status") 2B m d a e y f ~ abp q r zbAbD n g b h z i fbgbs t u BbCbF o j c k A l lbmbv w x DbEb
124 table.add_column("Path") 2B m d a e y f ~ abp q r zbAbD n g b h z i fbgbs t u BbCbF o j c k A l lbmbv w x DbEb
125 table.add_column("Source") 2B m d a e y f ~ abp q r zbAbD n g b h z i fbgbs t u BbCbF o j c k A l lbmbv w x DbEb
126 for target, skill, status, path, source in rows: 2B m d a e y f ~ abp q r zbAbD n g b h z i fbgbs t u BbCbF o j c k A l lbmbv w x DbEb
127 table.add_row(target, skill, _styled_status(status), path, source) 2B m d a e y f ~ abp q r zbAbD n g b h z i fbgbs t u BbCbF o j c k A l lbmbv w x DbEb
128 return table 2B m d a e y f ~ abp q r zbAbD n g b h z i fbgbs t u BbCbF o j c k A l lbmbv w x DbEb
131def tool_skill_status_table(rows: list[tuple[str, str, str]]) -> Table: 1KLMNOPQRS
132 table = Table(show_header=True, header_style="bold", box=None) 2H a e f C ObPbI b h i E QbRbJ c k l G SbTb
133 table.add_column("Target") 2H a e f C ObPbI b h i E QbRbJ c k l G SbTb
134 table.add_column("Status") 2H a e f C ObPbI b h i E QbRbJ c k l G SbTb
135 table.add_column("Path") 2H a e f C ObPbI b h i E QbRbJ c k l G SbTb
136 for target, status, path in rows: 2H a e f C ObPbI b h i E QbRbJ c k l G SbTb
137 table.add_row(target, _styled_status(status), path) 2H a e f C ObPbI b h i E QbRbJ c k l G SbTb
138 return table 2H a e f C ObPbI b h i E QbRbJ c k l G SbTb
141def print_table(table: Table, *, console: Console | None = None) -> None: 1KLMNOPQRS
142 _console(console).print(table) 2: ; B H * m d a e # y f ~ abFb$ T U V + C W p q r X Y = ? D I , n g b h % z i fbgbGb' Z 0 1 - E 2 s t u 3 4 @ [ F J . o j c k ( A l lbmbHb) 5 6 7 / G 8 v w x 9 !
145def print_result( 1KLMNOPQRS
146 action: str,
147 name: str,
148 target: str,
149 path: Path | str,
150 *,
151 console: Console | None = None,
152) -> None:
153 _console(console).print( 2m d a e nb` { | } WbXbYbC ] p q r tbubn g b h obbbcbdbeb1b2b3bE ^ s t u vbwbo j c k pbhbibjbkb6b7b8bG _ v w x xbyb
154 f"{_badge('result', action + ':')} [bold]{name}[/bold] ({target}) -> {path}"
155 )
158def print_skipped(name: str, reason: str, *, console: Console | None = None) -> None: 1KLMNOPQRS
159 _console(console).print( 2f Vbucvci 0bwcxcl 5byczc
160 f"{_badge('warning', 'Skipped:')} [bold]{name}[/bold]: {reason}"
161 )
164def _badge(kind: str, label: str) -> str: 1KLMNOPQRS
165 style = f"badge.{kind}" 2: ; B H * m d a e # y f Ubnb` { | Vb} $ T IbWbXbYbU V + LbC qb] W p q r X Y = ? D I , n g b h % z i Zbobbbcbdb0beb' Z Jb1b2b3b0 1 - MbE rb^ 2 s t u 3 4 @ [ F J . o j c k ( A l 4bpbhbibjb5bkb) 5 Kb6b7b8b6 7 / NbG sb_ 8 v w x 9 !
166 if style not in RICH_THEME: 2: ; B H * m d a e # y f Ubnb` { | Vb} $ T IbWbXbYbU V + LbC qb] W p q r X Y = ? D I , n g b h % z i Zbobbbcbdb0beb' Z Jb1b2b3b0 1 - MbE rb^ 2 s t u 3 4 @ [ F J . o j c k ( A l 4bpbhbibjb5bkb) 5 Kb6b7b8b6 7 / NbG sb_ 8 v w x 9 !
167 style = "tag" 2qbccdcrbecfcsbgchc
168 return f"[{style}] {label} [/]" 2: ; B H * m d a e # y f Ubnb` { | Vb} $ T IbWbXbYbU V + LbC qb] W p q r X Y = ? D I , n g b h % z i Zbobbbcbdb0beb' Z Jb1b2b3b0 1 - MbE rb^ 2 s t u 3 4 @ [ F J . o j c k ( A l 4bpbhbibjb5bkb) 5 Kb6b7b8b6 7 / NbG sb_ 8 v w x 9 !
171def _styled_status(status: str) -> str: 1KLMNOPQRS
172 style = { 2B H m d a e y f ~ abC qbp q r zbAbD I n g b h z i fbgbE rbs t u BbCbF J o j c k A l lbmbG sbv w x DbEb
173 "broken": "status.broken",
174 "hand-authored": "status.hand-authored",
175 "name mismatch": "status.name-mismatch",
176 "new": "status.new",
177 "orphaned": "status.orphaned",
178 "outdated": "status.outdated",
179 "up to date": "status.up-to-date",
180 }.get(status)
181 if style is None: 2B H m d a e y f ~ abC qbp q r zbAbD I n g b h z i fbgbE rbs t u BbCbF J o j c k A l lbmbG sbv w x DbEb
182 return status 2H a e f C qbObPbI b h i E rbQbRbJ c k l G sbSbTb
183 return f"[{style}]{status}[/]" 2B m d a e y f ~ abp q r zbAbD n g b h z i fbgbs t u BbCbF o j c k A l lbmbv w x DbEb
186def print_summary(items: dict[str, int], *, console: Console | None = None) -> None: 1KLMNOPQRS
187 non_zero = {name: count for name, count in items.items() if count} 2m d a # nb` { | } $ qb] tbubn g b % obbbcbdbeb' rb^ vbwbo j c ( pbhbibjbkb) sb_ xbyb
188 if not non_zero: 2m d a # nb` { | } $ qb] tbubn g b % obbbcbdbeb' rb^ vbwbo j c ( pbhbibjbkb) sb_ xbyb
189 print_message("No changes needed.", console=console) 2# $ ocpc% ' qcrc( ) sctc
190 return 2# $ ocpc% ' qcrc( ) sctc
191 if set(non_zero) == {"installed skill targets"}: 2m d a nb` { | } qb] tbubn g b obbbcbdbebrb^ vbwbo j c pbhbibjbkbsb_ xbyb
192 count = non_zero["installed skill targets"] 2m d a nb` { | } ] tbubn g b obbbcbdbeb^ vbwbo j c pbhbibjbkb_ xbyb
193 print_message(f"Installed {count} skill target(s).", console=console) 2m d a nb` { | } ] tbubn g b obbbcbdbeb^ vbwbo j c pbhbibjbkb_ xbyb
194 return 2m d a nb` { | } ] tbubn g b obbbcbdbeb^ vbwbo j c pbhbibjbkb_ xbyb
195 summary = ", ".join(f"{name}: {count}" for name, count in non_zero.items()) 2qbccdcrbecfcsbgchc
196 _console(console).print(f"{_badge('result', 'Summary:')} {summary}") 2qbccdcrbecfcsbgchc