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

1from pathlib import Path 1KLMNOPQRS

2 

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

8 

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} 

43 

44console = Console(theme=Theme(RICH_THEME)) 1KLMNOPQRS

45 

46 

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 !

49 

50 

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

55 

56 

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 !

59 

60 

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!

71 

72 

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

75 

76 

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

79 

80 

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

83 

84 

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

92 

93 

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

97 

98 

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!

106 

107 

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 !

117 

118 

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

129 

130 

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

139 

140 

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 !

143 

144 

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 ) 

156 

157 

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 ) 

162 

163 

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 !

169 

170 

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

184 

185 

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