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.
- htcli-1.1.0.dist-info/METADATA +509 -0
- htcli-1.1.0.dist-info/RECORD +140 -0
- htcli-1.1.0.dist-info/WHEEL +4 -0
- htcli-1.1.0.dist-info/entry_points.txt +2 -0
- htcli-1.1.0.dist-info/licenses/LICENSE +21 -0
- src/__init__.py +0 -0
- src/htcli/__init__.py +5 -0
- src/htcli/client/__init__.py +338 -0
- src/htcli/client/extrinsics/__init__.py +26 -0
- src/htcli/client/extrinsics/base.py +487 -0
- src/htcli/client/extrinsics/consensus.py +79 -0
- src/htcli/client/extrinsics/governance.py +714 -0
- src/htcli/client/extrinsics/identity.py +490 -0
- src/htcli/client/extrinsics/node.py +1054 -0
- src/htcli/client/extrinsics/overwatch.py +401 -0
- src/htcli/client/extrinsics/staking.py +1504 -0
- src/htcli/client/extrinsics/subnet.py +2218 -0
- src/htcli/client/extrinsics/validator.py +203 -0
- src/htcli/client/extrinsics/wallet.py +323 -0
- src/htcli/client/offchain/__init__.py +10 -0
- src/htcli/client/offchain/backup.py +385 -0
- src/htcli/client/offchain/config.py +541 -0
- src/htcli/client/offchain/wallet.py +839 -0
- src/htcli/client/rpc/__init__.py +20 -0
- src/htcli/client/rpc/chain.py +568 -0
- src/htcli/client/rpc/node.py +783 -0
- src/htcli/client/rpc/overwatch.py +680 -0
- src/htcli/client/rpc/staking.py +216 -0
- src/htcli/client/rpc/subnet.py +2104 -0
- src/htcli/client/rpc/wallet.py +912 -0
- src/htcli/commands/__init__.py +31 -0
- src/htcli/commands/chain/__init__.py +66 -0
- src/htcli/commands/chain/display.py +204 -0
- src/htcli/commands/chain/handlers.py +260 -0
- src/htcli/commands/config/__init__.py +158 -0
- src/htcli/commands/config/display.py +353 -0
- src/htcli/commands/config/handlers.py +347 -0
- src/htcli/commands/config/prompts.py +357 -0
- src/htcli/commands/consensus/__init__.py +61 -0
- src/htcli/commands/consensus/handlers.py +100 -0
- src/htcli/commands/governance/__init__.py +49 -0
- src/htcli/commands/governance/handlers.py +81 -0
- src/htcli/commands/node/__init__.py +304 -0
- src/htcli/commands/node/display.py +749 -0
- src/htcli/commands/node/error_handling.py +470 -0
- src/htcli/commands/node/handlers.py +844 -0
- src/htcli/commands/node/prompts.py +346 -0
- src/htcli/commands/overwatch/__init__.py +219 -0
- src/htcli/commands/overwatch/display.py +396 -0
- src/htcli/commands/overwatch/error_handling.py +276 -0
- src/htcli/commands/overwatch/handlers.py +443 -0
- src/htcli/commands/overwatch/prompts.py +359 -0
- src/htcli/commands/stake/__init__.py +736 -0
- src/htcli/commands/stake/display.py +1103 -0
- src/htcli/commands/stake/error_handling.py +425 -0
- src/htcli/commands/stake/handlers.py +1902 -0
- src/htcli/commands/stake/prompts.py +1080 -0
- src/htcli/commands/subnet/__init__.py +639 -0
- src/htcli/commands/subnet/display.py +801 -0
- src/htcli/commands/subnet/error_handling.py +524 -0
- src/htcli/commands/subnet/handlers.py +2855 -0
- src/htcli/commands/subnet/prompts.py +1225 -0
- src/htcli/commands/validator/__init__.py +192 -0
- src/htcli/commands/validator/display.py +54 -0
- src/htcli/commands/validator/handlers.py +340 -0
- src/htcli/commands/wallet/__init__.py +546 -0
- src/htcli/commands/wallet/display.py +806 -0
- src/htcli/commands/wallet/error_handling.py +210 -0
- src/htcli/commands/wallet/handlers.py +3040 -0
- src/htcli/commands/wallet/prompts.py +1518 -0
- src/htcli/config.py +184 -0
- src/htcli/dependencies.py +186 -0
- src/htcli/errors/__init__.py +63 -0
- src/htcli/errors/base.py +141 -0
- src/htcli/errors/display.py +20 -0
- src/htcli/errors/handlers.py +710 -0
- src/htcli/main.py +343 -0
- src/htcli/models/__init__.py +21 -0
- src/htcli/models/enums/enum_types.py +35 -0
- src/htcli/models/errors.py +103 -0
- src/htcli/models/requests/__init__.py +197 -0
- src/htcli/models/requests/config.py +70 -0
- src/htcli/models/requests/consensus.py +19 -0
- src/htcli/models/requests/governance.py +38 -0
- src/htcli/models/requests/identity.py +51 -0
- src/htcli/models/requests/key.py +22 -0
- src/htcli/models/requests/node.py +91 -0
- src/htcli/models/requests/overwatch.py +64 -0
- src/htcli/models/requests/staking.py +580 -0
- src/htcli/models/requests/subnet.py +195 -0
- src/htcli/models/requests/validator.py +139 -0
- src/htcli/models/requests/wallet.py +118 -0
- src/htcli/models/responses/__init__.py +147 -0
- src/htcli/models/responses/base.py +18 -0
- src/htcli/models/responses/chain.py +39 -0
- src/htcli/models/responses/config.py +58 -0
- src/htcli/models/responses/identity.py +102 -0
- src/htcli/models/responses/overwatch.py +51 -0
- src/htcli/models/responses/staking.py +502 -0
- src/htcli/models/responses/subnet.py +856 -0
- src/htcli/models/responses/wallet.py +185 -0
- src/htcli/ui/__init__.py +87 -0
- src/htcli/ui/colors.py +309 -0
- src/htcli/ui/components/__init__.py +60 -0
- src/htcli/ui/components/panels.py +174 -0
- src/htcli/ui/components/progress.py +166 -0
- src/htcli/ui/components/spinners.py +92 -0
- src/htcli/ui/components/tables.py +809 -0
- src/htcli/ui/components/trees.py +721 -0
- src/htcli/ui/display.py +336 -0
- src/htcli/ui/prompts.py +870 -0
- src/htcli/utils/__init__.py +76 -0
- src/htcli/utils/blockchain/__init__.py +75 -0
- src/htcli/utils/blockchain/formatting.py +368 -0
- src/htcli/utils/blockchain/patches.py +286 -0
- src/htcli/utils/blockchain/peer_id.py +186 -0
- src/htcli/utils/blockchain/staking.py +448 -0
- src/htcli/utils/blockchain/type_registry.py +1373 -0
- src/htcli/utils/blockchain/validation.py +179 -0
- src/htcli/utils/cache.py +613 -0
- src/htcli/utils/constants.py +38 -0
- src/htcli/utils/legacy/__init__.py +12 -0
- src/htcli/utils/legacy/colors.py +311 -0
- src/htcli/utils/legacy/crypto.py +1176 -0
- src/htcli/utils/legacy/formatting.py +452 -0
- src/htcli/utils/legacy/interactive.py +306 -0
- src/htcli/utils/legacy/subnet_manifest.py +265 -0
- src/htcli/utils/legacy/validation.py +488 -0
- src/htcli/utils/logging.py +183 -0
- src/htcli/utils/network/__init__.py +20 -0
- src/htcli/utils/network/subnet.py +344 -0
- src/htcli/utils/prompts.py +27 -0
- src/htcli/utils/scale_codec.py +155 -0
- src/htcli/utils/validation/__init__.py +57 -0
- src/htcli/utils/validation/prompt_validators.py +267 -0
- src/htcli/utils/wallet/__init__.py +65 -0
- src/htcli/utils/wallet/auth.py +151 -0
- src/htcli/utils/wallet/core.py +1069 -0
- src/htcli/utils/wallet/crypto.py +1615 -0
- 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
|
+
)
|
src/htcli/ui/__init__.py
ADDED
|
@@ -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
|
+
]
|