mirror of
https://github.com/overcuriousity/trace.git
synced 2025-12-20 04:52:21 +00:00
Comprehensive visual design improvements for TUI
This commit implements a complete visual redesign and refactoring to improve consistency, accessibility, and maintainability of the TUI interface. ## New Visual Constants Module - Created trace/tui/visual_constants.py with centralized constants: - Layout: Screen positioning and structure (header, content, footer) - Spacing: Padding and margins (dialogs, horizontal padding) - ColumnWidths: Fixed column widths for lists (tags, IOCs, content) - DialogSize: Standard dialog dimensions (small, medium, large) - Icons: Unicode symbols used throughout UI - Timing: Animation and feedback timing constants ## Color System Improvements - Fixed duplicate color definitions (removed from tui_app.py) - Use ColorPairs constants throughout instead of hardcoded numbers - Separated tag colors from footer colors: - Tags now use magenta (ColorPairs.TAG) instead of yellow - Footers keep yellow (ColorPairs.WARNING) for consistency - Updated TAG_SELECTED to magenta on cyan - All 129+ color_pair() calls now use semantic ColorPairs constants ## Accessibility Enhancements - Added warning icons (⚠) to all IOC displays for visual + color cues - Tag highlighting now uses distinct magenta color - Improved color semantics reduce reliance on color alone - Smart text truncation at word boundaries for better readability ## Layout & Spacing Standardization - Replaced magic numbers with Layout/Spacing constants: - Footer positioning: height - Layout.FOOTER_OFFSET_FROM_BOTTOM - Content area: Layout.CONTENT_START_Y - Truncation: width - Spacing.HORIZONTAL_PADDING - Dialog margins: Spacing.DIALOG_MARGIN - Standardized dialog sizes using DialogSize constants: - Input dialogs: DialogSize.MEDIUM - Multiline dialogs: DialogSize.LARGE - Confirm dialogs: DialogSize.SMALL (with dynamic width) - Settings dialog: DialogSize.MEDIUM ## User Experience Improvements - Enhanced footer command organization with visual grouping: - Used Icons.SEPARATOR_GROUP (│) to group related commands - Example: "[n] Add Note │ [t] Tags [i] IOCs │ [v] View [e] Export" - Smart content truncation (_safe_truncate): - Added word_break parameter (default True) - Breaks at word boundaries when >60% text retained - Maintains Unicode safety while improving readability - Improved empty state messages: - New _draw_empty_state() helper for consistent visual structure - Centered boxes with proper spacing - Clear call-to-action hints - Applied to "No cases found" and "No cases match filter" ## Code Quality & Maintainability - Eliminated hardcoded spacing values throughout 3,468-line file - Used Icons constants for all Unicode symbols (─│┌└◆●○▸⚠⌗◈✓✗?) - Fixed circular import issues with delayed global imports in TUI.__init__ - Updated comments to reflect new ColorPairs constants - Consistent use of f-strings for footer construction ## Visual Consistency - Replaced all "─" literals with Icons.SEPARATOR_H - Standardized truncation widths (width - Spacing.HORIZONTAL_PADDING) - Consistent use of ColumnWidths for tag (30) and IOC (50+2) displays - All dialogs now use standard sizes from visual_constants ## Testing - Verified no syntax errors in all modified files - Confirmed successful module imports - Tested CLI functionality (--help, --list) - Backward compatibility maintained This establishes a strong foundation for future UI enhancements while significantly improving code maintainability and visual consistency.
This commit is contained in:
@@ -39,5 +39,5 @@ def init_colors():
|
|||||||
curses.init_pair(ColorPairs.TAG, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
|
curses.init_pair(ColorPairs.TAG, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
|
||||||
# IOCs on selected background (red on cyan)
|
# IOCs on selected background (red on cyan)
|
||||||
curses.init_pair(ColorPairs.IOC_SELECTED, curses.COLOR_RED, curses.COLOR_CYAN)
|
curses.init_pair(ColorPairs.IOC_SELECTED, curses.COLOR_RED, curses.COLOR_CYAN)
|
||||||
# Tags on selected background (yellow on cyan)
|
# Tags on selected background (magenta on cyan)
|
||||||
curses.init_pair(ColorPairs.TAG_SELECTED, curses.COLOR_YELLOW, curses.COLOR_CYAN)
|
curses.init_pair(ColorPairs.TAG_SELECTED, curses.COLOR_MAGENTA, curses.COLOR_CYAN)
|
||||||
|
|||||||
@@ -113,15 +113,15 @@ class TextRenderer:
|
|||||||
screen.addstr(y, x_pos, text)
|
screen.addstr(y, x_pos, text)
|
||||||
screen.attroff(curses.color_pair(ColorPairs.ERROR) | curses.A_BOLD)
|
screen.attroff(curses.color_pair(ColorPairs.ERROR) | curses.A_BOLD)
|
||||||
else: # tag
|
else: # tag
|
||||||
# Tag highlighting: yellow on cyan if selected, yellow on black otherwise
|
# Tag highlighting: magenta on cyan if selected, magenta on black otherwise
|
||||||
if is_selected:
|
if is_selected:
|
||||||
screen.attron(curses.color_pair(ColorPairs.TAG_SELECTED))
|
screen.attron(curses.color_pair(ColorPairs.TAG_SELECTED))
|
||||||
screen.addstr(y, x_pos, text)
|
screen.addstr(y, x_pos, text)
|
||||||
screen.attroff(curses.color_pair(ColorPairs.TAG_SELECTED))
|
screen.attroff(curses.color_pair(ColorPairs.TAG_SELECTED))
|
||||||
else:
|
else:
|
||||||
screen.attron(curses.color_pair(ColorPairs.WARNING))
|
screen.attron(curses.color_pair(ColorPairs.TAG))
|
||||||
screen.addstr(y, x_pos, text)
|
screen.addstr(y, x_pos, text)
|
||||||
screen.attroff(curses.color_pair(ColorPairs.WARNING))
|
screen.attroff(curses.color_pair(ColorPairs.TAG))
|
||||||
|
|
||||||
x_pos += len(text)
|
x_pos += len(text)
|
||||||
last_pos = end
|
last_pos = end
|
||||||
|
|||||||
68
trace/tui/visual_constants.py
Normal file
68
trace/tui/visual_constants.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
"""Visual constants for consistent TUI layout and styling"""
|
||||||
|
|
||||||
|
|
||||||
|
class Layout:
|
||||||
|
"""Screen layout constants"""
|
||||||
|
HEADER_Y = 0
|
||||||
|
HEADER_X = 2
|
||||||
|
CONTENT_START_Y = 2
|
||||||
|
CONTENT_INDENT = 4
|
||||||
|
FOOTER_OFFSET_FROM_BOTTOM = 3
|
||||||
|
BORDER_OFFSET_FROM_BOTTOM = 2
|
||||||
|
|
||||||
|
|
||||||
|
class Spacing:
|
||||||
|
"""Spacing and padding constants"""
|
||||||
|
SECTION_VERTICAL_GAP = 2
|
||||||
|
ITEM_VERTICAL_GAP = 1
|
||||||
|
DIALOG_MARGIN = 4
|
||||||
|
HORIZONTAL_PADDING = 6 # width - 6 for truncation
|
||||||
|
HASH_DISPLAY_PADDING = 20 # width - 20
|
||||||
|
|
||||||
|
|
||||||
|
class ColumnWidths:
|
||||||
|
"""Fixed column widths for list displays"""
|
||||||
|
TAG_COLUMN = 30
|
||||||
|
IOC_COLUMN = 50
|
||||||
|
CONTENT_PREVIEW = 50
|
||||||
|
NOTE_PREVIEW = 60
|
||||||
|
|
||||||
|
|
||||||
|
class DialogSize:
|
||||||
|
"""Standard dialog dimensions (width, height)"""
|
||||||
|
SMALL = (40, 8) # Confirm dialogs
|
||||||
|
MEDIUM = (60, 15) # Settings, single input
|
||||||
|
LARGE = (70, 20) # Multiline, help
|
||||||
|
|
||||||
|
|
||||||
|
class Icons:
|
||||||
|
"""Unicode symbols used throughout UI"""
|
||||||
|
ACTIVE = "●"
|
||||||
|
INACTIVE = "○"
|
||||||
|
DIAMOND = "◆"
|
||||||
|
SQUARE = "■"
|
||||||
|
SMALL_SQUARE = "▪"
|
||||||
|
ARROW_RIGHT = "▸"
|
||||||
|
WARNING = "⚠"
|
||||||
|
HASH = "⌗"
|
||||||
|
FILTER = "◈"
|
||||||
|
VERIFIED = "✓"
|
||||||
|
FAILED = "✗"
|
||||||
|
UNSIGNED = "?"
|
||||||
|
SEPARATOR_H = "─"
|
||||||
|
SEPARATOR_V = "│"
|
||||||
|
SEPARATOR_GROUP = "│" # For grouping footer commands
|
||||||
|
BOX_TL = "┌"
|
||||||
|
BOX_BL = "└"
|
||||||
|
# Box drawing for improved empty states
|
||||||
|
BOX_DOUBLE_TL = "╔"
|
||||||
|
BOX_DOUBLE_TR = "╗"
|
||||||
|
BOX_DOUBLE_BL = "╚"
|
||||||
|
BOX_DOUBLE_BR = "╝"
|
||||||
|
BOX_DOUBLE_H = "═"
|
||||||
|
BOX_DOUBLE_V = "║"
|
||||||
|
|
||||||
|
|
||||||
|
class Timing:
|
||||||
|
"""Timing constants"""
|
||||||
|
FLASH_MESSAGE_DURATION = 3 # seconds
|
||||||
421
trace/tui_app.py
421
trace/tui_app.py
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user