htcli 1.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 (140) hide show
  1. htcli-1.1.0.dist-info/METADATA +509 -0
  2. htcli-1.1.0.dist-info/RECORD +140 -0
  3. htcli-1.1.0.dist-info/WHEEL +4 -0
  4. htcli-1.1.0.dist-info/entry_points.txt +2 -0
  5. htcli-1.1.0.dist-info/licenses/LICENSE +21 -0
  6. src/__init__.py +0 -0
  7. src/htcli/__init__.py +5 -0
  8. src/htcli/client/__init__.py +338 -0
  9. src/htcli/client/extrinsics/__init__.py +26 -0
  10. src/htcli/client/extrinsics/base.py +487 -0
  11. src/htcli/client/extrinsics/consensus.py +79 -0
  12. src/htcli/client/extrinsics/governance.py +714 -0
  13. src/htcli/client/extrinsics/identity.py +490 -0
  14. src/htcli/client/extrinsics/node.py +1054 -0
  15. src/htcli/client/extrinsics/overwatch.py +401 -0
  16. src/htcli/client/extrinsics/staking.py +1504 -0
  17. src/htcli/client/extrinsics/subnet.py +2218 -0
  18. src/htcli/client/extrinsics/validator.py +203 -0
  19. src/htcli/client/extrinsics/wallet.py +323 -0
  20. src/htcli/client/offchain/__init__.py +10 -0
  21. src/htcli/client/offchain/backup.py +385 -0
  22. src/htcli/client/offchain/config.py +541 -0
  23. src/htcli/client/offchain/wallet.py +839 -0
  24. src/htcli/client/rpc/__init__.py +20 -0
  25. src/htcli/client/rpc/chain.py +568 -0
  26. src/htcli/client/rpc/node.py +783 -0
  27. src/htcli/client/rpc/overwatch.py +680 -0
  28. src/htcli/client/rpc/staking.py +216 -0
  29. src/htcli/client/rpc/subnet.py +2104 -0
  30. src/htcli/client/rpc/wallet.py +912 -0
  31. src/htcli/commands/__init__.py +31 -0
  32. src/htcli/commands/chain/__init__.py +66 -0
  33. src/htcli/commands/chain/display.py +204 -0
  34. src/htcli/commands/chain/handlers.py +260 -0
  35. src/htcli/commands/config/__init__.py +158 -0
  36. src/htcli/commands/config/display.py +353 -0
  37. src/htcli/commands/config/handlers.py +347 -0
  38. src/htcli/commands/config/prompts.py +357 -0
  39. src/htcli/commands/consensus/__init__.py +61 -0
  40. src/htcli/commands/consensus/handlers.py +100 -0
  41. src/htcli/commands/governance/__init__.py +49 -0
  42. src/htcli/commands/governance/handlers.py +81 -0
  43. src/htcli/commands/node/__init__.py +304 -0
  44. src/htcli/commands/node/display.py +749 -0
  45. src/htcli/commands/node/error_handling.py +470 -0
  46. src/htcli/commands/node/handlers.py +844 -0
  47. src/htcli/commands/node/prompts.py +346 -0
  48. src/htcli/commands/overwatch/__init__.py +219 -0
  49. src/htcli/commands/overwatch/display.py +396 -0
  50. src/htcli/commands/overwatch/error_handling.py +276 -0
  51. src/htcli/commands/overwatch/handlers.py +443 -0
  52. src/htcli/commands/overwatch/prompts.py +359 -0
  53. src/htcli/commands/stake/__init__.py +736 -0
  54. src/htcli/commands/stake/display.py +1103 -0
  55. src/htcli/commands/stake/error_handling.py +425 -0
  56. src/htcli/commands/stake/handlers.py +1902 -0
  57. src/htcli/commands/stake/prompts.py +1080 -0
  58. src/htcli/commands/subnet/__init__.py +639 -0
  59. src/htcli/commands/subnet/display.py +801 -0
  60. src/htcli/commands/subnet/error_handling.py +524 -0
  61. src/htcli/commands/subnet/handlers.py +2855 -0
  62. src/htcli/commands/subnet/prompts.py +1225 -0
  63. src/htcli/commands/validator/__init__.py +192 -0
  64. src/htcli/commands/validator/display.py +54 -0
  65. src/htcli/commands/validator/handlers.py +340 -0
  66. src/htcli/commands/wallet/__init__.py +546 -0
  67. src/htcli/commands/wallet/display.py +806 -0
  68. src/htcli/commands/wallet/error_handling.py +210 -0
  69. src/htcli/commands/wallet/handlers.py +3040 -0
  70. src/htcli/commands/wallet/prompts.py +1518 -0
  71. src/htcli/config.py +184 -0
  72. src/htcli/dependencies.py +186 -0
  73. src/htcli/errors/__init__.py +63 -0
  74. src/htcli/errors/base.py +141 -0
  75. src/htcli/errors/display.py +20 -0
  76. src/htcli/errors/handlers.py +710 -0
  77. src/htcli/main.py +343 -0
  78. src/htcli/models/__init__.py +21 -0
  79. src/htcli/models/enums/enum_types.py +35 -0
  80. src/htcli/models/errors.py +103 -0
  81. src/htcli/models/requests/__init__.py +197 -0
  82. src/htcli/models/requests/config.py +70 -0
  83. src/htcli/models/requests/consensus.py +19 -0
  84. src/htcli/models/requests/governance.py +38 -0
  85. src/htcli/models/requests/identity.py +51 -0
  86. src/htcli/models/requests/key.py +22 -0
  87. src/htcli/models/requests/node.py +91 -0
  88. src/htcli/models/requests/overwatch.py +64 -0
  89. src/htcli/models/requests/staking.py +580 -0
  90. src/htcli/models/requests/subnet.py +195 -0
  91. src/htcli/models/requests/validator.py +139 -0
  92. src/htcli/models/requests/wallet.py +118 -0
  93. src/htcli/models/responses/__init__.py +147 -0
  94. src/htcli/models/responses/base.py +18 -0
  95. src/htcli/models/responses/chain.py +39 -0
  96. src/htcli/models/responses/config.py +58 -0
  97. src/htcli/models/responses/identity.py +102 -0
  98. src/htcli/models/responses/overwatch.py +51 -0
  99. src/htcli/models/responses/staking.py +502 -0
  100. src/htcli/models/responses/subnet.py +856 -0
  101. src/htcli/models/responses/wallet.py +185 -0
  102. src/htcli/ui/__init__.py +87 -0
  103. src/htcli/ui/colors.py +309 -0
  104. src/htcli/ui/components/__init__.py +60 -0
  105. src/htcli/ui/components/panels.py +174 -0
  106. src/htcli/ui/components/progress.py +166 -0
  107. src/htcli/ui/components/spinners.py +92 -0
  108. src/htcli/ui/components/tables.py +809 -0
  109. src/htcli/ui/components/trees.py +721 -0
  110. src/htcli/ui/display.py +336 -0
  111. src/htcli/ui/prompts.py +870 -0
  112. src/htcli/utils/__init__.py +76 -0
  113. src/htcli/utils/blockchain/__init__.py +75 -0
  114. src/htcli/utils/blockchain/formatting.py +368 -0
  115. src/htcli/utils/blockchain/patches.py +286 -0
  116. src/htcli/utils/blockchain/peer_id.py +186 -0
  117. src/htcli/utils/blockchain/staking.py +448 -0
  118. src/htcli/utils/blockchain/type_registry.py +1373 -0
  119. src/htcli/utils/blockchain/validation.py +179 -0
  120. src/htcli/utils/cache.py +613 -0
  121. src/htcli/utils/constants.py +38 -0
  122. src/htcli/utils/legacy/__init__.py +12 -0
  123. src/htcli/utils/legacy/colors.py +311 -0
  124. src/htcli/utils/legacy/crypto.py +1176 -0
  125. src/htcli/utils/legacy/formatting.py +452 -0
  126. src/htcli/utils/legacy/interactive.py +306 -0
  127. src/htcli/utils/legacy/subnet_manifest.py +265 -0
  128. src/htcli/utils/legacy/validation.py +488 -0
  129. src/htcli/utils/logging.py +183 -0
  130. src/htcli/utils/network/__init__.py +20 -0
  131. src/htcli/utils/network/subnet.py +344 -0
  132. src/htcli/utils/prompts.py +27 -0
  133. src/htcli/utils/scale_codec.py +155 -0
  134. src/htcli/utils/validation/__init__.py +57 -0
  135. src/htcli/utils/validation/prompt_validators.py +267 -0
  136. src/htcli/utils/wallet/__init__.py +65 -0
  137. src/htcli/utils/wallet/auth.py +151 -0
  138. src/htcli/utils/wallet/core.py +1069 -0
  139. src/htcli/utils/wallet/crypto.py +1615 -0
  140. src/htcli/utils/wallet/migration.py +159 -0
@@ -0,0 +1,185 @@
1
+ """
2
+ Wallet-related response models.
3
+ """
4
+
5
+ from typing import Any, Optional
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+ from .base import BaseResponse
10
+
11
+
12
+ class WalletCreateResponse(BaseModel):
13
+ """Response model for wallet creation."""
14
+
15
+ name: str = Field(..., description="Wallet name")
16
+ address: str = Field(..., description="Wallet address")
17
+ public_key: str = Field(..., description="Public key")
18
+ key_type: str = Field(..., description="Key type")
19
+ wallet_type: str = Field(..., description="Wallet type")
20
+ mnemonic: Optional[str] = Field(None, description="Mnemonic phrase (if generated)")
21
+ encrypted: bool = Field(False, description="Whether wallet is encrypted")
22
+
23
+
24
+ class WalletListResponse(BaseModel):
25
+ """Response model for wallet listing."""
26
+
27
+ wallets: list[dict[str, Any]] = Field(..., description="List of wallets")
28
+ total_count: int = Field(..., description="Total number of wallets")
29
+ coldkeys: int = Field(..., description="Number of coldkeys")
30
+ hotkeys: int = Field(..., description="Number of hotkeys")
31
+
32
+
33
+ class WalletStatusResponse(BaseModel):
34
+ """Response model for wallet status."""
35
+
36
+ wallets: list[dict[str, Any]] = Field(..., description="Wallet information")
37
+ total_keys: int = Field(..., description="Total number of keys")
38
+ total_addresses: int = Field(..., description="Total number of addresses")
39
+
40
+
41
+ class WalletBalanceResponse(BaseModel):
42
+ """Response model for wallet balance."""
43
+
44
+ address: str = Field(..., description="Wallet address")
45
+ balance: int = Field(..., description="Raw balance in planck")
46
+ formatted_balance: str = Field(..., description="Formatted balance in TENSOR")
47
+ unit: str = Field(..., description="Balance unit")
48
+ wallet_name: Optional[str] = Field(None, description="Wallet name")
49
+
50
+
51
+ class WalletTransferResponse(BaseModel):
52
+ """Response model for wallet transfers."""
53
+
54
+ from_address: str = Field(..., description="Source address")
55
+ to_address: str = Field(..., description="Destination address")
56
+ amount: str = Field(..., description="Transfer amount")
57
+ unit: str = Field(..., description="Amount unit")
58
+ transaction_hash: Optional[str] = Field(None, description="Transaction hash")
59
+ block_hash: Optional[str] = Field(None, description="Block hash")
60
+ success: bool = Field(..., description="Transfer success status")
61
+
62
+
63
+ class WalletUpdateResponse(BaseModel):
64
+ """Response model for wallet updates."""
65
+
66
+ old_name: str = Field(..., description="Original wallet name")
67
+ new_name: str = Field(..., description="New wallet name")
68
+ address: str = Field(..., description="Wallet address")
69
+ key_type: str = Field(..., description="Key type")
70
+ name_updated: bool = Field(..., description="Whether name was updated")
71
+ password_updated: bool = Field(..., description="Whether password was updated")
72
+ owner_updated: Optional[bool] = Field(
73
+ None, description="Whether owner was updated (hotkeys only)"
74
+ )
75
+ old_owner_address: Optional[str] = Field(
76
+ None, description="Old owner address (hotkeys only)"
77
+ )
78
+ new_owner_address: Optional[str] = Field(
79
+ None, description="New owner address (hotkeys only)"
80
+ )
81
+ transaction_hash: Optional[str] = Field(
82
+ None, description="Transaction hash for on-chain owner update"
83
+ )
84
+ block_hash: Optional[str] = Field(
85
+ None, description="Block hash for on-chain owner update"
86
+ )
87
+ block_number: Optional[int] = Field(
88
+ None, description="Block number for on-chain owner update"
89
+ )
90
+
91
+
92
+ class WalletDeleteResponse(BaseModel):
93
+ """Response model for wallet deletion."""
94
+
95
+ deleted_wallets: list[str] = Field(..., description="List of deleted wallet names")
96
+ total_deleted: int = Field(..., description="Total number of wallets deleted")
97
+ coldkeys_deleted: int = Field(..., description="Number of coldkeys deleted")
98
+ hotkeys_deleted: int = Field(
99
+ ..., description="Number of directly deleted hotkeys (standalone)"
100
+ )
101
+ associated_hotkeys_deleted: int = Field(
102
+ ...,
103
+ description="Number of associated hotkeys deleted (deleted because owner coldkey was deleted)",
104
+ )
105
+ associated_hotkey_details: list[dict[str, str]] = Field(
106
+ default_factory=list,
107
+ description="List of associated hotkeys with name and owner information",
108
+ )
109
+
110
+
111
+ class StakeAddResponse(BaseResponse):
112
+ """Response for stake addition."""
113
+
114
+ stake_amount: Optional[int] = Field(None, description="Amount of stake added")
115
+ shares_received: Optional[int] = Field(None, description="Shares received")
116
+ node_id: Optional[int] = Field(None, description="Node ID")
117
+ subnet_id: Optional[int] = Field(None, description="Subnet ID")
118
+ reward_rate: Optional[float] = Field(None, description="Current reward rate")
119
+
120
+
121
+ class StakeRemoveResponse(BaseResponse):
122
+ """Response for stake removal."""
123
+
124
+ stake_amount: Optional[int] = Field(None, description="Amount of stake removed")
125
+ shares_removed: Optional[int] = Field(None, description="Shares removed")
126
+ node_id: Optional[int] = Field(None, description="Node ID")
127
+ subnet_id: Optional[int] = Field(None, description="Subnet ID")
128
+ unbonding_started: Optional[bool] = Field(
129
+ None, description="Whether unbonding started"
130
+ )
131
+ unbonding_period: Optional[int] = Field(
132
+ None, description="Unbonding period in epochs"
133
+ )
134
+
135
+
136
+ class StakeInfoResponse(BaseResponse):
137
+ """Response for stake information."""
138
+
139
+ total_stake: Optional[int] = Field(None, description="Total stake amount")
140
+ shares_owned: Optional[int] = Field(None, description="Shares owned")
141
+ reward_rate: Optional[float] = Field(None, description="Current reward rate")
142
+ estimated_rewards: Optional[float] = Field(None, description="Estimated rewards")
143
+ unbonding_amount: Optional[int] = Field(None, description="Amount in unbonding")
144
+ unbonding_epochs: Optional[int] = Field(
145
+ None, description="Epochs until unbonding complete"
146
+ )
147
+
148
+
149
+ class UnbondingClaimResponse(BaseResponse):
150
+ """Response for unbonding claim."""
151
+
152
+ claimed_amount: Optional[int] = Field(None, description="Amount claimed")
153
+ remaining_unbonding: Optional[int] = Field(
154
+ None, description="Remaining unbonding amount"
155
+ )
156
+ claim_epoch: Optional[int] = Field(None, description="Epoch when claimed")
157
+
158
+
159
+ class DelegateStakeAddResponse(BaseResponse):
160
+ """Response for delegate stake addition."""
161
+
162
+ stake_amount: Optional[int] = Field(
163
+ None, description="Amount of delegate stake added"
164
+ )
165
+ shares_received: Optional[int] = Field(None, description="Shares received")
166
+ delegate_pool_id: Optional[int] = Field(None, description="Delegate pool ID")
167
+ subnet_id: Optional[int] = Field(None, description="Subnet ID")
168
+ reward_rate: Optional[float] = Field(None, description="Current reward rate")
169
+
170
+
171
+ class DelegateStakeRemoveResponse(BaseResponse):
172
+ """Response for delegate stake removal."""
173
+
174
+ stake_amount: Optional[int] = Field(
175
+ None, description="Amount of delegate stake removed"
176
+ )
177
+ shares_removed: Optional[int] = Field(None, description="Shares removed")
178
+ delegate_pool_id: Optional[int] = Field(None, description="Delegate pool ID")
179
+ subnet_id: Optional[int] = Field(None, description="Subnet ID")
180
+ unbonding_started: Optional[bool] = Field(
181
+ None, description="Whether unbonding started"
182
+ )
183
+ unbonding_period: Optional[int] = Field(
184
+ None, description="Unbonding period in epochs"
185
+ )
@@ -0,0 +1,87 @@
1
+ """
2
+ HTCLI UI module for consistent user interface elements.
3
+ Provides colors, components, and interactive elements for the CLI.
4
+ """
5
+
6
+ from .colors import HTCLIColors, HTCLITheme
7
+ from .components import (
8
+ HTCLILoadingContext,
9
+ HTCLIPanel,
10
+ HTCLIProgress,
11
+ HTCLISpinner,
12
+ HTCLIStatus,
13
+ HTCLITable,
14
+ HTCLITree,
15
+ create_network_tree,
16
+ create_node_table,
17
+ create_subnet_details_tree,
18
+ create_subnet_table,
19
+ create_transaction_tree,
20
+ create_wallet_table,
21
+ create_wallet_tree,
22
+ )
23
+ from .display import (
24
+ HTCLIConsole,
25
+ get_console,
26
+ print_blockchain_data,
27
+ print_error,
28
+ print_footer,
29
+ print_header,
30
+ print_info,
31
+ print_logo,
32
+ print_success,
33
+ print_warning,
34
+ )
35
+ from .prompts import (
36
+ HTCLIPrompt,
37
+ address_prompt,
38
+ amount_prompt,
39
+ confirm_prompt,
40
+ confirmation_panel_prompt,
41
+ password_prompt,
42
+ select_prompt,
43
+ text_prompt,
44
+ wallet_selection_prompt,
45
+ )
46
+
47
+ __all__ = [
48
+ # Colors and theming
49
+ "HTCLIColors",
50
+ "HTCLITheme",
51
+ # UI Components
52
+ "HTCLISpinner",
53
+ "HTCLIProgress",
54
+ "HTCLITable",
55
+ "HTCLITree",
56
+ "HTCLIPanel",
57
+ "HTCLIStatus",
58
+ "HTCLILoadingContext",
59
+ "create_node_table",
60
+ "create_subnet_table",
61
+ "create_wallet_table",
62
+ "create_network_tree",
63
+ "create_wallet_tree",
64
+ "create_subnet_details_tree",
65
+ "create_transaction_tree",
66
+ # Prompts
67
+ "HTCLIPrompt",
68
+ "confirm_prompt",
69
+ "select_prompt",
70
+ "text_prompt",
71
+ "password_prompt",
72
+ "amount_prompt",
73
+ "address_prompt",
74
+ "wallet_selection_prompt",
75
+ "confirmation_panel_prompt",
76
+ # Display utilities
77
+ "HTCLIConsole",
78
+ "print_success",
79
+ "print_error",
80
+ "print_warning",
81
+ "print_info",
82
+ "print_header",
83
+ "print_footer",
84
+ "print_logo",
85
+ "print_blockchain_data",
86
+ "get_console",
87
+ ]
src/htcli/ui/colors.py ADDED
@@ -0,0 +1,309 @@
1
+ """
2
+ HTCLI Color scheme and theming system.
3
+ Provides consistent colors across all CLI components.
4
+ """
5
+
6
+ from rich.style import Style
7
+ from rich.theme import Theme
8
+
9
+
10
+ class HTCLIColors:
11
+ """HTCLI color palette with blockchain/crypto-inspired theme."""
12
+
13
+ # Primary brand colors
14
+ PRIMARY = "#00D4AA" # Teal green - main brand color
15
+ PRIMARY_DARK = "#00A082" # Darker teal
16
+ PRIMARY_LIGHT = "#33DFBB" # Lighter teal
17
+
18
+ # Secondary colors
19
+ SECONDARY = "#6366F1" # Indigo - for accents
20
+ SECONDARY_DARK = "#4F46E5" # Darker indigo
21
+ SECONDARY_LIGHT = "#818CF8" # Lighter indigo
22
+
23
+ # Status colors
24
+ SUCCESS = "#10B981" # Green - for success states
25
+ ERROR = "#EF4444" # Red - for errors
26
+ WARNING = "#F59E0B" # Amber - for warnings
27
+ INFO = "#3B82F6" # Blue - for info
28
+
29
+ # Neutral colors
30
+ WHITE = "#FFFFFF"
31
+ BLACK = "#000000"
32
+ GRAY_50 = "#F9FAFB"
33
+ GRAY_100 = "#F3F4F6"
34
+ GRAY_200 = "#E5E7EB"
35
+ GRAY_300 = "#D1D5DB"
36
+ GRAY_400 = "#9CA3AF"
37
+ GRAY_500 = "#6B7280"
38
+ GRAY_600 = "#4B5563"
39
+ GRAY_700 = "#374151"
40
+ GRAY_800 = "#1F2937"
41
+ GRAY_900 = "#111827"
42
+
43
+ # Blockchain-specific colors
44
+ BLOCKCHAIN_BLUE = "#1E3A8A" # Deep blue for blockchain elements
45
+ CRYPTO_GOLD = "#F59E0B" # Gold for tokens/rewards
46
+ NETWORK_PURPLE = "#7C3AED" # Purple for network status
47
+ VALIDATOR_GREEN = "#059669" # Green for validators
48
+ STAKING_ORANGE = "#EA580C" # Orange for staking
49
+
50
+ # Semantic colors
51
+ BALANCE = SUCCESS # Green for positive balances
52
+ DEBT = ERROR # Red for negative/insufficient
53
+ PENDING = WARNING # Amber for pending states
54
+ ACTIVE = SUCCESS # Green for active states
55
+ INACTIVE = GRAY_500 # Gray for inactive states
56
+
57
+ @classmethod
58
+ def get_status_color(cls, status: str) -> str:
59
+ """Get color for a given status."""
60
+ status_map = {
61
+ "success": cls.SUCCESS,
62
+ "error": cls.ERROR,
63
+ "warning": cls.WARNING,
64
+ "info": cls.INFO,
65
+ "active": cls.ACTIVE,
66
+ "inactive": cls.INACTIVE,
67
+ "pending": cls.PENDING,
68
+ "balance": cls.BALANCE,
69
+ "debt": cls.DEBT,
70
+ }
71
+ return status_map.get(status.lower(), cls.GRAY_500)
72
+
73
+ @classmethod
74
+ def get_gradient_colors(cls) -> dict[str, str]:
75
+ """Get colors for gradient effects."""
76
+ return {
77
+ "primary_gradient": [
78
+ cls.PRIMARY_DARK,
79
+ cls.PRIMARY,
80
+ cls.PRIMARY_LIGHT,
81
+ ],
82
+ "secondary_gradient": [
83
+ cls.SECONDARY_DARK,
84
+ cls.SECONDARY,
85
+ cls.SECONDARY_LIGHT,
86
+ ],
87
+ "success_gradient": [
88
+ cls.VALIDATOR_GREEN,
89
+ cls.SUCCESS,
90
+ cls.PRIMARY_LIGHT,
91
+ ],
92
+ "blockchain_gradient": [
93
+ cls.BLOCKCHAIN_BLUE,
94
+ cls.NETWORK_PURPLE,
95
+ cls.SECONDARY,
96
+ ],
97
+ }
98
+
99
+
100
+ class HTCLITheme:
101
+ """Rich theme configuration for HTCLI."""
102
+
103
+ @classmethod
104
+ def get_theme(cls) -> Theme:
105
+ """Get the complete HTCLI Rich theme."""
106
+ return Theme(
107
+ {
108
+ # Basic semantic styles
109
+ "success": Style(color=HTCLIColors.SUCCESS, bold=True),
110
+ "error": Style(color=HTCLIColors.ERROR, bold=True),
111
+ "warning": Style(color=HTCLIColors.WARNING, bold=True),
112
+ "info": Style(color=HTCLIColors.INFO, bold=True),
113
+ # HTCLI specific styles
114
+ "htcli.primary": Style(color=HTCLIColors.PRIMARY, bold=True),
115
+ "htcli.accent": Style(color=HTCLIColors.SECONDARY_LIGHT, bold=True),
116
+ "htcli.secondary": Style(color=HTCLIColors.SECONDARY),
117
+ "htcli.brand": Style(color=HTCLIColors.PRIMARY, bold=True),
118
+ "htcli.success": Style(color=HTCLIColors.SUCCESS, bold=True),
119
+ "htcli.error": Style(color=HTCLIColors.ERROR, bold=True),
120
+ "htcli.warning": Style(color=HTCLIColors.WARNING, bold=True),
121
+ "htcli.info": Style(color=HTCLIColors.INFO, bold=True),
122
+ "htcli.muted": Style(color=HTCLIColors.GRAY_500),
123
+ # Headers and titles
124
+ "htcli.header": Style(
125
+ color=HTCLIColors.PRIMARY, bold=True, underline=True
126
+ ),
127
+ "htcli.title": Style(color=HTCLIColors.PRIMARY, bold=True),
128
+ "htcli.subtitle": Style(color=HTCLIColors.GRAY_500),
129
+ # Status indicators
130
+ "htcli.status.active": Style(color=HTCLIColors.ACTIVE, bold=True),
131
+ "htcli.status.inactive": Style(color=HTCLIColors.INACTIVE),
132
+ "htcli.status.pending": Style(color=HTCLIColors.PENDING, bold=True),
133
+ "htcli.status.success": Style(color=HTCLIColors.SUCCESS, bold=True),
134
+ "htcli.status.error": Style(color=HTCLIColors.ERROR, bold=True),
135
+ "htcli.status.confirmed": Style(color=HTCLIColors.SUCCESS, bold=True),
136
+ # Blockchain elements
137
+ "htcli.address": Style(color=HTCLIColors.BLOCKCHAIN_BLUE, bold=True),
138
+ "htcli.hash": Style(color=HTCLIColors.GRAY_600, italic=True),
139
+ "htcli.amount": Style(color=HTCLIColors.CRYPTO_GOLD, bold=True),
140
+ "htcli.balance": Style(color=HTCLIColors.BALANCE, bold=True),
141
+ "htcli.fee": Style(color=HTCLIColors.GRAY_500),
142
+ # Network and validators
143
+ "htcli.network": Style(color=HTCLIColors.NETWORK_PURPLE, bold=True),
144
+ "htcli.validator": Style(color=HTCLIColors.VALIDATOR_GREEN, bold=True),
145
+ "htcli.subnet": Style(color=HTCLIColors.SECONDARY, bold=True),
146
+ "htcli.node": Style(color=HTCLIColors.INFO, bold=True),
147
+ # Staking and rewards
148
+ "htcli.stake": Style(color=HTCLIColors.STAKING_ORANGE, bold=True),
149
+ "htcli.reward": Style(color=HTCLIColors.CRYPTO_GOLD, bold=True),
150
+ "htcli.delegation": Style(color=HTCLIColors.SECONDARY_LIGHT),
151
+ # Interactive elements
152
+ "htcli.prompt": Style(color=HTCLIColors.PRIMARY, bold=True),
153
+ "htcli.input": Style(color=HTCLIColors.GRAY_700),
154
+ "htcli.selection": Style(
155
+ color=HTCLIColors.PRIMARY, bold=True, reverse=True
156
+ ),
157
+ "htcli.highlight": Style(color=HTCLIColors.PRIMARY_LIGHT, bold=True),
158
+ # Tables and data
159
+ "htcli.table.header": Style(color=HTCLIColors.PRIMARY, bold=True),
160
+ "htcli.table.row": Style(color=HTCLIColors.GRAY_700),
161
+ "htcli.table.alt": Style(color=HTCLIColors.GRAY_600),
162
+ "htcli.table.border": Style(color=HTCLIColors.GRAY_300),
163
+ # Progress and loading
164
+ "htcli.progress.bar": Style(color=HTCLIColors.PRIMARY),
165
+ "htcli.progress.percentage": Style(
166
+ color=HTCLIColors.PRIMARY, bold=True
167
+ ),
168
+ "htcli.spinner": Style(color=HTCLIColors.PRIMARY, bold=True),
169
+ # Code and technical
170
+ "htcli.code": Style(color=HTCLIColors.GRAY_600, italic=True),
171
+ "htcli.command": Style(color=HTCLIColors.SECONDARY, bold=True),
172
+ "htcli.option": Style(color=HTCLIColors.GRAY_500),
173
+ "htcli.value": Style(color=HTCLIColors.GRAY_400, bold=False),
174
+ # Borders and panels
175
+ "htcli.border": Style(color=HTCLIColors.GRAY_300),
176
+ "htcli.panel": Style(color=HTCLIColors.GRAY_700),
177
+ "htcli.panel.border": Style(color=HTCLIColors.GRAY_300),
178
+ "htcli.panel.title": Style(color=HTCLIColors.PRIMARY, bold=True),
179
+ # Tree structures
180
+ "htcli.tree": Style(color=HTCLIColors.GRAY_700),
181
+ "htcli.tree.branch": Style(color=HTCLIColors.GRAY_600),
182
+ # Special states
183
+ "htcli.disabled": Style(color=HTCLIColors.GRAY_500, dim=True),
184
+ "htcli.selected": Style(
185
+ color=HTCLIColors.PRIMARY, bold=True, reverse=True
186
+ ),
187
+ "htcli.focused": Style(color=HTCLIColors.PRIMARY_LIGHT, bold=True),
188
+ # Emojis and icons context
189
+ "htcli.icon.success": Style(color=HTCLIColors.SUCCESS),
190
+ "htcli.icon.error": Style(color=HTCLIColors.ERROR),
191
+ "htcli.icon.warning": Style(color=HTCLIColors.WARNING),
192
+ "htcli.icon.info": Style(color=HTCLIColors.INFO),
193
+ "htcli.icon.blockchain": Style(color=HTCLIColors.BLOCKCHAIN_BLUE),
194
+ "htcli.icon.crypto": Style(color=HTCLIColors.CRYPTO_GOLD),
195
+ }
196
+ )
197
+
198
+ @classmethod
199
+ def get_style(cls, style_name: str, fallback: str = "default") -> Style:
200
+ """Get a specific style from the theme."""
201
+ theme = cls.get_theme()
202
+ return theme.styles.get(style_name, Style.parse(fallback))
203
+
204
+ @classmethod
205
+ def get_color_styles(cls) -> dict[str, Style]:
206
+ """Get just the color styles for quick access."""
207
+ return {
208
+ "primary": Style(color=HTCLIColors.PRIMARY),
209
+ "secondary": Style(color=HTCLIColors.SECONDARY),
210
+ "success": Style(color=HTCLIColors.SUCCESS),
211
+ "error": Style(color=HTCLIColors.ERROR),
212
+ "warning": Style(color=HTCLIColors.WARNING),
213
+ "info": Style(color=HTCLIColors.INFO),
214
+ "muted": Style(color=HTCLIColors.GRAY_500),
215
+ "dim": Style(color=HTCLIColors.GRAY_500),
216
+ }
217
+
218
+
219
+ # Utility functions for quick color access
220
+ def primary(text: str, bold: bool = True) -> str:
221
+ """Wrap text in primary color."""
222
+ style = "bold " if bold else ""
223
+ return f"[{style}{HTCLIColors.PRIMARY}]{text}[/]"
224
+
225
+
226
+ def success(text: str, bold: bool = True) -> str:
227
+ """Wrap text in success color."""
228
+ style = "bold " if bold else ""
229
+ return f"[{style}{HTCLIColors.SUCCESS}]{text}[/]"
230
+
231
+
232
+ def error(text: str, bold: bool = True) -> str:
233
+ """Wrap text in error color."""
234
+ style = "bold " if bold else ""
235
+ return f"[{style}{HTCLIColors.ERROR}]{text}[/]"
236
+
237
+
238
+ def warning(text: str, bold: bool = True) -> str:
239
+ """Wrap text in warning color."""
240
+ style = "bold " if bold else ""
241
+ return f"[{style}{HTCLIColors.WARNING}]{text}[/]"
242
+
243
+
244
+ def info(text: str, bold: bool = True) -> str:
245
+ """Wrap text in info color."""
246
+ style = "bold " if bold else ""
247
+ return f"[{style}{HTCLIColors.INFO}]{text}[/]"
248
+
249
+
250
+ def muted(text: str) -> str:
251
+ """Wrap text in muted color."""
252
+ return f"[{HTCLIColors.GRAY_500}]{text}[/]"
253
+
254
+
255
+ def highlight(text: str) -> str:
256
+ """Highlight text with primary color and bold."""
257
+ return f"[bold {HTCLIColors.PRIMARY}]{text}[/]"
258
+
259
+
260
+ def amount(value: str, currency: str = "") -> str:
261
+ """Format an amount with crypto gold color."""
262
+ if currency:
263
+ return f"[bold {HTCLIColors.CRYPTO_GOLD}]{value}[/] [dim]{currency}[/]"
264
+ return f"[bold {HTCLIColors.CRYPTO_GOLD}]{value}[/]"
265
+
266
+
267
+ def address(addr: str, truncate: bool = True) -> str:
268
+ """Format a blockchain address in MetaMask style."""
269
+ from src.htcli.utils.blockchain.formatting import to_checksum_address
270
+ # Apply EIP-55 checksum first
271
+ checksummed = to_checksum_address(addr) or addr
272
+
273
+ if truncate and len(checksummed) > 10:
274
+ # Remove 0x prefix if present for calculation
275
+ clean_address = checksummed[2:] if checksummed.startswith("0x") else checksummed
276
+
277
+ if len(clean_address) > 10:
278
+ display_addr = f"0x{clean_address[:5]}...{clean_address[-5:]}"
279
+ else:
280
+ display_addr = checksummed
281
+ else:
282
+ display_addr = checksummed
283
+ return f"[bold {HTCLIColors.BLOCKCHAIN_BLUE}]{display_addr}[/]"
284
+
285
+
286
+ def address_full(addr: str) -> str:
287
+ """Format a blockchain address in full (no truncation)."""
288
+ return f"[bold {HTCLIColors.BLOCKCHAIN_BLUE}]{addr}[/]"
289
+
290
+
291
+ def format_address_display(addr: str, truncate: bool = True) -> str:
292
+ """Centralized address formatting function for consistent use across all displays."""
293
+ if not addr or addr == "N/A":
294
+ return "N/A"
295
+
296
+ from src.htcli.utils.blockchain.formatting import to_checksum_address
297
+ # Apply EIP-55 checksum first
298
+ checksummed = to_checksum_address(addr) or addr
299
+
300
+ if truncate and len(checksummed) > 10:
301
+ # Remove 0x prefix if present for calculation
302
+ clean_address = checksummed[2:] if checksummed.startswith("0x") else checksummed
303
+
304
+ if len(clean_address) > 10:
305
+ return f"0x{clean_address[:5]}...{clean_address[-5:]}"
306
+ else:
307
+ return checksummed
308
+ else:
309
+ return checksummed
@@ -0,0 +1,60 @@
1
+ """
2
+ HTCLI UI Components Package.
3
+ Provides all reusable UI components for the HTCLI application.
4
+ """
5
+
6
+ # Import all components for easy access
7
+ from .panels import (
8
+ HTCLIPanel,
9
+ HTCLIStatus,
10
+ create_error_summary_panel,
11
+ create_help_panel,
12
+ create_success_summary_panel,
13
+ create_welcome_panel,
14
+ )
15
+ from .progress import HTCLILoadingContext, HTCLIProgress
16
+ from .spinners import HTCLISpinner
17
+ from .tables import (
18
+ HTCLITable,
19
+ create_balance_table,
20
+ create_htcli_balance_table,
21
+ create_node_table,
22
+ create_subnet_table,
23
+ create_wallet_minimal_table,
24
+ create_wallet_table,
25
+ )
26
+ from .trees import (
27
+ HTCLITree,
28
+ create_network_tree,
29
+ create_subnet_details_tree,
30
+ create_transaction_tree,
31
+ create_wallet_hierarchy_tree,
32
+ create_wallet_tree,
33
+ )
34
+
35
+ __all__ = [
36
+ # Core components
37
+ "HTCLISpinner",
38
+ "HTCLIProgress",
39
+ "HTCLILoadingContext",
40
+ "HTCLITable",
41
+ "HTCLITree",
42
+ "HTCLIPanel",
43
+ "HTCLIStatus",
44
+ # Factory functions
45
+ "create_balance_table",
46
+ "create_htcli_balance_table",
47
+ "create_node_table",
48
+ "create_subnet_table",
49
+ "create_wallet_minimal_table",
50
+ "create_wallet_table",
51
+ "create_network_tree",
52
+ "create_wallet_hierarchy_tree",
53
+ "create_wallet_tree",
54
+ "create_subnet_details_tree",
55
+ "create_transaction_tree",
56
+ "create_welcome_panel",
57
+ "create_help_panel",
58
+ "create_error_summary_panel",
59
+ "create_success_summary_panel",
60
+ ]