add tooltips
This commit is contained in:
parent
b2fbac4622
commit
a32334eaa3
6 changed files with 90 additions and 55 deletions
|
|
@ -15,7 +15,7 @@ class AppConfig(BaseModel):
|
|||
target_platform: str = Field(default="fluxer") # fluxer | stoat | none
|
||||
target_bot_token: Optional[str] = Field(default=None)
|
||||
target_server_id: Optional[str] = Field(default=None)
|
||||
target_api_url: Optional[str] = Field(default="default")
|
||||
target_api_url: Optional[str] = Field(default=None)
|
||||
migration: MigrationSettings = Field(default_factory=MigrationSettings)
|
||||
|
||||
# ── backward‑compat shims (read‑only) ────────────────────────────────
|
||||
|
|
@ -77,12 +77,12 @@ def load_config(config_path: Union[str, Path] = "config.yaml", create_if_missing
|
|||
data.setdefault("target_platform", "fluxer")
|
||||
data.setdefault("target_bot_token", data["fluxer_bot_token"])
|
||||
data.setdefault("target_server_id", data.get("fluxer_community_id"))
|
||||
data.setdefault("target_api_url", data.get("fluxer_api_url", "default"))
|
||||
data.setdefault("target_api_url", data.get("fluxer_api_url") or "default")
|
||||
elif data.get("stoat_bot_token") and data["stoat_bot_token"] not in ("STOAT_BOT_TOKEN", None):
|
||||
data.setdefault("target_platform", "stoat")
|
||||
data.setdefault("target_bot_token", data["stoat_bot_token"])
|
||||
data.setdefault("target_server_id", data.get("stoat_server_id"))
|
||||
data.setdefault("target_api_url", data.get("stoat_api_url", "default"))
|
||||
data.setdefault("target_api_url", data.get("stoat_api_url") or "default")
|
||||
# Remove legacy keys so they don't conflict with the model
|
||||
for key in ("fluxer_bot_token", "fluxer_community_id", "fluxer_api_url",
|
||||
"stoat_bot_token", "stoat_server_id", "stoat_api_url",
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@ class BackupPane(Container):
|
|||
|
||||
yield Label("", id="bp_lbl_backup")
|
||||
with Vertical(id="bp_actions"):
|
||||
yield Button("Backup Server Profile", id="bp_backup_profile", disabled=True)
|
||||
yield Button("Backup Channel Messages", id="bp_backup_msgs", disabled=True, variant="primary")
|
||||
yield Button("Update Existing Backup", id="bp_backup_sync", disabled=True, variant="success")
|
||||
yield Button("Backup Server Profile", id="bp_backup_profile", disabled=True, tooltip="Backup Discord server roles, emojis, and channel structure")
|
||||
yield Button("Backup Channel Messages", id="bp_backup_msgs", disabled=True, variant="primary", tooltip="Select and backup message history from text channels")
|
||||
yield Button("Update Existing Backup", id="bp_backup_sync", disabled=True, variant="success", tooltip="Scan for new messages\n& Update existing backup")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self._validate()
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ class NewConfigModal(ModalScreen[str]):
|
|||
def compose(self) -> ComposeResult:
|
||||
with Vertical(id="new_config_dialog"):
|
||||
yield Label("Enter new configuration name:", id="new_config_title")
|
||||
yield Input(placeholder="e.g. MyServer", id="new_config_input")
|
||||
yield Input(placeholder="e.g. MyServer", id="new_config_input", tooltip="Enter a unique name for this config")
|
||||
with Horizontal(id="new_config_buttons"):
|
||||
yield Button("Create", variant="success", id="btn_create")
|
||||
yield Button("Create", variant="success", id="btn_create", tooltip="Create config and launch setup")
|
||||
yield Button("Cancel", variant="primary", id="btn_cancel")
|
||||
|
||||
def _get_sanitized_name(self) -> str:
|
||||
|
|
@ -97,7 +97,7 @@ class ConfigSelectionScreen(Screen):
|
|||
with VerticalScroll(id="config_list_container"):
|
||||
yield ListView(id="config_list")
|
||||
with Horizontal(id="config_sel_actions"):
|
||||
yield Button("New Config", id="btn_new_config", variant="success")
|
||||
yield Button("New Config", id="btn_new_config", variant="success", tooltip="Create a new configuration folder")
|
||||
yield Button("Exit", id="btn_exit", variant="error")
|
||||
yield Footer()
|
||||
|
||||
|
|
@ -219,9 +219,10 @@ class ConfigScreen(Screen):
|
|||
value=self.config.discord_bot_token or "",
|
||||
id="inp_discord_token",
|
||||
password=True,
|
||||
placeholder="Paste Bot Token here"
|
||||
placeholder="Paste Bot Token here",
|
||||
tooltip="Enter your Discord BOT token from the Developer Portal"
|
||||
)
|
||||
yield Button("Validate", id="btn_fetch_guilds", variant="primary")
|
||||
yield Button("Validate", id="btn_fetch_guilds", variant="primary", tooltip="Verify token and fetch available Discord servers")
|
||||
|
||||
yield Label("Server ID:", classes="field_label")
|
||||
yield Select(
|
||||
|
|
@ -237,17 +238,17 @@ class ConfigScreen(Screen):
|
|||
yield RadioButton(
|
||||
"Shuttle Transfer (direct migration)",
|
||||
id="radio_direct",
|
||||
value=(cur_mode == "direct_transfer"),
|
||||
value=(cur_mode == "direct_transfer")
|
||||
)
|
||||
yield RadioButton(
|
||||
"Backup & Migrate (backup first, then migrate)",
|
||||
id="radio_backup",
|
||||
value=(cur_mode == "backup_transfer"),
|
||||
value=(cur_mode == "backup_transfer")
|
||||
)
|
||||
yield RadioButton(
|
||||
"Backup Only (local backup, no migration)",
|
||||
id="radio_bkonly",
|
||||
value=(cur_mode == "backup_only"),
|
||||
value=(cur_mode == "backup_only")
|
||||
)
|
||||
|
||||
# ── Target Platform (hidden for backup_only) ─────────────
|
||||
|
|
@ -258,12 +259,12 @@ class ConfigScreen(Screen):
|
|||
yield RadioButton(
|
||||
"Fluxer",
|
||||
id="radio_fluxer",
|
||||
value=(cur_plat == "fluxer"),
|
||||
value=(cur_plat == "fluxer")
|
||||
)
|
||||
yield RadioButton(
|
||||
"Stoat",
|
||||
id="radio_stoat",
|
||||
value=(cur_plat == "stoat"),
|
||||
value=(cur_plat == "stoat")
|
||||
)
|
||||
yield Label("Bot Token:", classes="field_label")
|
||||
with Horizontal(classes="fetch_row"):
|
||||
|
|
@ -271,9 +272,10 @@ class ConfigScreen(Screen):
|
|||
value=self.config.target_bot_token or "",
|
||||
id="inp_target_token",
|
||||
password=True,
|
||||
placeholder="Paste Target Bot Token"
|
||||
placeholder="Paste Target Bot Token",
|
||||
tooltip="Enter the Bot token for the target platform"
|
||||
)
|
||||
yield Button("Validate", id="btn_fetch_target_servers", variant="primary")
|
||||
yield Button("Validate", id="btn_fetch_target_servers", variant="primary", tooltip="Verify token and fetch available communities")
|
||||
|
||||
yield Label("Community / Server ID:", classes="field_label")
|
||||
yield Select(
|
||||
|
|
@ -284,13 +286,15 @@ class ConfigScreen(Screen):
|
|||
|
||||
yield Label("Target API URL:", classes="field_label")
|
||||
yield Input(
|
||||
value=self.config.target_api_url or "default",
|
||||
value=self.config.target_api_url if (self.config.target_api_url and self.config.target_api_url != "default") else "",
|
||||
id="inp_target_api",
|
||||
placeholder="Leave this Empty for official instance",
|
||||
tooltip="Enter the custom API url\nfor self hosted instances"
|
||||
)
|
||||
|
||||
yield Rule()
|
||||
with Horizontal(id="cfg_actions"):
|
||||
yield Button("Save Configuration", variant="success", id="btn_save")
|
||||
yield Button("Save Configuration", variant="success", id="btn_save", tooltip="Save all changes to config.yaml")
|
||||
yield Button("Back", id="btn_back")
|
||||
yield Footer()
|
||||
|
||||
|
|
@ -455,7 +459,7 @@ class ConfigScreen(Screen):
|
|||
self.config.target_server_id = None
|
||||
|
||||
target_api = self.query_one("#inp_target_api", Input).value.strip()
|
||||
self.config.target_api_url = target_api or "default"
|
||||
self.config.target_api_url = target_api or None
|
||||
else:
|
||||
self.config.target_platform = "none"
|
||||
|
||||
|
|
|
|||
|
|
@ -101,14 +101,14 @@ class ProgressScreen(Screen[None]):
|
|||
|
||||
with Vertical(id="prog_actions"):
|
||||
with Horizontal(classes="action_row", id="prog_actions_row1"):
|
||||
yield Button("Start from First", id="btn_start_first", disabled=True, variant="primary")
|
||||
yield Button("Continue Migration", id="btn_continue", disabled=True, variant="success")
|
||||
yield Button("Start from ID", id="btn_start_id", disabled=True, variant="warning")
|
||||
yield Button("Start from First", id="btn_start_first", disabled=True, variant="primary", tooltip="Start the operation from the beginning")
|
||||
yield Button("Continue Migration", id="btn_continue", disabled=True, variant="success", tooltip="Resume the operation from the last saved state")
|
||||
yield Button("Start from ID", id="btn_start_id", disabled=True, variant="warning", tooltip="Start or resume from a specific Discord Message ID")
|
||||
with Horizontal(classes="action_row", id="prog_actions_row2"):
|
||||
yield Button("Back", id="btn_back", disabled=False)
|
||||
yield Button("Main Menu", id="btn_main_menu", disabled=False)
|
||||
with Horizontal(classes="action_row", id="prog_actions_cancel"):
|
||||
yield Button("Cancel", id="btn_cancel", variant="error")
|
||||
yield Button("Cancel", id="btn_cancel", variant="error", tooltip="Stop current operation")
|
||||
yield Footer()
|
||||
|
||||
def __init__(self, log_level: str = "INFO", *args, **kwargs):
|
||||
|
|
@ -232,7 +232,11 @@ class ProgressScreen(Screen[None]):
|
|||
show_id: bool = True,
|
||||
btn_start_label: str = "Start from First",
|
||||
btn_continue_label: str = "Continue Migration",
|
||||
btn_id_label: str = "Start from ID"
|
||||
btn_id_label: str = "Start from ID",
|
||||
btn_start_variant: str = "primary",
|
||||
btn_start_tooltip: str | None = None,
|
||||
btn_continue_tooltip: str | None = None,
|
||||
btn_id_tooltip: str | None = None
|
||||
):
|
||||
"""Phase 2: Wait for user confirmation after analysis."""
|
||||
try: self.query_one("#prog_loader", LoadingIndicator).display = False
|
||||
|
|
@ -241,12 +245,27 @@ class ProgressScreen(Screen[None]):
|
|||
try: self.query_one("#prog_timer", Label).display = False
|
||||
except Exception: pass
|
||||
|
||||
# Update button labels
|
||||
try: self.query_one("#btn_start_first", Button).label = btn_start_label
|
||||
# Update button labels, variants and tooltips
|
||||
try:
|
||||
btn_start = self.query_one("#btn_start_first", Button)
|
||||
btn_start.label = btn_start_label
|
||||
btn_start.variant = btn_start_variant
|
||||
if btn_start_tooltip:
|
||||
btn_start.tooltip = btn_start_tooltip
|
||||
except Exception: pass
|
||||
try: self.query_one("#btn_continue", Button).label = btn_continue_label
|
||||
|
||||
try:
|
||||
btn_cont = self.query_one("#btn_continue", Button)
|
||||
btn_cont.label = btn_continue_label
|
||||
if btn_continue_tooltip:
|
||||
btn_cont.tooltip = btn_continue_tooltip
|
||||
except Exception: pass
|
||||
try: self.query_one("#btn_start_id", Button).label = btn_id_label
|
||||
|
||||
try:
|
||||
btn_id = self.query_one("#btn_start_id", Button)
|
||||
btn_id.label = btn_id_label
|
||||
if btn_id_tooltip:
|
||||
btn_id.tooltip = btn_id_tooltip
|
||||
except Exception: pass
|
||||
|
||||
# Show confirmation buttons
|
||||
|
|
@ -426,8 +445,8 @@ class OptionSelectModal(ModalScreen[list[str]]):
|
|||
with Vertical(id="opt_dialog"):
|
||||
yield Label(self._title, id="opt_title")
|
||||
with Horizontal(id="opt_batch_buttons"):
|
||||
yield Button("Select All", id="btn_opt_all", flat=True)
|
||||
yield Button("Deselect All", id="btn_opt_none", flat=True)
|
||||
yield Button("Select All", id="btn_opt_all", flat=True, tooltip="Select all available options")
|
||||
yield Button("Deselect All", id="btn_opt_none", flat=True, tooltip="Deselect all options")
|
||||
|
||||
with Vertical(id="opt_scroll"):
|
||||
for opt_id, label in self._options:
|
||||
|
|
@ -435,8 +454,8 @@ class OptionSelectModal(ModalScreen[list[str]]):
|
|||
|
||||
yield Rule()
|
||||
with Horizontal(id="opt_buttons"):
|
||||
yield Button("Proceed", variant="success", id="btn_opt_ok")
|
||||
yield Button("Back", id="btn_opt_back")
|
||||
yield Button("Proceed", variant="success", id="btn_opt_ok", tooltip="Proceed with the selected options")
|
||||
yield Button("Back", id="btn_opt_back", tooltip="Return to the previous menu")
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed):
|
||||
if event.button.id == "btn_opt_back":
|
||||
|
|
@ -568,8 +587,8 @@ class ChannelPickerScreen(Screen[tuple]):
|
|||
|
||||
yield Rule()
|
||||
with Horizontal(id="chanpick_buttons"):
|
||||
yield Button("Select", variant="success", id="btn_pick_ok")
|
||||
yield Button("Back", id="btn_pick_back")
|
||||
yield Button("Select", variant="success", id="btn_pick_ok", tooltip="Confirm selection and start migration")
|
||||
yield Button("Back", id="btn_pick_back", tooltip="Cancel selection")
|
||||
yield Footer()
|
||||
|
||||
def on_mount(self) -> None:
|
||||
|
|
@ -719,17 +738,17 @@ class ChannelSelectScreen(Screen[dict]):
|
|||
yield Label("Note: Channels shown in green have existing backups", classes="label_warning")
|
||||
|
||||
with Horizontal(id="select_all_buttons"):
|
||||
yield Button("Select All", id="btn_all")
|
||||
yield Button("Deselect All", id="btn_none")
|
||||
yield Button("Select All", id="btn_all", tooltip="Select all channels for backup")
|
||||
yield Button("Deselect All", id="btn_none", tooltip="Deselect all channels")
|
||||
|
||||
yield Rule()
|
||||
with Horizontal(id="confirm_buttons"):
|
||||
if self.any_found:
|
||||
yield Button("Sync", variant="success", id="btn_sync")
|
||||
yield Button("Force Overwrite", variant="warning", id="btn_force")
|
||||
yield Button("Sync", variant="success", id="btn_sync", tooltip="Backup new channels\n& update existing backups")
|
||||
yield Button("Force Overwrite", variant="warning", id="btn_force", tooltip="Overwrite existing backups\nwith fresh data")
|
||||
else:
|
||||
yield Button("Backup", variant="success", id="btn_backup")
|
||||
yield Button("Back", id="btn_cancel_chan")
|
||||
yield Button("Backup", variant="success", id="btn_backup", tooltip="Start backing up selected channels")
|
||||
yield Button("Back", id="btn_cancel_chan", tooltip="Cancel and go back")
|
||||
yield Footer()
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
|
|
@ -798,8 +817,8 @@ class MessageIDInputModal(ModalScreen[int | None]):
|
|||
yield Label("Enter an ID and click Verify to preview.", id="lbl_msg_preview")
|
||||
|
||||
with Horizontal(id="msg_id_buttons"):
|
||||
yield Button("Verify", variant="primary", id="btn_verify_start", disabled=True)
|
||||
yield Button("Back", variant="warning", id="btn_cancel_msg_id")
|
||||
yield Button("Verify", variant="primary", id="btn_verify_start", disabled=True, tooltip="Check if this message ID exists in the channel")
|
||||
yield Button("Back", variant="warning", id="btn_cancel_msg_id", tooltip="Cancel and go back")
|
||||
|
||||
def on_input_changed(self, event: Input.Changed) -> None:
|
||||
if event.input.id == "input_msg_id":
|
||||
|
|
|
|||
|
|
@ -129,8 +129,8 @@ class ModeScreen(Screen):
|
|||
yield Rule()
|
||||
with Horizontal(id="bottom_actions"):
|
||||
if mode == "backup_transfer":
|
||||
yield Button("Switch to Migrate ⇄", id="btn_switch", variant="primary")
|
||||
yield Button("Configuration", id="btn_config", variant="success")
|
||||
yield Button("Switch to Migrate ⇄", id="btn_switch", variant="primary", tooltip="Switch between\nBackup & Migrate operations")
|
||||
yield Button("Configuration", id="btn_config", variant="success", tooltip="Change Bot tokens\nand Reaper Mode")
|
||||
yield Button("Exit", id="btn_exit", variant="error")
|
||||
|
||||
yield Footer()
|
||||
|
|
|
|||
|
|
@ -129,11 +129,11 @@ class ShuttlePane(Container):
|
|||
|
||||
yield Label("", id="sp_lbl_status")
|
||||
with Vertical(id="sp_actions"):
|
||||
yield Button("Clone Server Template", id="sp_clone", disabled=True)
|
||||
yield Button("Sync Server Settings", id="sp_sync", disabled=True)
|
||||
yield Button("Migrate Message History", id="sp_messages", disabled=True, variant="primary")
|
||||
yield Button("Clone Server Template", id="sp_clone", disabled=True, tooltip="Clone server roles, categories, and channels to the target community")
|
||||
yield Button("Sync Server Settings", id="sp_sync", disabled=True, tooltip="Sync emojis, stickers, server name, and icon to the target community")
|
||||
yield Button("Migrate Message History", id="sp_messages", disabled=True, variant="primary", tooltip="Migrate message history from Discord to the target platform")
|
||||
yield Rule()
|
||||
yield Button("Danger Zone ⚠", id="sp_danger", variant="error", disabled=True, flat=True)
|
||||
yield Button("Danger Zone ⚠", id="sp_danger", variant="error", disabled=True, flat=True, tooltip="Dangerous operations:\ndelete channels, roles, emojis on target\n(use with caution)")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self._rebuild_engine()
|
||||
|
|
@ -459,8 +459,10 @@ class ShuttlePane(Container):
|
|||
|
||||
choice = await modal.phase_wait_confirm(
|
||||
btn_start_label="Start Cloning",
|
||||
btn_id_label="Force Clone (may create duplicates)",
|
||||
show_id=True
|
||||
btn_id_label="Force Clone",
|
||||
show_id=True,
|
||||
btn_start_tooltip="Clone without creating duplicates",
|
||||
btn_id_tooltip="Force clone everything\n(may create duplicates)"
|
||||
)
|
||||
if choice == "btn_back":
|
||||
modal.dismiss()
|
||||
|
|
@ -539,7 +541,9 @@ class ShuttlePane(Container):
|
|||
choice = await modal.phase_wait_confirm(
|
||||
btn_start_label="Start Syncing",
|
||||
btn_id_label="Force Sync",
|
||||
show_id=True
|
||||
show_id=True,
|
||||
btn_start_tooltip="Sync new assets only",
|
||||
btn_id_tooltip="Force sync assets\n(may create duplicates)"
|
||||
)
|
||||
if choice == "btn_back":
|
||||
modal.dismiss()
|
||||
|
|
@ -905,7 +909,10 @@ class ShuttlePane(Container):
|
|||
show_id=True,
|
||||
btn_start_label="Start from\nFirst Message",
|
||||
btn_continue_label="Continue\nMigration",
|
||||
btn_id_label="Start from\nmessage ID"
|
||||
btn_id_label="Start from\nmessage ID",
|
||||
btn_start_tooltip="Start migrating from the earliest available message",
|
||||
btn_continue_tooltip="Resume from the last successfully migrated message",
|
||||
btn_id_tooltip="Start migrating from a specific Discord message ID"
|
||||
)
|
||||
logger.info(f"User confirmation choice: {choice}")
|
||||
if choice == "btn_back":
|
||||
|
|
@ -1084,7 +1091,12 @@ class ShuttlePane(Container):
|
|||
modal.write(f" [dim](could not fetch list)[/dim]")
|
||||
modal.write("")
|
||||
|
||||
choice = await modal.phase_wait_confirm(btn_start_label="WIPE ALL DATA", show_id=False)
|
||||
choice = await modal.phase_wait_confirm(
|
||||
btn_start_label="WIPE ALL DATA",
|
||||
show_id=False,
|
||||
btn_start_variant="error",
|
||||
btn_start_tooltip="WARNING\nIrreversible Operation!\nProceed with Caution"
|
||||
)
|
||||
if choice == "btn_back":
|
||||
modal.dismiss()
|
||||
self._open_danger_menu()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue