titan-cli 0.1.0__py3-none-any.whl

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.
Files changed (146) hide show
  1. titan_cli/__init__.py +3 -0
  2. titan_cli/__main__.py +4 -0
  3. titan_cli/ai/__init__.py +0 -0
  4. titan_cli/ai/agents/__init__.py +15 -0
  5. titan_cli/ai/agents/base.py +152 -0
  6. titan_cli/ai/client.py +170 -0
  7. titan_cli/ai/constants.py +56 -0
  8. titan_cli/ai/exceptions.py +48 -0
  9. titan_cli/ai/models.py +34 -0
  10. titan_cli/ai/oauth_helper.py +120 -0
  11. titan_cli/ai/providers/__init__.py +9 -0
  12. titan_cli/ai/providers/anthropic.py +117 -0
  13. titan_cli/ai/providers/base.py +75 -0
  14. titan_cli/ai/providers/gemini.py +278 -0
  15. titan_cli/cli.py +59 -0
  16. titan_cli/clients/__init__.py +1 -0
  17. titan_cli/clients/gcloud_client.py +52 -0
  18. titan_cli/core/__init__.py +3 -0
  19. titan_cli/core/config.py +274 -0
  20. titan_cli/core/discovery.py +51 -0
  21. titan_cli/core/errors.py +81 -0
  22. titan_cli/core/models.py +52 -0
  23. titan_cli/core/plugins/available.py +36 -0
  24. titan_cli/core/plugins/models.py +67 -0
  25. titan_cli/core/plugins/plugin_base.py +108 -0
  26. titan_cli/core/plugins/plugin_registry.py +163 -0
  27. titan_cli/core/secrets.py +141 -0
  28. titan_cli/core/workflows/__init__.py +22 -0
  29. titan_cli/core/workflows/models.py +88 -0
  30. titan_cli/core/workflows/project_step_source.py +86 -0
  31. titan_cli/core/workflows/workflow_exceptions.py +17 -0
  32. titan_cli/core/workflows/workflow_filter_service.py +137 -0
  33. titan_cli/core/workflows/workflow_registry.py +419 -0
  34. titan_cli/core/workflows/workflow_sources.py +307 -0
  35. titan_cli/engine/__init__.py +39 -0
  36. titan_cli/engine/builder.py +159 -0
  37. titan_cli/engine/context.py +82 -0
  38. titan_cli/engine/mock_context.py +176 -0
  39. titan_cli/engine/results.py +91 -0
  40. titan_cli/engine/steps/ai_assistant_step.py +185 -0
  41. titan_cli/engine/steps/command_step.py +93 -0
  42. titan_cli/engine/utils/__init__.py +3 -0
  43. titan_cli/engine/utils/venv.py +31 -0
  44. titan_cli/engine/workflow_executor.py +187 -0
  45. titan_cli/external_cli/__init__.py +0 -0
  46. titan_cli/external_cli/configs.py +17 -0
  47. titan_cli/external_cli/launcher.py +65 -0
  48. titan_cli/messages.py +121 -0
  49. titan_cli/ui/tui/__init__.py +205 -0
  50. titan_cli/ui/tui/__previews__/statusbar_preview.py +88 -0
  51. titan_cli/ui/tui/app.py +113 -0
  52. titan_cli/ui/tui/icons.py +70 -0
  53. titan_cli/ui/tui/screens/__init__.py +24 -0
  54. titan_cli/ui/tui/screens/ai_config.py +498 -0
  55. titan_cli/ui/tui/screens/ai_config_wizard.py +882 -0
  56. titan_cli/ui/tui/screens/base.py +110 -0
  57. titan_cli/ui/tui/screens/cli_launcher.py +151 -0
  58. titan_cli/ui/tui/screens/global_setup_wizard.py +363 -0
  59. titan_cli/ui/tui/screens/main_menu.py +162 -0
  60. titan_cli/ui/tui/screens/plugin_config_wizard.py +550 -0
  61. titan_cli/ui/tui/screens/plugin_management.py +377 -0
  62. titan_cli/ui/tui/screens/project_setup_wizard.py +686 -0
  63. titan_cli/ui/tui/screens/workflow_execution.py +592 -0
  64. titan_cli/ui/tui/screens/workflows.py +249 -0
  65. titan_cli/ui/tui/textual_components.py +537 -0
  66. titan_cli/ui/tui/textual_workflow_executor.py +405 -0
  67. titan_cli/ui/tui/theme.py +102 -0
  68. titan_cli/ui/tui/widgets/__init__.py +40 -0
  69. titan_cli/ui/tui/widgets/button.py +108 -0
  70. titan_cli/ui/tui/widgets/header.py +116 -0
  71. titan_cli/ui/tui/widgets/panel.py +81 -0
  72. titan_cli/ui/tui/widgets/status_bar.py +115 -0
  73. titan_cli/ui/tui/widgets/table.py +77 -0
  74. titan_cli/ui/tui/widgets/text.py +177 -0
  75. titan_cli/utils/__init__.py +0 -0
  76. titan_cli/utils/autoupdate.py +155 -0
  77. titan_cli-0.1.0.dist-info/METADATA +149 -0
  78. titan_cli-0.1.0.dist-info/RECORD +146 -0
  79. titan_cli-0.1.0.dist-info/WHEEL +4 -0
  80. titan_cli-0.1.0.dist-info/entry_points.txt +9 -0
  81. titan_cli-0.1.0.dist-info/licenses/LICENSE +201 -0
  82. titan_plugin_git/__init__.py +1 -0
  83. titan_plugin_git/clients/__init__.py +8 -0
  84. titan_plugin_git/clients/git_client.py +772 -0
  85. titan_plugin_git/exceptions.py +40 -0
  86. titan_plugin_git/messages.py +112 -0
  87. titan_plugin_git/models.py +39 -0
  88. titan_plugin_git/plugin.py +118 -0
  89. titan_plugin_git/steps/__init__.py +1 -0
  90. titan_plugin_git/steps/ai_commit_message_step.py +171 -0
  91. titan_plugin_git/steps/branch_steps.py +104 -0
  92. titan_plugin_git/steps/commit_step.py +80 -0
  93. titan_plugin_git/steps/push_step.py +63 -0
  94. titan_plugin_git/steps/status_step.py +59 -0
  95. titan_plugin_git/workflows/__previews__/__init__.py +1 -0
  96. titan_plugin_git/workflows/__previews__/commit_ai_preview.py +124 -0
  97. titan_plugin_git/workflows/commit-ai.yaml +28 -0
  98. titan_plugin_github/__init__.py +11 -0
  99. titan_plugin_github/agents/__init__.py +6 -0
  100. titan_plugin_github/agents/config_loader.py +130 -0
  101. titan_plugin_github/agents/issue_generator.py +353 -0
  102. titan_plugin_github/agents/pr_agent.py +528 -0
  103. titan_plugin_github/clients/__init__.py +8 -0
  104. titan_plugin_github/clients/github_client.py +1105 -0
  105. titan_plugin_github/config/__init__.py +0 -0
  106. titan_plugin_github/config/pr_agent.toml +85 -0
  107. titan_plugin_github/exceptions.py +28 -0
  108. titan_plugin_github/messages.py +88 -0
  109. titan_plugin_github/models.py +330 -0
  110. titan_plugin_github/plugin.py +131 -0
  111. titan_plugin_github/steps/__init__.py +12 -0
  112. titan_plugin_github/steps/ai_pr_step.py +172 -0
  113. titan_plugin_github/steps/create_pr_step.py +86 -0
  114. titan_plugin_github/steps/github_prompt_steps.py +171 -0
  115. titan_plugin_github/steps/issue_steps.py +143 -0
  116. titan_plugin_github/steps/preview_step.py +40 -0
  117. titan_plugin_github/utils.py +82 -0
  118. titan_plugin_github/workflows/__previews__/__init__.py +1 -0
  119. titan_plugin_github/workflows/__previews__/create_pr_ai_preview.py +140 -0
  120. titan_plugin_github/workflows/create-issue-ai.yaml +32 -0
  121. titan_plugin_github/workflows/create-pr-ai.yaml +49 -0
  122. titan_plugin_jira/__init__.py +8 -0
  123. titan_plugin_jira/agents/__init__.py +6 -0
  124. titan_plugin_jira/agents/config_loader.py +154 -0
  125. titan_plugin_jira/agents/jira_agent.py +553 -0
  126. titan_plugin_jira/agents/prompts.py +364 -0
  127. titan_plugin_jira/agents/response_parser.py +435 -0
  128. titan_plugin_jira/agents/token_tracker.py +223 -0
  129. titan_plugin_jira/agents/validators.py +246 -0
  130. titan_plugin_jira/clients/jira_client.py +745 -0
  131. titan_plugin_jira/config/jira_agent.toml +92 -0
  132. titan_plugin_jira/config/templates/issue_analysis.md.j2 +78 -0
  133. titan_plugin_jira/exceptions.py +37 -0
  134. titan_plugin_jira/formatters/__init__.py +6 -0
  135. titan_plugin_jira/formatters/markdown_formatter.py +245 -0
  136. titan_plugin_jira/messages.py +115 -0
  137. titan_plugin_jira/models.py +89 -0
  138. titan_plugin_jira/plugin.py +264 -0
  139. titan_plugin_jira/steps/ai_analyze_issue_step.py +105 -0
  140. titan_plugin_jira/steps/get_issue_step.py +82 -0
  141. titan_plugin_jira/steps/prompt_select_issue_step.py +80 -0
  142. titan_plugin_jira/steps/search_saved_query_step.py +238 -0
  143. titan_plugin_jira/utils/__init__.py +13 -0
  144. titan_plugin_jira/utils/issue_sorter.py +140 -0
  145. titan_plugin_jira/utils/saved_queries.py +150 -0
  146. titan_plugin_jira/workflows/analyze-jira-issues.yaml +34 -0
@@ -0,0 +1,116 @@
1
+ """
2
+ Header Widget
3
+
4
+ Custom header widget with title and back button.
5
+ """
6
+ from textual.app import ComposeResult
7
+ from textual.widget import Widget
8
+ from textual.widgets import Static
9
+ from textual.containers import Horizontal
10
+ from textual.reactive import reactive
11
+ from textual.message import Message
12
+
13
+ from titan_cli.ui.tui.icons import Icons
14
+
15
+
16
+ class HeaderWidget(Widget):
17
+ """
18
+ Header widget that displays screen title and back button.
19
+
20
+ Shows:
21
+ - Left: Back button (← Back)
22
+ - Center: Screen title
23
+ - Right: Empty (for symmetry)
24
+ """
25
+
26
+ # Reactive property for title
27
+ title: reactive[str] = reactive("Titan CLI")
28
+
29
+ DEFAULT_CSS = """
30
+ HeaderWidget {
31
+ background: $surface-lighten-1;
32
+ height: 3;
33
+ width: 100%;
34
+ dock: top;
35
+ }
36
+
37
+ HeaderWidget Horizontal {
38
+ width: 100%;
39
+ height: 100%;
40
+ align: center middle;
41
+ }
42
+
43
+ HeaderWidget #header-back {
44
+ width: auto;
45
+ min-width: 15;
46
+ height: 100%;
47
+ background: transparent;
48
+ color: $primary;
49
+ text-align: left;
50
+ padding: 0 2;
51
+ content-align: left middle;
52
+ }
53
+
54
+ HeaderWidget #header-back:hover {
55
+ background: $surface-lighten-2;
56
+ text-style: bold;
57
+ }
58
+
59
+ HeaderWidget #header-title {
60
+ width: 1fr;
61
+ height: 100%;
62
+ content-align: center middle;
63
+ text-align: center;
64
+ color: $primary;
65
+ text-style: bold;
66
+ }
67
+
68
+ HeaderWidget .header-left,
69
+ HeaderWidget .header-right {
70
+ width: auto;
71
+ min-width: 15;
72
+ height: 100%;
73
+ }
74
+ """
75
+
76
+ def __init__(self, title: str = "Titan CLI", show_back: bool = True, **kwargs):
77
+ """
78
+ Initialize header widget.
79
+
80
+ Args:
81
+ title: Title to display in header
82
+ show_back: Whether to show back button
83
+ """
84
+ super().__init__(**kwargs)
85
+ self.title = title
86
+ self.show_back = show_back
87
+
88
+ def compose(self) -> ComposeResult:
89
+ """Compose the header with back button and title."""
90
+ with Horizontal():
91
+ if self.show_back:
92
+ yield Static(f"{Icons.BACK} Back", id="header-back", classes="header-left")
93
+ else:
94
+ yield Static("", classes="header-left")
95
+
96
+ yield Static(self.title, id="header-title")
97
+ yield Static("", classes="header-right")
98
+
99
+ def on_click(self, event) -> None:
100
+ """Handle click on header elements."""
101
+ if event.widget.id == "header-back":
102
+ # Post a message to the screen to go back
103
+ self.post_message(self.BackPressed())
104
+
105
+ def watch_title(self, new_value: str) -> None:
106
+ """Update title display when title changes."""
107
+ if self.is_mounted:
108
+ try:
109
+ title_widget = self.query_one("#header-title", Static)
110
+ title_widget.update(new_value)
111
+ except Exception:
112
+ pass
113
+
114
+ class BackPressed(Message):
115
+ """Message sent when back button is pressed."""
116
+ pass
@@ -0,0 +1,81 @@
1
+ """
2
+ Panel Widget
3
+
4
+ A bordered container for displaying important messages with different types.
5
+ """
6
+
7
+ from textual.app import ComposeResult
8
+ from textual.widget import Widget
9
+ from textual.widgets import Label
10
+ from textual.containers import Container
11
+
12
+ from titan_cli.ui.tui.icons import Icons
13
+
14
+
15
+ class Panel(Widget):
16
+ """Panel widget with border and type-based styling."""
17
+
18
+ DEFAULT_CSS = """
19
+ Panel {
20
+ width: auto;
21
+ height: auto;
22
+ margin: 0 0 1 0;
23
+ }
24
+
25
+ Panel > Container {
26
+ width: auto;
27
+ height: auto;
28
+ border: round $primary;
29
+ padding: 1;
30
+ }
31
+
32
+ Panel.info > Container {
33
+ border: round $accent;
34
+ }
35
+
36
+ Panel.success > Container {
37
+ border: round $success;
38
+ }
39
+
40
+ Panel.warning > Container {
41
+ border: round $warning;
42
+ }
43
+
44
+ Panel.error > Container {
45
+ border: round $error;
46
+ }
47
+
48
+ Panel Label {
49
+ width: auto;
50
+ height: auto;
51
+ }
52
+ """
53
+
54
+ def __init__(self, text: str, panel_type: str = "info", **kwargs):
55
+ """
56
+ Initialize panel.
57
+
58
+ Args:
59
+ text: Text to display
60
+ panel_type: Type of panel (info, success, warning, error)
61
+ """
62
+ super().__init__(**kwargs)
63
+ self.text = text
64
+ self.panel_type = panel_type
65
+
66
+ # Add CSS class based on type
67
+ self.add_class(panel_type)
68
+
69
+ def compose(self) -> ComposeResult:
70
+ """Compose the panel with bordered container."""
71
+ # Map panel types to icons from Icons class
72
+ icons = {
73
+ "info": Icons.INFO,
74
+ "success": Icons.SUCCESS,
75
+ "warning": Icons.WARNING,
76
+ "error": Icons.ERROR,
77
+ }
78
+
79
+ icon = icons.get(self.panel_type, Icons.INFO)
80
+ with Container():
81
+ yield Label(f"{icon} {self.text}")
@@ -0,0 +1,115 @@
1
+ """
2
+ Status Bar Widget
3
+
4
+ Fixed status bar showing git branch, AI info, and active project.
5
+ """
6
+ from textual.app import ComposeResult
7
+ from textual.widget import Widget
8
+ from textual.widgets import Static
9
+ from textual.containers import Horizontal
10
+ from textual.reactive import reactive
11
+
12
+ class StatusBarWidget(Widget):
13
+ """
14
+ Status bar widget that displays project information.
15
+
16
+ Shows:
17
+ - Left: Git branch
18
+ - Center: AI provider and model
19
+ - Right: Active project name
20
+
21
+ This widget is designed to be docked at the bottom of the screen.
22
+ """
23
+
24
+ # Reactive properties - automatically update the widget when changed
25
+ git_branch: reactive[str] = reactive("N/A")
26
+ ai_info: reactive[str] = reactive("N/A")
27
+ project_name: reactive[str] = reactive("N/A")
28
+
29
+ DEFAULT_CSS = """
30
+ StatusBarWidget {
31
+ background: $surface-lighten-1;
32
+ color: white;
33
+ height: 1;
34
+ width: 100%;
35
+ dock: bottom;
36
+ }
37
+
38
+ StatusBarWidget Horizontal {
39
+ width: 100%;
40
+ }
41
+
42
+ StatusBarWidget Static {
43
+ width: 1fr;
44
+ height: 100%;
45
+ content-align: center middle;
46
+ }
47
+
48
+ StatusBarWidget #branch-info {
49
+ text-align: left;
50
+ color: blue;
51
+ }
52
+
53
+ StatusBarWidget #ai-info {
54
+ text-align: center;
55
+ color: green;
56
+ }
57
+
58
+ StatusBarWidget #project-info {
59
+ text-align: right;
60
+ color: orange;
61
+ }
62
+ """
63
+
64
+ def compose(self) -> ComposeResult:
65
+ """Compose the status bar with three columns."""
66
+ with Horizontal():
67
+ yield Static(f"{self.git_branch}", id="branch-info")
68
+ yield Static(f"{self.ai_info}", id="ai-info")
69
+ yield Static(f"{self.project_name}", id="project-info")
70
+
71
+ def _update_branch(self, value: str) -> None:
72
+ """Update branch display."""
73
+ branch_widget = self.query_one("#branch-info", Static)
74
+ branch_widget.update(value)
75
+
76
+ def _update_ai(self, value: str) -> None:
77
+ """Update AI display."""
78
+ ai_widget = self.query_one("#ai-info", Static)
79
+ ai_widget.update(value)
80
+
81
+ def _update_project(self, value: str) -> None:
82
+ """Update project display."""
83
+ project_widget = self.query_one("#project-info", Static)
84
+ project_widget.update(value)
85
+
86
+ def watch_git_branch(self, new_value: str) -> None:
87
+ """Update branch display when git_branch changes."""
88
+ if self.is_mounted:
89
+ self._update_branch(new_value)
90
+
91
+ def watch_ai_info(self, new_value: str) -> None:
92
+ """Update AI display when ai_info changes."""
93
+ if self.is_mounted:
94
+ self._update_ai(new_value)
95
+
96
+ def watch_project_name(self, new_value: str) -> None:
97
+ """Update project display when project_name changes."""
98
+ if self.is_mounted:
99
+ self._update_project(new_value)
100
+
101
+ def update_status(self, git_branch: str = None, ai_info: str = None, project_name: str = None):
102
+ """
103
+ Update status bar information.
104
+
105
+ Args:
106
+ git_branch: Git branch name
107
+ ai_info: AI provider/model info
108
+ project_name: Active project name
109
+ """
110
+ if git_branch is not None:
111
+ self.git_branch = git_branch
112
+ if ai_info is not None:
113
+ self.ai_info = ai_info
114
+ if project_name is not None:
115
+ self.project_name = project_name
@@ -0,0 +1,77 @@
1
+ """
2
+ Table Widget
3
+
4
+ A simple table widget for displaying tabular data.
5
+ """
6
+
7
+ from typing import List
8
+ from textual.app import ComposeResult
9
+ from textual.widget import Widget
10
+ from textual.widgets import DataTable
11
+
12
+
13
+ class Table(Widget):
14
+ """Table widget for displaying rows and columns."""
15
+
16
+ DEFAULT_CSS = """
17
+ Table {
18
+ width: 100%;
19
+ height: auto;
20
+ margin: 0 0 1 0;
21
+ }
22
+
23
+ Table.compact {
24
+ width: auto;
25
+ }
26
+
27
+ Table > DataTable {
28
+ width: 100%;
29
+ height: auto;
30
+ }
31
+
32
+ Table.compact > DataTable {
33
+ width: auto;
34
+ }
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ headers: List[str],
40
+ rows: List[List[str]],
41
+ title: str = "",
42
+ full_width: bool = True,
43
+ **kwargs
44
+ ):
45
+ """
46
+ Initialize table.
47
+
48
+ Args:
49
+ headers: List of column headers
50
+ rows: List of rows (each row is a list of cell values)
51
+ title: Optional title for the table
52
+ full_width: If False, table uses auto width (compact mode)
53
+ """
54
+ super().__init__(**kwargs)
55
+ self.headers = headers
56
+ self.rows = rows
57
+ self.title_text = title
58
+
59
+ # Add compact class if not full width
60
+ if not full_width:
61
+ self.add_class("compact")
62
+
63
+ def compose(self) -> ComposeResult:
64
+ """Compose the table."""
65
+ table = DataTable()
66
+ if self.title_text:
67
+ table.border_title = self.title_text
68
+
69
+ # Add columns
70
+ for header in self.headers:
71
+ table.add_column(header)
72
+
73
+ # Add rows
74
+ for row in self.rows:
75
+ table.add_row(*row)
76
+
77
+ yield table
@@ -0,0 +1,177 @@
1
+ """
2
+ Text Widgets
3
+
4
+ Reusable text widgets with theme-based styling.
5
+ """
6
+ from textual.widgets import Static
7
+
8
+
9
+ # Shared CSS for all text styling - DRY principle
10
+ SHARED_TEXT_CSS = """
11
+ .dim, DimText, DimItalicText {
12
+ color: $text-muted;
13
+ }
14
+
15
+ .bold, BoldText, BoldPrimaryText {
16
+ text-style: bold;
17
+ }
18
+
19
+ .italic, ItalicText, DimItalicText {
20
+ text-style: italic;
21
+ }
22
+
23
+ .primary, PrimaryText, BoldPrimaryText {
24
+ color: $primary;
25
+ }
26
+
27
+ .success, SuccessText {
28
+ color: $success;
29
+ }
30
+
31
+ .error, ErrorText {
32
+ color: $error;
33
+ }
34
+
35
+ .warning, WarningText {
36
+ color: $warning;
37
+ }
38
+ """
39
+
40
+
41
+ class Text(Static):
42
+ """
43
+ Reusable text widget with dynamic styling via CSS classes.
44
+
45
+ Usage:
46
+ # Create with initial style
47
+ text = Text("Hello", style="bold")
48
+
49
+ # Change style dynamically
50
+ text.set_style("error")
51
+
52
+ # Combine multiple styles
53
+ text = Text("Hello", style="bold primary")
54
+
55
+ Available styles:
56
+ - dim: Muted/dimmed text
57
+ - bold: Bold text
58
+ - italic: Italic text
59
+ - primary: Primary color
60
+ - success: Success/green color
61
+ - error: Error/red color
62
+ - warning: Warning/yellow color
63
+ """
64
+
65
+ DEFAULT_CSS = SHARED_TEXT_CSS
66
+
67
+ def __init__(self, renderable="", *, style: str = "", **kwargs):
68
+ """
69
+ Initialize text widget.
70
+
71
+ Args:
72
+ renderable: Text content
73
+ style: Space-separated style classes (e.g., "bold primary")
74
+ **kwargs: Additional Static arguments
75
+ """
76
+ # Add style classes to existing classes
77
+ if style:
78
+ existing_classes = kwargs.get("classes", "")
79
+ if existing_classes:
80
+ kwargs["classes"] = f"{existing_classes} {style}"
81
+ else:
82
+ kwargs["classes"] = style
83
+
84
+ super().__init__(renderable, **kwargs)
85
+
86
+ def set_style(self, *styles: str) -> None:
87
+ """
88
+ Set the text style(s), removing previous styles.
89
+
90
+ Args:
91
+ *styles: One or more style names to apply
92
+
93
+ Example:
94
+ text.set_style("bold", "error")
95
+ """
96
+ # Remove all style classes
97
+ for cls in ["dim", "bold", "italic", "primary", "success", "error", "warning"]:
98
+ self.remove_class(cls)
99
+
100
+ # Add new styles
101
+ for style in styles:
102
+ if style:
103
+ self.add_class(style)
104
+
105
+ def add_style(self, *styles: str) -> None:
106
+ """
107
+ Add style(s) to the text without removing existing ones.
108
+
109
+ Args:
110
+ *styles: One or more style names to add
111
+
112
+ Example:
113
+ text.add_style("italic")
114
+ """
115
+ for style in styles:
116
+ if style:
117
+ self.add_class(style)
118
+
119
+ def remove_style(self, *styles: str) -> None:
120
+ """
121
+ Remove style(s) from the text.
122
+
123
+ Args:
124
+ *styles: One or more style names to remove
125
+
126
+ Example:
127
+ text.remove_style("bold")
128
+ """
129
+ for style in styles:
130
+ if style:
131
+ self.remove_class(style)
132
+
133
+
134
+ # Convenience widgets - use shared CSS
135
+ class DimText(Static):
136
+ """Text widget with dim/muted styling."""
137
+ DEFAULT_CSS = SHARED_TEXT_CSS
138
+
139
+
140
+ class BoldText(Static):
141
+ """Text widget with bold styling."""
142
+ DEFAULT_CSS = SHARED_TEXT_CSS
143
+
144
+
145
+ class PrimaryText(Static):
146
+ """Text widget with primary color."""
147
+ DEFAULT_CSS = SHARED_TEXT_CSS
148
+
149
+
150
+ class BoldPrimaryText(Static):
151
+ """Text widget with bold primary color."""
152
+ DEFAULT_CSS = SHARED_TEXT_CSS
153
+
154
+
155
+ class SuccessText(Static):
156
+ """Text widget with success/green color."""
157
+ DEFAULT_CSS = SHARED_TEXT_CSS
158
+
159
+
160
+ class ErrorText(Static):
161
+ """Text widget with error/red color."""
162
+ DEFAULT_CSS = SHARED_TEXT_CSS
163
+
164
+
165
+ class WarningText(Static):
166
+ """Text widget with warning/yellow color."""
167
+ DEFAULT_CSS = SHARED_TEXT_CSS
168
+
169
+
170
+ class ItalicText(Static):
171
+ """Text widget with italic styling."""
172
+ DEFAULT_CSS = SHARED_TEXT_CSS
173
+
174
+
175
+ class DimItalicText(Static):
176
+ """Text widget with dim italic styling."""
177
+ DEFAULT_CSS = SHARED_TEXT_CSS
File without changes