#!/usr/bin/env python3 """Tools Tab — Tool usage breakdown + skill usage.""" import sys from pathlib import Path import gi gi.require_version("Gtk", "3.0") from gi.repository import GLib, Gtk sys.path.insert(0, str(Path.home() / "Projekte" / "ClaudeCodePanel")) from theme import get_palette from monitor import get_top_tools, get_skill_usage, get_missed_skills_summary class ToolsTab(Gtk.Box): """Tool + skill usage display.""" def __init__(self): super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=8) self.set_margin_top(12) self.set_margin_start(12) self.set_margin_end(12) self._tools_box = None self._skills_box = None self._missed_box = None self._build_ui() def _build_ui(self): p = get_palette() # --- Tool usage --- tools_lbl = Gtk.Label() tools_lbl.set_markup('Tool Usage (Today)') tools_lbl.set_halign(Gtk.Align.START) tools_lbl.get_style_context().add_class("section-title") self.pack_start(tools_lbl, False, False, 0) self._tools_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) self._tools_box.get_style_context().add_class("base-card") self.pack_start(self._tools_box, False, False, 0) # --- Skill usage --- skills_lbl = Gtk.Label() skills_lbl.set_markup('Skill Usage (Today)') skills_lbl.set_halign(Gtk.Align.START) skills_lbl.get_style_context().add_class("section-title") self.pack_start(skills_lbl, False, False, 0) self._skills_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) self._skills_box.get_style_context().add_class("base-card") self.pack_start(self._skills_box, False, False, 0) # --- Missed skills --- missed_lbl = Gtk.Label() missed_lbl.set_markup('Missed Skills') missed_lbl.set_halign(Gtk.Align.START) missed_lbl.get_style_context().add_class("section-title") self.pack_start(missed_lbl, False, False, 0) self._missed_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) self._missed_box.get_style_context().add_class("base-card") self.pack_start(self._missed_box, False, False, 0) def refresh(self) -> None: """Reload tool/skill data from monitor.py.""" p = get_palette() # Tool usage self._clear_box(self._tools_box) tools = get_top_tools(n=15) if tools: max_count = tools[0][1] if tools else 1 for name, count in tools: row = self._build_bar_row(name, count, max_count, p["accent"], p) self._tools_box.pack_start(row, False, False, 0) else: self._add_empty(self._tools_box, "No tool data yet", p) self._tools_box.show_all() # Skill usage self._clear_box(self._skills_box) skills = get_skill_usage(days=1) if skills: # Flatten today's skills from datetime import datetime today = datetime.now().strftime("%Y-%m-%d") today_skills = skills.get(today, {}) if today_skills: sorted_skills = sorted(today_skills.items(), key=lambda x: -x[1]) max_s = sorted_skills[0][1] if sorted_skills else 1 for name, count in sorted_skills: row = self._build_bar_row(name, count, max_s, p["mauve"], p) self._skills_box.pack_start(row, False, False, 0) else: self._add_empty(self._skills_box, "No skills used today", p) else: self._add_empty(self._skills_box, "No skill data yet", p) self._skills_box.show_all() # Missed skills self._clear_box(self._missed_box) missed = get_missed_skills_summary() if missed: for name, count in missed[:8]: row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) name_lbl = Gtk.Label() name_lbl.set_markup(f'{GLib.markup_escape_text(name)}') name_lbl.set_xalign(0) name_lbl.set_width_chars(20) row.pack_start(name_lbl, False, False, 0) count_lbl = Gtk.Label() count_lbl.set_markup( f'{count} missed' ) row.pack_end(count_lbl, False, False, 0) self._missed_box.pack_start(row, False, False, 0) else: self._add_empty(self._missed_box, "No missed skills", p) self._missed_box.show_all() def _build_bar_row(self, name: str, count: int, max_count: int, bar_color: str, p: dict) -> Gtk.Box: """Build a row with name, count, and a proportional bar.""" row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) name_lbl = Gtk.Label() name_lbl.set_markup(f'{GLib.markup_escape_text(name)}') name_lbl.set_xalign(0) name_lbl.set_width_chars(20) row.pack_start(name_lbl, False, False, 0) # Visual bar bar = Gtk.DrawingArea() frac = count / max_count if max_count > 0 else 0 bar.set_size_request(max(4, int(frac * 150)), 12) bar.connect("draw", self._draw_mini_bar, bar_color) bar.set_valign(Gtk.Align.CENTER) row.pack_start(bar, False, False, 0) count_lbl = Gtk.Label() count_lbl.set_markup( f'{count}' ) row.pack_start(count_lbl, False, False, 0) return row @staticmethod def _draw_mini_bar(widget, cr, color_hex): width = widget.get_allocated_width() height = widget.get_allocated_height() r = int(color_hex[1:3], 16) / 255 g = int(color_hex[3:5], 16) / 255 b = int(color_hex[5:7], 16) / 255 cr.set_source_rgba(r, g, b, 0.6) # Rounded rectangle radius = 3 cr.arc(radius, radius, radius, 3.14159, 1.5 * 3.14159) cr.arc(width - radius, radius, radius, 1.5 * 3.14159, 0) cr.arc(width - radius, height - radius, radius, 0, 0.5 * 3.14159) cr.arc(radius, height - radius, radius, 0.5 * 3.14159, 3.14159) cr.close_path() cr.fill() return False @staticmethod def _clear_box(box): for child in box.get_children(): box.remove(child) @staticmethod def _add_empty(box, text, p): lbl = Gtk.Label() lbl.set_markup(f'{text}') box.pack_start(lbl, False, False, 0)