v3.42.11: Replace all Unicode emojis with ASCII text
- Replace all emoji characters with ASCII equivalents throughout codebase - Replace Unicode box-drawing characters (═║╔╗╚╝━─) with ASCII (+|-=) - Replace checkmarks (✓✗) with [OK]/[FAIL] markers - 59 files updated, 741 lines changed - Improves terminal compatibility and reduces visual noise
This commit is contained in:
@@ -209,12 +209,12 @@ func (m ArchiveBrowserModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
// Validate selection based on mode
|
||||
if m.mode == "restore-cluster" && !selected.Format.IsClusterBackup() {
|
||||
m.message = errorStyle.Render("❌ Please select a cluster backup (.tar.gz)")
|
||||
m.message = errorStyle.Render("[FAIL] Please select a cluster backup (.tar.gz)")
|
||||
return m, nil
|
||||
}
|
||||
|
||||
if m.mode == "restore-single" && selected.Format.IsClusterBackup() {
|
||||
m.message = errorStyle.Render("❌ Please select a single database backup")
|
||||
m.message = errorStyle.Render("[FAIL] Please select a single database backup")
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ func (m ArchiveBrowserModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Show detailed info
|
||||
if len(m.archives) > 0 && m.cursor < len(m.archives) {
|
||||
selected := m.archives[m.cursor]
|
||||
m.message = fmt.Sprintf("📦 %s | Format: %s | Size: %s | Modified: %s",
|
||||
m.message = fmt.Sprintf("[PKG] %s | Format: %s | Size: %s | Modified: %s",
|
||||
selected.Name,
|
||||
selected.Format.String(),
|
||||
formatSize(selected.Size),
|
||||
@@ -251,13 +251,13 @@ func (m ArchiveBrowserModel) View() string {
|
||||
var s strings.Builder
|
||||
|
||||
// Header
|
||||
title := "📦 Backup Archives"
|
||||
title := "[PKG] Backup Archives"
|
||||
if m.mode == "restore-single" {
|
||||
title = "📦 Select Archive to Restore (Single Database)"
|
||||
title = "[PKG] Select Archive to Restore (Single Database)"
|
||||
} else if m.mode == "restore-cluster" {
|
||||
title = "📦 Select Archive to Restore (Cluster)"
|
||||
title = "[PKG] Select Archive to Restore (Cluster)"
|
||||
} else if m.mode == "diagnose" {
|
||||
title = "🔍 Select Archive to Diagnose"
|
||||
title = "[SEARCH] Select Archive to Diagnose"
|
||||
}
|
||||
|
||||
s.WriteString(titleStyle.Render(title))
|
||||
@@ -269,7 +269,7 @@ func (m ArchiveBrowserModel) View() string {
|
||||
}
|
||||
|
||||
if m.err != nil {
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("❌ Error: %v", m.err)))
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("[FAIL] Error: %v", m.err)))
|
||||
s.WriteString("\n\n")
|
||||
s.WriteString(infoStyle.Render("Press Esc to go back"))
|
||||
return s.String()
|
||||
@@ -293,7 +293,7 @@ func (m ArchiveBrowserModel) View() string {
|
||||
s.WriteString(archiveHeaderStyle.Render(fmt.Sprintf("%-40s %-25s %-12s %-20s",
|
||||
"FILENAME", "FORMAT", "SIZE", "MODIFIED")))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(strings.Repeat("─", 100))
|
||||
s.WriteString(strings.Repeat("-", 100))
|
||||
s.WriteString("\n")
|
||||
|
||||
// Show archives (limit to visible area)
|
||||
@@ -317,13 +317,13 @@ func (m ArchiveBrowserModel) View() string {
|
||||
}
|
||||
|
||||
// Color code based on validity and age
|
||||
statusIcon := "✓"
|
||||
statusIcon := "[+]"
|
||||
if !archive.Valid {
|
||||
statusIcon = "✗"
|
||||
statusIcon = "[-]"
|
||||
style = archiveInvalidStyle
|
||||
} else if time.Since(archive.Modified) > 30*24*time.Hour {
|
||||
style = archiveOldStyle
|
||||
statusIcon = "⚠"
|
||||
statusIcon = "[WARN]"
|
||||
}
|
||||
|
||||
filename := truncate(archive.Name, 38)
|
||||
@@ -351,7 +351,7 @@ func (m ArchiveBrowserModel) View() string {
|
||||
s.WriteString(infoStyle.Render(fmt.Sprintf("Total: %d archive(s) | Selected: %d/%d",
|
||||
len(m.archives), m.cursor+1, len(m.archives))))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(infoStyle.Render("⌨️ ↑/↓: Navigate | Enter: Select | d: Diagnose | f: Filter | i: Info | Esc: Back"))
|
||||
s.WriteString(infoStyle.Render("[KEY] ↑/↓: Navigate | Enter: Select | d: Diagnose | f: Filter | i: Info | Esc: Back"))
|
||||
|
||||
return s.String()
|
||||
}
|
||||
|
||||
@@ -136,11 +136,11 @@ func executeBackupWithTUIProgress(parentCtx context.Context, cfg *config.Config,
|
||||
var result string
|
||||
switch backupType {
|
||||
case "single":
|
||||
result = fmt.Sprintf("✓ Single database backup of '%s' completed successfully in %v", dbName, elapsed)
|
||||
result = fmt.Sprintf("[+] Single database backup of '%s' completed successfully in %v", dbName, elapsed)
|
||||
case "sample":
|
||||
result = fmt.Sprintf("✓ Sample backup of '%s' (ratio: %d) completed successfully in %v", dbName, ratio, elapsed)
|
||||
result = fmt.Sprintf("[+] Sample backup of '%s' (ratio: %d) completed successfully in %v", dbName, ratio, elapsed)
|
||||
case "cluster":
|
||||
result = fmt.Sprintf("✓ Cluster backup completed successfully in %v", elapsed)
|
||||
result = fmt.Sprintf("[+] Cluster backup completed successfully in %v", elapsed)
|
||||
}
|
||||
|
||||
return backupCompleteMsg{
|
||||
@@ -200,9 +200,9 @@ func (m BackupExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.err = msg.err
|
||||
m.result = msg.result
|
||||
if m.err == nil {
|
||||
m.status = "✅ Backup completed successfully!"
|
||||
m.status = "[OK] Backup completed successfully!"
|
||||
} else {
|
||||
m.status = fmt.Sprintf("❌ Backup failed: %v", m.err)
|
||||
m.status = fmt.Sprintf("[FAIL] Backup failed: %v", m.err)
|
||||
}
|
||||
// Auto-forward in debug/auto-confirm mode
|
||||
if m.config.TUIAutoConfirm {
|
||||
@@ -216,7 +216,7 @@ func (m BackupExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if !m.done && !m.cancelling {
|
||||
// User requested cancellation - cancel the context
|
||||
m.cancelling = true
|
||||
m.status = "⏹️ Cancelling backup... (please wait)"
|
||||
m.status = "[STOP] Cancelling backup... (please wait)"
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
}
|
||||
@@ -240,7 +240,7 @@ func (m BackupExecutionModel) View() string {
|
||||
|
||||
// Clear screen with newlines and render header
|
||||
s.WriteString("\n\n")
|
||||
header := titleStyle.Render("🔄 Backup Execution")
|
||||
header := titleStyle.Render("[EXEC] Backup Execution")
|
||||
s.WriteString(header)
|
||||
s.WriteString("\n\n")
|
||||
|
||||
@@ -261,13 +261,13 @@ func (m BackupExecutionModel) View() string {
|
||||
s.WriteString(fmt.Sprintf(" %s %s\n", spinnerFrames[m.spinnerFrame], m.status))
|
||||
} else {
|
||||
s.WriteString(fmt.Sprintf(" %s %s\n", spinnerFrames[m.spinnerFrame], m.status))
|
||||
s.WriteString("\n ⌨️ Press Ctrl+C or ESC to cancel\n")
|
||||
s.WriteString("\n [KEY] Press Ctrl+C or ESC to cancel\n")
|
||||
}
|
||||
} else {
|
||||
s.WriteString(fmt.Sprintf(" %s\n\n", m.status))
|
||||
|
||||
if m.err != nil {
|
||||
s.WriteString(fmt.Sprintf(" ❌ Error: %v\n", m.err))
|
||||
s.WriteString(fmt.Sprintf(" [FAIL] Error: %v\n", m.err))
|
||||
} else if m.result != "" {
|
||||
// Parse and display result cleanly
|
||||
lines := strings.Split(m.result, "\n")
|
||||
@@ -278,7 +278,7 @@ func (m BackupExecutionModel) View() string {
|
||||
}
|
||||
}
|
||||
}
|
||||
s.WriteString("\n ⌨️ Press Enter or ESC to return to menu\n")
|
||||
s.WriteString("\n [KEY] Press Enter or ESC to return to menu\n")
|
||||
}
|
||||
|
||||
return s.String()
|
||||
|
||||
@@ -86,7 +86,7 @@ func (m BackupManagerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Verify archive
|
||||
if len(m.archives) > 0 && m.cursor < len(m.archives) {
|
||||
selected := m.archives[m.cursor]
|
||||
m.message = fmt.Sprintf("🔍 Verifying %s...", selected.Name)
|
||||
m.message = fmt.Sprintf("[SEARCH] Verifying %s...", selected.Name)
|
||||
// In real implementation, would run verification
|
||||
}
|
||||
|
||||
@@ -96,16 +96,16 @@ func (m BackupManagerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
selected := m.archives[m.cursor]
|
||||
archivePath := selected.Path
|
||||
confirm := NewConfirmationModelWithAction(m.config, m.logger, m,
|
||||
"🗑️ Delete Archive",
|
||||
"[DELETE] Delete Archive",
|
||||
fmt.Sprintf("Delete archive '%s'? This cannot be undone.", selected.Name),
|
||||
func() (tea.Model, tea.Cmd) {
|
||||
// Delete the archive
|
||||
err := deleteArchive(archivePath)
|
||||
if err != nil {
|
||||
m.err = fmt.Errorf("failed to delete archive: %v", err)
|
||||
m.message = fmt.Sprintf("❌ Failed to delete: %v", err)
|
||||
m.message = fmt.Sprintf("[FAIL] Failed to delete: %v", err)
|
||||
} else {
|
||||
m.message = fmt.Sprintf("✅ Deleted: %s", selected.Name)
|
||||
m.message = fmt.Sprintf("[OK] Deleted: %s", selected.Name)
|
||||
}
|
||||
// Refresh the archive list
|
||||
m.loading = true
|
||||
@@ -118,7 +118,7 @@ func (m BackupManagerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Show info
|
||||
if len(m.archives) > 0 && m.cursor < len(m.archives) {
|
||||
selected := m.archives[m.cursor]
|
||||
m.message = fmt.Sprintf("📦 %s | %s | %s | Modified: %s",
|
||||
m.message = fmt.Sprintf("[PKG] %s | %s | %s | Modified: %s",
|
||||
selected.Name,
|
||||
selected.Format.String(),
|
||||
formatSize(selected.Size),
|
||||
@@ -152,7 +152,7 @@ func (m BackupManagerModel) View() string {
|
||||
var s strings.Builder
|
||||
|
||||
// Title
|
||||
s.WriteString(titleStyle.Render("🗄️ Backup Archive Manager"))
|
||||
s.WriteString(titleStyle.Render("[DB] Backup Archive Manager"))
|
||||
s.WriteString("\n\n")
|
||||
|
||||
if m.loading {
|
||||
@@ -161,7 +161,7 @@ func (m BackupManagerModel) View() string {
|
||||
}
|
||||
|
||||
if m.err != nil {
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("❌ Error: %v", m.err)))
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("[FAIL] Error: %v", m.err)))
|
||||
s.WriteString("\n\n")
|
||||
s.WriteString(infoStyle.Render("Press Esc to go back"))
|
||||
return s.String()
|
||||
@@ -184,7 +184,7 @@ func (m BackupManagerModel) View() string {
|
||||
s.WriteString(archiveHeaderStyle.Render(fmt.Sprintf("%-35s %-25s %-12s %-20s",
|
||||
"FILENAME", "FORMAT", "SIZE", "MODIFIED")))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(strings.Repeat("─", 95))
|
||||
s.WriteString(strings.Repeat("-", 95))
|
||||
s.WriteString("\n")
|
||||
|
||||
// Show archives (limit to visible area)
|
||||
@@ -208,12 +208,12 @@ func (m BackupManagerModel) View() string {
|
||||
}
|
||||
|
||||
// Status icon
|
||||
statusIcon := "✓"
|
||||
statusIcon := "[+]"
|
||||
if !archive.Valid {
|
||||
statusIcon = "✗"
|
||||
statusIcon = "[-]"
|
||||
style = archiveInvalidStyle
|
||||
} else if time.Since(archive.Modified) > 30*24*time.Hour {
|
||||
statusIcon = "⚠"
|
||||
statusIcon = "[WARN]"
|
||||
}
|
||||
|
||||
filename := truncate(archive.Name, 33)
|
||||
@@ -240,7 +240,7 @@ func (m BackupManagerModel) View() string {
|
||||
|
||||
s.WriteString(infoStyle.Render(fmt.Sprintf("Selected: %d/%d", m.cursor+1, len(m.archives))))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(infoStyle.Render("⌨️ ↑/↓: Navigate | r: Restore | v: Verify | d: Delete | i: Info | R: Refresh | Esc: Back"))
|
||||
s.WriteString(infoStyle.Render("[KEY] ↑/↓: Navigate | r: Restore | v: Verify | d: Delete | i: Info | R: Refresh | Esc: Back"))
|
||||
|
||||
return s.String()
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func (m ConfirmationModel) View() string {
|
||||
s.WriteString(" ")
|
||||
}
|
||||
|
||||
s.WriteString("\n\n⌨️ ←/→: Select • Enter/y: Confirm • n/ESC: Cancel\n")
|
||||
s.WriteString("\n\n[KEYS] <-/->: Select | Enter/y: Confirm | n/ESC: Cancel\n")
|
||||
|
||||
return s.String()
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ func (m DatabaseSelectorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return executor, executor.Init()
|
||||
}
|
||||
inputModel := NewInputModel(m.config, m.logger, m,
|
||||
"📊 Sample Ratio",
|
||||
"[STATS] Sample Ratio",
|
||||
"Enter sample ratio (1-100):",
|
||||
"10",
|
||||
ValidateInt(1, 100))
|
||||
@@ -152,7 +152,7 @@ func (m DatabaseSelectorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// If sample backup, ask for ratio first
|
||||
if m.backupType == "sample" {
|
||||
inputModel := NewInputModel(m.config, m.logger, m,
|
||||
"📊 Sample Ratio",
|
||||
"[STATS] Sample Ratio",
|
||||
"Enter sample ratio (1-100):",
|
||||
"10",
|
||||
ValidateInt(1, 100))
|
||||
@@ -176,12 +176,12 @@ func (m DatabaseSelectorModel) View() string {
|
||||
s.WriteString(fmt.Sprintf("\n%s\n\n", header))
|
||||
|
||||
if m.loading {
|
||||
s.WriteString("⏳ Loading databases...\n")
|
||||
s.WriteString("[WAIT] Loading databases...\n")
|
||||
return s.String()
|
||||
}
|
||||
|
||||
if m.err != nil {
|
||||
s.WriteString(fmt.Sprintf("❌ Error: %v\n", m.err))
|
||||
s.WriteString(fmt.Sprintf("[FAIL] Error: %v\n", m.err))
|
||||
s.WriteString("\nPress ESC to go back\n")
|
||||
return s.String()
|
||||
}
|
||||
@@ -203,7 +203,7 @@ func (m DatabaseSelectorModel) View() string {
|
||||
s.WriteString(fmt.Sprintf("\n%s\n", m.message))
|
||||
}
|
||||
|
||||
s.WriteString("\n⌨️ ↑/↓: Navigate • Enter: Select • ESC: Back • q: Quit\n")
|
||||
s.WriteString("\n[KEYS] Up/Down: Navigate | Enter: Select | ESC: Back | q: Quit\n")
|
||||
|
||||
return s.String()
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ func (m DiagnoseViewModel) View() string {
|
||||
var s strings.Builder
|
||||
|
||||
// Header
|
||||
s.WriteString(titleStyle.Render("🔍 Backup Diagnosis"))
|
||||
s.WriteString(titleStyle.Render("[SEARCH] Backup Diagnosis"))
|
||||
s.WriteString("\n\n")
|
||||
|
||||
// Archive info
|
||||
@@ -175,14 +175,14 @@ func (m DiagnoseViewModel) View() string {
|
||||
s.WriteString("\n\n")
|
||||
|
||||
if m.running {
|
||||
s.WriteString(infoStyle.Render("⏳ " + m.progress))
|
||||
s.WriteString(infoStyle.Render("[WAIT] " + m.progress))
|
||||
s.WriteString("\n\n")
|
||||
s.WriteString(diagnoseInfoStyle.Render("This may take a while for large archives..."))
|
||||
return s.String()
|
||||
}
|
||||
|
||||
if m.err != nil {
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("❌ Diagnosis failed: %v", m.err)))
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("[FAIL] Diagnosis failed: %v", m.err)))
|
||||
s.WriteString("\n\n")
|
||||
s.WriteString(infoStyle.Render("Press Enter or Esc to go back"))
|
||||
return s.String()
|
||||
@@ -205,72 +205,72 @@ func (m DiagnoseViewModel) renderSingleResult(result *restore.DiagnoseResult) st
|
||||
var s strings.Builder
|
||||
|
||||
// Status
|
||||
s.WriteString(strings.Repeat("─", 60))
|
||||
s.WriteString(strings.Repeat("-", 60))
|
||||
s.WriteString("\n")
|
||||
|
||||
if result.IsValid {
|
||||
s.WriteString(diagnosePassStyle.Render("✅ STATUS: VALID"))
|
||||
s.WriteString(diagnosePassStyle.Render("[OK] STATUS: VALID"))
|
||||
} else {
|
||||
s.WriteString(diagnoseFailStyle.Render("❌ STATUS: INVALID"))
|
||||
s.WriteString(diagnoseFailStyle.Render("[FAIL] STATUS: INVALID"))
|
||||
}
|
||||
s.WriteString("\n")
|
||||
|
||||
if result.IsTruncated {
|
||||
s.WriteString(diagnoseFailStyle.Render("⚠️ TRUNCATED: File appears incomplete"))
|
||||
s.WriteString(diagnoseFailStyle.Render("[WARN] TRUNCATED: File appears incomplete"))
|
||||
s.WriteString("\n")
|
||||
}
|
||||
|
||||
if result.IsCorrupted {
|
||||
s.WriteString(diagnoseFailStyle.Render("⚠️ CORRUPTED: File structure is damaged"))
|
||||
s.WriteString(diagnoseFailStyle.Render("[WARN] CORRUPTED: File structure is damaged"))
|
||||
s.WriteString("\n")
|
||||
}
|
||||
|
||||
s.WriteString(strings.Repeat("─", 60))
|
||||
s.WriteString(strings.Repeat("-", 60))
|
||||
s.WriteString("\n\n")
|
||||
|
||||
// Details
|
||||
if result.Details != nil {
|
||||
s.WriteString(diagnoseHeaderStyle.Render("📊 DETAILS:"))
|
||||
s.WriteString(diagnoseHeaderStyle.Render("[STATS] DETAILS:"))
|
||||
s.WriteString("\n")
|
||||
|
||||
if result.Details.HasPGDMPSignature {
|
||||
s.WriteString(diagnosePassStyle.Render(" ✓ "))
|
||||
s.WriteString(diagnosePassStyle.Render(" [+] "))
|
||||
s.WriteString("Has PGDMP signature (custom format)\n")
|
||||
}
|
||||
|
||||
if result.Details.HasSQLHeader {
|
||||
s.WriteString(diagnosePassStyle.Render(" ✓ "))
|
||||
s.WriteString(diagnosePassStyle.Render(" [+] "))
|
||||
s.WriteString("Has PostgreSQL SQL header\n")
|
||||
}
|
||||
|
||||
if result.Details.GzipValid {
|
||||
s.WriteString(diagnosePassStyle.Render(" ✓ "))
|
||||
s.WriteString(diagnosePassStyle.Render(" [+] "))
|
||||
s.WriteString("Gzip compression valid\n")
|
||||
}
|
||||
|
||||
if result.Details.PgRestoreListable {
|
||||
s.WriteString(diagnosePassStyle.Render(" ✓ "))
|
||||
s.WriteString(diagnosePassStyle.Render(" [+] "))
|
||||
s.WriteString(fmt.Sprintf("pg_restore can list contents (%d tables)\n", result.Details.TableCount))
|
||||
}
|
||||
|
||||
if result.Details.CopyBlockCount > 0 {
|
||||
s.WriteString(diagnoseInfoStyle.Render(" • "))
|
||||
s.WriteString(diagnoseInfoStyle.Render(" - "))
|
||||
s.WriteString(fmt.Sprintf("Contains %d COPY blocks\n", result.Details.CopyBlockCount))
|
||||
}
|
||||
|
||||
if result.Details.UnterminatedCopy {
|
||||
s.WriteString(diagnoseFailStyle.Render(" ✗ "))
|
||||
s.WriteString(diagnoseFailStyle.Render(" [-] "))
|
||||
s.WriteString(fmt.Sprintf("Unterminated COPY block: %s (line %d)\n",
|
||||
result.Details.LastCopyTable, result.Details.LastCopyLineNumber))
|
||||
}
|
||||
|
||||
if result.Details.ProperlyTerminated {
|
||||
s.WriteString(diagnosePassStyle.Render(" ✓ "))
|
||||
s.WriteString(diagnosePassStyle.Render(" [+] "))
|
||||
s.WriteString("All COPY blocks properly terminated\n")
|
||||
}
|
||||
|
||||
if result.Details.ExpandedSize > 0 {
|
||||
s.WriteString(diagnoseInfoStyle.Render(" • "))
|
||||
s.WriteString(diagnoseInfoStyle.Render(" - "))
|
||||
s.WriteString(fmt.Sprintf("Expanded size: %s (ratio: %.1fx)\n",
|
||||
formatSize(result.Details.ExpandedSize), result.Details.CompressionRatio))
|
||||
}
|
||||
@@ -279,14 +279,14 @@ func (m DiagnoseViewModel) renderSingleResult(result *restore.DiagnoseResult) st
|
||||
// Errors
|
||||
if len(result.Errors) > 0 {
|
||||
s.WriteString("\n")
|
||||
s.WriteString(diagnoseFailStyle.Render("❌ ERRORS:"))
|
||||
s.WriteString(diagnoseFailStyle.Render("[FAIL] ERRORS:"))
|
||||
s.WriteString("\n")
|
||||
for i, e := range result.Errors {
|
||||
if i >= 5 {
|
||||
s.WriteString(diagnoseInfoStyle.Render(fmt.Sprintf(" ... and %d more\n", len(result.Errors)-5)))
|
||||
break
|
||||
}
|
||||
s.WriteString(diagnoseFailStyle.Render(" • "))
|
||||
s.WriteString(diagnoseFailStyle.Render(" - "))
|
||||
s.WriteString(truncate(e, 70))
|
||||
s.WriteString("\n")
|
||||
}
|
||||
@@ -295,14 +295,14 @@ func (m DiagnoseViewModel) renderSingleResult(result *restore.DiagnoseResult) st
|
||||
// Warnings
|
||||
if len(result.Warnings) > 0 {
|
||||
s.WriteString("\n")
|
||||
s.WriteString(diagnoseWarnStyle.Render("⚠️ WARNINGS:"))
|
||||
s.WriteString(diagnoseWarnStyle.Render("[WARN] WARNINGS:"))
|
||||
s.WriteString("\n")
|
||||
for i, w := range result.Warnings {
|
||||
if i >= 3 {
|
||||
s.WriteString(diagnoseInfoStyle.Render(fmt.Sprintf(" ... and %d more\n", len(result.Warnings)-3)))
|
||||
break
|
||||
}
|
||||
s.WriteString(diagnoseWarnStyle.Render(" • "))
|
||||
s.WriteString(diagnoseWarnStyle.Render(" - "))
|
||||
s.WriteString(truncate(w, 70))
|
||||
s.WriteString("\n")
|
||||
}
|
||||
@@ -311,7 +311,7 @@ func (m DiagnoseViewModel) renderSingleResult(result *restore.DiagnoseResult) st
|
||||
// Recommendations
|
||||
if !result.IsValid {
|
||||
s.WriteString("\n")
|
||||
s.WriteString(diagnoseHeaderStyle.Render("💡 RECOMMENDATIONS:"))
|
||||
s.WriteString(diagnoseHeaderStyle.Render("[HINT] RECOMMENDATIONS:"))
|
||||
s.WriteString("\n")
|
||||
if result.IsTruncated {
|
||||
s.WriteString(" 1. Re-run the backup process for this database\n")
|
||||
@@ -341,17 +341,17 @@ func (m DiagnoseViewModel) renderClusterResults() string {
|
||||
}
|
||||
}
|
||||
|
||||
s.WriteString(strings.Repeat("─", 60))
|
||||
s.WriteString(strings.Repeat("-", 60))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(diagnoseHeaderStyle.Render(fmt.Sprintf("📊 CLUSTER SUMMARY: %d databases\n", len(m.results))))
|
||||
s.WriteString(strings.Repeat("─", 60))
|
||||
s.WriteString(diagnoseHeaderStyle.Render(fmt.Sprintf("[STATS] CLUSTER SUMMARY: %d databases\n", len(m.results))))
|
||||
s.WriteString(strings.Repeat("-", 60))
|
||||
s.WriteString("\n\n")
|
||||
|
||||
if invalidCount == 0 {
|
||||
s.WriteString(diagnosePassStyle.Render("✅ All dumps are valid"))
|
||||
s.WriteString(diagnosePassStyle.Render("[OK] All dumps are valid"))
|
||||
s.WriteString("\n\n")
|
||||
} else {
|
||||
s.WriteString(diagnoseFailStyle.Render(fmt.Sprintf("❌ %d/%d dumps have issues", invalidCount, len(m.results))))
|
||||
s.WriteString(diagnoseFailStyle.Render(fmt.Sprintf("[FAIL] %d/%d dumps have issues", invalidCount, len(m.results))))
|
||||
s.WriteString("\n\n")
|
||||
}
|
||||
|
||||
@@ -378,13 +378,13 @@ func (m DiagnoseViewModel) renderClusterResults() string {
|
||||
|
||||
var status string
|
||||
if r.IsValid {
|
||||
status = diagnosePassStyle.Render("✓")
|
||||
status = diagnosePassStyle.Render("[+]")
|
||||
} else if r.IsTruncated {
|
||||
status = diagnoseFailStyle.Render("✗ TRUNCATED")
|
||||
status = diagnoseFailStyle.Render("[-] TRUNCATED")
|
||||
} else if r.IsCorrupted {
|
||||
status = diagnoseFailStyle.Render("✗ CORRUPTED")
|
||||
status = diagnoseFailStyle.Render("[-] CORRUPTED")
|
||||
} else {
|
||||
status = diagnoseFailStyle.Render("✗ INVALID")
|
||||
status = diagnoseFailStyle.Render("[-] INVALID")
|
||||
}
|
||||
|
||||
line := fmt.Sprintf("%s %s %-35s %s",
|
||||
@@ -405,7 +405,7 @@ func (m DiagnoseViewModel) renderClusterResults() string {
|
||||
if m.cursor < len(m.results) {
|
||||
selected := m.results[m.cursor]
|
||||
s.WriteString("\n")
|
||||
s.WriteString(strings.Repeat("─", 60))
|
||||
s.WriteString(strings.Repeat("-", 60))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(diagnoseHeaderStyle.Render("Selected: " + selected.FileName))
|
||||
s.WriteString("\n\n")
|
||||
@@ -413,7 +413,7 @@ func (m DiagnoseViewModel) renderClusterResults() string {
|
||||
// Show condensed details for selected
|
||||
if selected.Details != nil {
|
||||
if selected.Details.UnterminatedCopy {
|
||||
s.WriteString(diagnoseFailStyle.Render(" ✗ Unterminated COPY: "))
|
||||
s.WriteString(diagnoseFailStyle.Render(" [-] Unterminated COPY: "))
|
||||
s.WriteString(selected.Details.LastCopyTable)
|
||||
s.WriteString(fmt.Sprintf(" (line %d)\n", selected.Details.LastCopyLineNumber))
|
||||
}
|
||||
@@ -429,7 +429,7 @@ func (m DiagnoseViewModel) renderClusterResults() string {
|
||||
if i >= 2 {
|
||||
break
|
||||
}
|
||||
s.WriteString(diagnoseFailStyle.Render(" • "))
|
||||
s.WriteString(diagnoseFailStyle.Render(" - "))
|
||||
s.WriteString(truncate(e, 55))
|
||||
s.WriteString("\n")
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ func (dp *DirectoryPicker) View() string {
|
||||
if dp.allowFiles {
|
||||
pickerType = "File/Directory"
|
||||
}
|
||||
header := fmt.Sprintf("📁 %s Picker - %s", pickerType, dp.currentPath)
|
||||
header := fmt.Sprintf("[DIR] %s Picker - %s", pickerType, dp.currentPath)
|
||||
content.WriteString(dp.styles.Header.Render(header))
|
||||
content.WriteString("\n\n")
|
||||
|
||||
@@ -216,13 +216,13 @@ func (dp *DirectoryPicker) View() string {
|
||||
for i, item := range dp.items {
|
||||
var prefix string
|
||||
if item.Name == ".." {
|
||||
prefix = "⬆️ "
|
||||
prefix = "[UP] "
|
||||
} else if item.Name == "Error reading directory" {
|
||||
prefix = "❌ "
|
||||
prefix = "[X] "
|
||||
} else if item.IsDir {
|
||||
prefix = "📁 "
|
||||
prefix = "[DIR] "
|
||||
} else {
|
||||
prefix = "📄 "
|
||||
prefix = "[FILE] "
|
||||
}
|
||||
|
||||
line := prefix + item.Name
|
||||
@@ -235,9 +235,9 @@ func (dp *DirectoryPicker) View() string {
|
||||
}
|
||||
|
||||
// Help text
|
||||
help := "\n↑/↓: Navigate • Enter: Open/Select File • s: Select Directory • q/Esc: Cancel"
|
||||
help := "\nUp/Down: Navigate | Enter: Open/Select File | s: Select Directory | q/Esc: Cancel"
|
||||
if !dp.allowFiles {
|
||||
help = "\n↑/↓: Navigate • Enter: Open • s: Select Directory • q/Esc: Cancel"
|
||||
help = "\nUp/Down: Navigate | Enter: Open | s: Select Directory | q/Esc: Cancel"
|
||||
}
|
||||
content.WriteString(dp.styles.Help.Render(help))
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ func loadHistory(cfg *config.Config) []HistoryEntry {
|
||||
Type: backupType,
|
||||
Database: database,
|
||||
Timestamp: info.ModTime(),
|
||||
Status: "✅ Completed",
|
||||
Status: "[OK] Completed",
|
||||
Filename: name,
|
||||
})
|
||||
}
|
||||
@@ -191,11 +191,11 @@ func (m HistoryViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (m HistoryViewModel) View() string {
|
||||
var s strings.Builder
|
||||
|
||||
header := titleStyle.Render("📜 Operation History")
|
||||
header := titleStyle.Render("[HISTORY] Operation History")
|
||||
s.WriteString(fmt.Sprintf("\n%s\n\n", header))
|
||||
|
||||
if len(m.history) == 0 {
|
||||
s.WriteString("📭 No backup history found\n\n")
|
||||
s.WriteString("[EMPTY] No backup history found\n\n")
|
||||
} else {
|
||||
maxVisible := 15 // Show max 15 items at once
|
||||
|
||||
@@ -211,7 +211,7 @@ func (m HistoryViewModel) View() string {
|
||||
|
||||
// Show scroll indicators
|
||||
if start > 0 {
|
||||
s.WriteString(" ▲ More entries above...\n")
|
||||
s.WriteString(" [^] More entries above...\n")
|
||||
}
|
||||
|
||||
// Display only visible entries
|
||||
@@ -233,13 +233,13 @@ func (m HistoryViewModel) View() string {
|
||||
|
||||
// Show scroll indicator if more entries below
|
||||
if end < len(m.history) {
|
||||
s.WriteString(fmt.Sprintf(" ▼ %d more entries below...\n", len(m.history)-end))
|
||||
s.WriteString(fmt.Sprintf(" [v] %d more entries below...\n", len(m.history)-end))
|
||||
}
|
||||
|
||||
s.WriteString("\n")
|
||||
}
|
||||
|
||||
s.WriteString("⌨️ ↑/↓: Navigate • PgUp/PgDn: Jump • Home/End: First/Last • ESC: Back • q: Quit\n")
|
||||
s.WriteString("[KEYS] Up/Down: Navigate - PgUp/PgDn: Jump - Home/End: First/Last - ESC: Back - q: Quit\n")
|
||||
|
||||
return s.String()
|
||||
}
|
||||
|
||||
@@ -137,10 +137,10 @@ func (m InputModel) View() string {
|
||||
s.WriteString("\n\n")
|
||||
|
||||
if m.err != nil {
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("❌ Error: %v\n\n", m.err)))
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("[FAIL] Error: %v\n\n", m.err)))
|
||||
}
|
||||
|
||||
s.WriteString("⌨️ Type value • Enter: Confirm • ESC: Cancel\n")
|
||||
s.WriteString("[KEYS] Type value | Enter: Confirm | ESC: Cancel\n")
|
||||
|
||||
return s.String()
|
||||
}
|
||||
|
||||
@@ -89,12 +89,12 @@ func NewMenuModel(cfg *config.Config, log logger.Logger) *MenuModel {
|
||||
"Single Database Backup",
|
||||
"Sample Database Backup (with ratio)",
|
||||
"Cluster Backup (all databases)",
|
||||
"────────────────────────────────",
|
||||
"--------------------------------",
|
||||
"Restore Single Database",
|
||||
"Restore Cluster Backup",
|
||||
"Diagnose Backup File",
|
||||
"List & Manage Backups",
|
||||
"────────────────────────────────",
|
||||
"--------------------------------",
|
||||
"View Active Operations",
|
||||
"Show Operation History",
|
||||
"Database Status & Health Check",
|
||||
@@ -177,7 +177,7 @@ func (m *MenuModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case 12: // Settings
|
||||
return m.handleSettings()
|
||||
case 13: // Clear History
|
||||
m.message = "🗑️ History cleared"
|
||||
m.message = "[DEL] History cleared"
|
||||
case 14: // Quit
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
@@ -262,7 +262,7 @@ func (m *MenuModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case 12: // Settings
|
||||
return m.handleSettings()
|
||||
case 13: // Clear History
|
||||
m.message = "🗑️ History cleared"
|
||||
m.message = "[DEL] History cleared"
|
||||
case 14: // Quit
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
@@ -285,7 +285,7 @@ func (m *MenuModel) View() string {
|
||||
var s string
|
||||
|
||||
// Header
|
||||
header := titleStyle.Render("🗄️ Database Backup Tool - Interactive Menu")
|
||||
header := titleStyle.Render("[DB] Database Backup Tool - Interactive Menu")
|
||||
s += fmt.Sprintf("\n%s\n\n", header)
|
||||
|
||||
if len(m.dbTypes) > 0 {
|
||||
@@ -299,7 +299,7 @@ func (m *MenuModel) View() string {
|
||||
}
|
||||
selector := fmt.Sprintf("Target Engine: %s", strings.Join(options, menuStyle.Render(" | ")))
|
||||
s += dbSelectorLabelStyle.Render(selector) + "\n"
|
||||
hint := infoStyle.Render("Switch with ←/→ or t • Cluster backup requires PostgreSQL")
|
||||
hint := infoStyle.Render("Switch with <-/-> or t | Cluster backup requires PostgreSQL")
|
||||
s += hint + "\n"
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ func (m *MenuModel) View() string {
|
||||
}
|
||||
|
||||
// Footer
|
||||
footer := infoStyle.Render("\n⌨️ Press ↑/↓ to navigate • Enter to select • q to quit")
|
||||
footer := infoStyle.Render("\n[KEYS] Press Up/Down to navigate | Enter to select | q to quit")
|
||||
s += footer
|
||||
|
||||
return s
|
||||
@@ -334,20 +334,20 @@ func (m *MenuModel) View() string {
|
||||
|
||||
// handleSingleBackup opens database selector for single backup
|
||||
func (m *MenuModel) handleSingleBackup() (tea.Model, tea.Cmd) {
|
||||
selector := NewDatabaseSelector(m.config, m.logger, m, m.ctx, "🗄️ Single Database Backup", "single")
|
||||
selector := NewDatabaseSelector(m.config, m.logger, m, m.ctx, "[DB] Single Database Backup", "single")
|
||||
return selector, selector.Init()
|
||||
}
|
||||
|
||||
// handleSampleBackup opens database selector for sample backup
|
||||
func (m *MenuModel) handleSampleBackup() (tea.Model, tea.Cmd) {
|
||||
selector := NewDatabaseSelector(m.config, m.logger, m, m.ctx, "📊 Sample Database Backup", "sample")
|
||||
selector := NewDatabaseSelector(m.config, m.logger, m, m.ctx, "[STATS] Sample Database Backup", "sample")
|
||||
return selector, selector.Init()
|
||||
}
|
||||
|
||||
// handleClusterBackup shows confirmation and executes cluster backup
|
||||
func (m *MenuModel) handleClusterBackup() (tea.Model, tea.Cmd) {
|
||||
if !m.config.IsPostgreSQL() {
|
||||
m.message = errorStyle.Render("❌ Cluster backup is available only for PostgreSQL targets")
|
||||
m.message = errorStyle.Render("[FAIL] Cluster backup is available only for PostgreSQL targets")
|
||||
return m, nil
|
||||
}
|
||||
// Skip confirmation in auto-confirm mode
|
||||
@@ -356,7 +356,7 @@ func (m *MenuModel) handleClusterBackup() (tea.Model, tea.Cmd) {
|
||||
return executor, executor.Init()
|
||||
}
|
||||
confirm := NewConfirmationModelWithAction(m.config, m.logger, m,
|
||||
"🗄️ Cluster Backup",
|
||||
"[DB] Cluster Backup",
|
||||
"This will backup ALL databases in the cluster. Continue?",
|
||||
func() (tea.Model, tea.Cmd) {
|
||||
executor := NewBackupExecution(m.config, m.logger, m, m.ctx, "cluster", "", 0)
|
||||
@@ -399,7 +399,7 @@ func (m *MenuModel) handleRestoreSingle() (tea.Model, tea.Cmd) {
|
||||
// handleRestoreCluster opens archive browser for cluster restore
|
||||
func (m *MenuModel) handleRestoreCluster() (tea.Model, tea.Cmd) {
|
||||
if !m.config.IsPostgreSQL() {
|
||||
m.message = errorStyle.Render("❌ Cluster restore is available only for PostgreSQL")
|
||||
m.message = errorStyle.Render("[FAIL] Cluster restore is available only for PostgreSQL")
|
||||
return m, nil
|
||||
}
|
||||
browser := NewArchiveBrowser(m.config, m.logger, m, m.ctx, "restore-cluster")
|
||||
@@ -428,7 +428,7 @@ func (m *MenuModel) applyDatabaseSelection() {
|
||||
|
||||
selection := m.dbTypes[m.dbTypeCursor]
|
||||
if err := m.config.SetDatabaseType(selection.value); err != nil {
|
||||
m.message = errorStyle.Render(fmt.Sprintf("❌ %v", err))
|
||||
m.message = errorStyle.Render(fmt.Sprintf("[FAIL] %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ func (m *MenuModel) applyDatabaseSelection() {
|
||||
m.config.Port = m.config.GetDefaultPort()
|
||||
}
|
||||
|
||||
m.message = successStyle.Render(fmt.Sprintf("🔀 Target database set to %s", m.config.DisplayDatabaseType()))
|
||||
m.message = successStyle.Render(fmt.Sprintf("[SWITCH] Target database set to %s", m.config.DisplayDatabaseType()))
|
||||
if m.logger != nil {
|
||||
m.logger.Info("updated target database type", "type", m.config.DatabaseType, "port", m.config.Port)
|
||||
}
|
||||
|
||||
@@ -49,14 +49,14 @@ func (m OperationsViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (m OperationsViewModel) View() string {
|
||||
var s strings.Builder
|
||||
|
||||
header := titleStyle.Render("📊 Active Operations")
|
||||
header := titleStyle.Render("[STATS] Active Operations")
|
||||
s.WriteString(fmt.Sprintf("\n%s\n\n", header))
|
||||
|
||||
s.WriteString("Currently running operations:\n\n")
|
||||
s.WriteString(infoStyle.Render("📭 No active operations"))
|
||||
s.WriteString(infoStyle.Render("[NONE] No active operations"))
|
||||
s.WriteString("\n\n")
|
||||
|
||||
s.WriteString("⌨️ Press any key to return to menu\n")
|
||||
s.WriteString("[KEYS] Press any key to return to menu\n")
|
||||
|
||||
return s.String()
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ func (m RestoreExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if !m.done && !m.cancelling {
|
||||
// User requested cancellation - cancel the context
|
||||
m.cancelling = true
|
||||
m.status = "⏹️ Cancelling restore... (please wait)"
|
||||
m.status = "[STOP] Cancelling restore... (please wait)"
|
||||
m.phase = "Cancelling"
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
@@ -297,7 +297,7 @@ func (m RestoreExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case "q":
|
||||
if !m.done && !m.cancelling {
|
||||
m.cancelling = true
|
||||
m.status = "⏹️ Cancelling restore... (please wait)"
|
||||
m.status = "[STOP] Cancelling restore... (please wait)"
|
||||
m.phase = "Cancelling"
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
@@ -321,9 +321,9 @@ func (m RestoreExecutionModel) View() string {
|
||||
s.Grow(512) // Pre-allocate estimated capacity for better performance
|
||||
|
||||
// Title
|
||||
title := "💾 Restoring Database"
|
||||
title := "[RESTORE] Restoring Database"
|
||||
if m.restoreType == "restore-cluster" {
|
||||
title = "💾 Restoring Cluster"
|
||||
title = "[RESTORE] Restoring Cluster"
|
||||
}
|
||||
s.WriteString(titleStyle.Render(title))
|
||||
s.WriteString("\n\n")
|
||||
@@ -338,12 +338,12 @@ func (m RestoreExecutionModel) View() string {
|
||||
if m.done {
|
||||
// Show result
|
||||
if m.err != nil {
|
||||
s.WriteString(errorStyle.Render("❌ Restore Failed"))
|
||||
s.WriteString(errorStyle.Render("[FAIL] Restore Failed"))
|
||||
s.WriteString("\n\n")
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("Error: %v", m.err)))
|
||||
s.WriteString("\n")
|
||||
} else {
|
||||
s.WriteString(successStyle.Render("✅ Restore Completed Successfully"))
|
||||
s.WriteString(successStyle.Render("[OK] Restore Completed Successfully"))
|
||||
s.WriteString("\n\n")
|
||||
s.WriteString(successStyle.Render(m.result))
|
||||
s.WriteString("\n")
|
||||
@@ -351,7 +351,7 @@ func (m RestoreExecutionModel) View() string {
|
||||
|
||||
s.WriteString(fmt.Sprintf("\nElapsed Time: %s\n", formatDuration(m.elapsed)))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(infoStyle.Render("⌨️ Press Enter to continue"))
|
||||
s.WriteString(infoStyle.Render("[KEYS] Press Enter to continue"))
|
||||
} else {
|
||||
// Show progress
|
||||
s.WriteString(fmt.Sprintf("Phase: %s\n", m.phase))
|
||||
@@ -373,7 +373,7 @@ func (m RestoreExecutionModel) View() string {
|
||||
// Elapsed time
|
||||
s.WriteString(fmt.Sprintf("Elapsed: %s\n", formatDuration(m.elapsed)))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(infoStyle.Render("⌨️ Press Ctrl+C to cancel"))
|
||||
s.WriteString(infoStyle.Render("[KEYS] Press Ctrl+C to cancel"))
|
||||
}
|
||||
|
||||
return s.String()
|
||||
|
||||
@@ -264,7 +264,7 @@ func (m RestorePreviewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Toggle cluster cleanup
|
||||
m.cleanClusterFirst = !m.cleanClusterFirst
|
||||
if m.cleanClusterFirst {
|
||||
m.message = checkWarningStyle.Render(fmt.Sprintf("⚠️ Will drop %d existing database(s) before restore", m.existingDBCount))
|
||||
m.message = checkWarningStyle.Render(fmt.Sprintf("[WARN] Will drop %d existing database(s) before restore", m.existingDBCount))
|
||||
} else {
|
||||
m.message = fmt.Sprintf("Clean cluster first: disabled")
|
||||
}
|
||||
@@ -278,7 +278,7 @@ func (m RestorePreviewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Toggle debug log saving
|
||||
m.saveDebugLog = !m.saveDebugLog
|
||||
if m.saveDebugLog {
|
||||
m.message = infoStyle.Render("📋 Debug log: enabled (will save detailed report on failure)")
|
||||
m.message = infoStyle.Render("[DEBUG] Debug log: enabled (will save detailed report on failure)")
|
||||
} else {
|
||||
m.message = "Debug log: disabled"
|
||||
}
|
||||
@@ -288,7 +288,7 @@ func (m RestorePreviewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if m.workDir == "" {
|
||||
// Set to backup directory as default alternative
|
||||
m.workDir = m.config.BackupDir
|
||||
m.message = infoStyle.Render(fmt.Sprintf("📁 Work directory set to: %s", m.workDir))
|
||||
m.message = infoStyle.Render(fmt.Sprintf("[DIR] Work directory set to: %s", m.workDir))
|
||||
} else {
|
||||
// Clear work directory (use system temp)
|
||||
m.workDir = ""
|
||||
@@ -302,7 +302,7 @@ func (m RestorePreviewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
if !m.canProceed {
|
||||
m.message = errorStyle.Render("❌ Cannot proceed - critical safety checks failed")
|
||||
m.message = errorStyle.Render("[FAIL] Cannot proceed - critical safety checks failed")
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -319,15 +319,15 @@ func (m RestorePreviewModel) View() string {
|
||||
var s strings.Builder
|
||||
|
||||
// Title
|
||||
title := "🔍 Restore Preview"
|
||||
title := "Restore Preview"
|
||||
if m.mode == "restore-cluster" {
|
||||
title = "🔍 Cluster Restore Preview"
|
||||
title = "Cluster Restore Preview"
|
||||
}
|
||||
s.WriteString(titleStyle.Render(title))
|
||||
s.WriteString("\n\n")
|
||||
|
||||
// Archive Information
|
||||
s.WriteString(archiveHeaderStyle.Render("📦 Archive Information"))
|
||||
s.WriteString(archiveHeaderStyle.Render("[ARCHIVE] Information"))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(fmt.Sprintf(" File: %s\n", m.archive.Name))
|
||||
s.WriteString(fmt.Sprintf(" Format: %s\n", m.archive.Format.String()))
|
||||
@@ -340,25 +340,25 @@ func (m RestorePreviewModel) View() string {
|
||||
|
||||
// Target Information
|
||||
if m.mode == "restore-single" {
|
||||
s.WriteString(archiveHeaderStyle.Render("🎯 Target Information"))
|
||||
s.WriteString(archiveHeaderStyle.Render("[TARGET] Information"))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(fmt.Sprintf(" Database: %s\n", m.targetDB))
|
||||
s.WriteString(fmt.Sprintf(" Host: %s:%d\n", m.config.Host, m.config.Port))
|
||||
|
||||
cleanIcon := "✗"
|
||||
cleanIcon := "[N]"
|
||||
if m.cleanFirst {
|
||||
cleanIcon = "✓"
|
||||
cleanIcon = "[Y]"
|
||||
}
|
||||
s.WriteString(fmt.Sprintf(" Clean First: %s %v\n", cleanIcon, m.cleanFirst))
|
||||
|
||||
createIcon := "✗"
|
||||
createIcon := "[N]"
|
||||
if m.createIfMissing {
|
||||
createIcon = "✓"
|
||||
createIcon = "[Y]"
|
||||
}
|
||||
s.WriteString(fmt.Sprintf(" Create If Missing: %s %v\n", createIcon, m.createIfMissing))
|
||||
s.WriteString("\n")
|
||||
} else if m.mode == "restore-cluster" {
|
||||
s.WriteString(archiveHeaderStyle.Render("🎯 Cluster Restore Options"))
|
||||
s.WriteString(archiveHeaderStyle.Render("[CLUSTER] Restore Options"))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(fmt.Sprintf(" Host: %s:%d\n", m.config.Host, m.config.Port))
|
||||
|
||||
@@ -376,10 +376,10 @@ func (m RestorePreviewModel) View() string {
|
||||
s.WriteString(fmt.Sprintf(" - %s\n", db))
|
||||
}
|
||||
|
||||
cleanIcon := "✗"
|
||||
cleanIcon := "[N]"
|
||||
cleanStyle := infoStyle
|
||||
if m.cleanClusterFirst {
|
||||
cleanIcon = "✓"
|
||||
cleanIcon = "[Y]"
|
||||
cleanStyle = checkWarningStyle
|
||||
}
|
||||
s.WriteString(cleanStyle.Render(fmt.Sprintf(" Clean All First: %s %v (press 'c' to toggle)\n", cleanIcon, m.cleanClusterFirst)))
|
||||
@@ -390,7 +390,7 @@ func (m RestorePreviewModel) View() string {
|
||||
}
|
||||
|
||||
// Safety Checks
|
||||
s.WriteString(archiveHeaderStyle.Render("🛡️ Safety Checks"))
|
||||
s.WriteString(archiveHeaderStyle.Render("[SAFETY] Checks"))
|
||||
s.WriteString("\n")
|
||||
|
||||
if m.checking {
|
||||
@@ -398,21 +398,21 @@ func (m RestorePreviewModel) View() string {
|
||||
s.WriteString("\n")
|
||||
} else {
|
||||
for _, check := range m.safetyChecks {
|
||||
icon := "○"
|
||||
icon := "[ ]"
|
||||
style := checkPendingStyle
|
||||
|
||||
switch check.Status {
|
||||
case "passed":
|
||||
icon = "✓"
|
||||
icon = "[+]"
|
||||
style = checkPassedStyle
|
||||
case "failed":
|
||||
icon = "✗"
|
||||
icon = "[-]"
|
||||
style = checkFailedStyle
|
||||
case "warning":
|
||||
icon = "⚠"
|
||||
icon = "[!]"
|
||||
style = checkWarningStyle
|
||||
case "checking":
|
||||
icon = "⟳"
|
||||
icon = "[~]"
|
||||
style = checkPendingStyle
|
||||
}
|
||||
|
||||
@@ -428,13 +428,13 @@ func (m RestorePreviewModel) View() string {
|
||||
|
||||
// Warnings
|
||||
if m.cleanFirst {
|
||||
s.WriteString(checkWarningStyle.Render("⚠️ Warning: Clean-first enabled"))
|
||||
s.WriteString(checkWarningStyle.Render("[WARN] Warning: Clean-first enabled"))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(infoStyle.Render(" All existing data in target database will be dropped!"))
|
||||
s.WriteString("\n\n")
|
||||
}
|
||||
if m.cleanClusterFirst && m.existingDBCount > 0 {
|
||||
s.WriteString(checkWarningStyle.Render("🔥 WARNING: Cluster cleanup enabled"))
|
||||
s.WriteString(checkWarningStyle.Render("[DANGER] WARNING: Cluster cleanup enabled"))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(checkWarningStyle.Render(fmt.Sprintf(" %d existing database(s) will be DROPPED before restore!", m.existingDBCount)))
|
||||
s.WriteString("\n")
|
||||
@@ -443,30 +443,30 @@ func (m RestorePreviewModel) View() string {
|
||||
}
|
||||
|
||||
// Advanced Options
|
||||
s.WriteString(archiveHeaderStyle.Render("⚙️ Advanced Options"))
|
||||
s.WriteString(archiveHeaderStyle.Render("[OPTIONS] Advanced"))
|
||||
s.WriteString("\n")
|
||||
|
||||
// Work directory option
|
||||
workDirIcon := "✗"
|
||||
workDirIcon := "[-]"
|
||||
workDirStyle := infoStyle
|
||||
workDirValue := "(system temp)"
|
||||
if m.workDir != "" {
|
||||
workDirIcon = "✓"
|
||||
workDirIcon = "[+]"
|
||||
workDirStyle = checkPassedStyle
|
||||
workDirValue = m.workDir
|
||||
}
|
||||
s.WriteString(workDirStyle.Render(fmt.Sprintf(" %s Work Dir: %s (press 'w' to toggle)", workDirIcon, workDirValue)))
|
||||
s.WriteString("\n")
|
||||
if m.workDir == "" {
|
||||
s.WriteString(infoStyle.Render(" ⚠️ Large archives need more space than /tmp may have"))
|
||||
s.WriteString(infoStyle.Render(" [WARN] Large archives need more space than /tmp may have"))
|
||||
s.WriteString("\n")
|
||||
}
|
||||
|
||||
// Debug log option
|
||||
debugIcon := "✗"
|
||||
debugIcon := "[-]"
|
||||
debugStyle := infoStyle
|
||||
if m.saveDebugLog {
|
||||
debugIcon = "✓"
|
||||
debugIcon = "[+]"
|
||||
debugStyle = checkPassedStyle
|
||||
}
|
||||
s.WriteString(debugStyle.Render(fmt.Sprintf(" %s Debug Log: %v (press 'd' to toggle)", debugIcon, m.saveDebugLog)))
|
||||
@@ -485,25 +485,25 @@ func (m RestorePreviewModel) View() string {
|
||||
|
||||
// Footer
|
||||
if m.checking {
|
||||
s.WriteString(infoStyle.Render("⌨️ Please wait..."))
|
||||
s.WriteString(infoStyle.Render("Please wait..."))
|
||||
} else if m.canProceed {
|
||||
s.WriteString(successStyle.Render("✅ Ready to restore"))
|
||||
s.WriteString(successStyle.Render("[OK] Ready to restore"))
|
||||
s.WriteString("\n")
|
||||
if m.mode == "restore-single" {
|
||||
s.WriteString(infoStyle.Render("⌨️ t: Clean-first | c: Create | w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel"))
|
||||
s.WriteString(infoStyle.Render("t: Clean-first | c: Create | w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel"))
|
||||
} else if m.mode == "restore-cluster" {
|
||||
if m.existingDBCount > 0 {
|
||||
s.WriteString(infoStyle.Render("⌨️ c: Cleanup | w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel"))
|
||||
s.WriteString(infoStyle.Render("c: Cleanup | w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel"))
|
||||
} else {
|
||||
s.WriteString(infoStyle.Render("⌨️ w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel"))
|
||||
s.WriteString(infoStyle.Render("w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel"))
|
||||
}
|
||||
} else {
|
||||
s.WriteString(infoStyle.Render("⌨️ w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel"))
|
||||
s.WriteString(infoStyle.Render("w: WorkDir | d: Debug | Enter: Proceed | Esc: Cancel"))
|
||||
}
|
||||
} else {
|
||||
s.WriteString(errorStyle.Render("❌ Cannot proceed - please fix errors above"))
|
||||
s.WriteString(errorStyle.Render("[FAIL] Cannot proceed - please fix errors above"))
|
||||
s.WriteString("\n")
|
||||
s.WriteString(infoStyle.Render("⌨️ Esc: Go back"))
|
||||
s.WriteString(infoStyle.Render("Esc: Go back"))
|
||||
}
|
||||
|
||||
return s.String()
|
||||
|
||||
@@ -459,9 +459,9 @@ func (m SettingsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if m.cursor < len(m.settings) {
|
||||
setting := m.settings[m.cursor]
|
||||
if err := setting.Update(m.config, selectedPath); err != nil {
|
||||
m.message = "❌ Error: " + err.Error()
|
||||
m.message = "[FAIL] Error: " + err.Error()
|
||||
} else {
|
||||
m.message = "✅ Directory updated: " + selectedPath
|
||||
m.message = "[OK] Directory updated: " + selectedPath
|
||||
}
|
||||
}
|
||||
m.browsingDir = false
|
||||
@@ -500,9 +500,9 @@ func (m SettingsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
currentSetting := m.settings[m.cursor]
|
||||
if currentSetting.Type == "selector" {
|
||||
if err := currentSetting.Update(m.config, ""); err != nil {
|
||||
m.message = errorStyle.Render(fmt.Sprintf("❌ %s", err.Error()))
|
||||
m.message = errorStyle.Render(fmt.Sprintf("[FAIL] %s", err.Error()))
|
||||
} else {
|
||||
m.message = successStyle.Render(fmt.Sprintf("✅ Updated %s", currentSetting.DisplayName))
|
||||
m.message = successStyle.Render(fmt.Sprintf("[OK] Updated %s", currentSetting.DisplayName))
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@@ -515,11 +515,11 @@ func (m SettingsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if m.settings[m.cursor].Type == "path" {
|
||||
return m.openDirectoryBrowser()
|
||||
} else {
|
||||
m.message = "❌ Tab key only works on directory path fields"
|
||||
m.message = "[FAIL] Tab key only works on directory path fields"
|
||||
return m, nil
|
||||
}
|
||||
} else {
|
||||
m.message = "❌ Invalid selection"
|
||||
m.message = "[FAIL] Invalid selection"
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -597,18 +597,18 @@ func (m SettingsModel) saveEditedValue() (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
if setting == nil {
|
||||
m.message = errorStyle.Render("❌ Setting not found")
|
||||
m.message = errorStyle.Render("[FAIL] Setting not found")
|
||||
m.editing = false
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Update the configuration
|
||||
if err := setting.Update(m.config, m.editingValue); err != nil {
|
||||
m.message = errorStyle.Render(fmt.Sprintf("❌ %s", err.Error()))
|
||||
m.message = errorStyle.Render(fmt.Sprintf("[FAIL] %s", err.Error()))
|
||||
return m, nil
|
||||
}
|
||||
|
||||
m.message = successStyle.Render(fmt.Sprintf("✅ Updated %s", setting.DisplayName))
|
||||
m.message = successStyle.Render(fmt.Sprintf("[OK] Updated %s", setting.DisplayName))
|
||||
m.editing = false
|
||||
m.editingField = ""
|
||||
m.editingValue = ""
|
||||
@@ -628,7 +628,7 @@ func (m SettingsModel) resetToDefaults() (tea.Model, tea.Cmd) {
|
||||
newConfig.DatabaseType = m.config.DatabaseType
|
||||
|
||||
*m.config = *newConfig
|
||||
m.message = successStyle.Render("✅ Settings reset to defaults")
|
||||
m.message = successStyle.Render("[OK] Settings reset to defaults")
|
||||
|
||||
return m, nil
|
||||
}
|
||||
@@ -636,19 +636,19 @@ func (m SettingsModel) resetToDefaults() (tea.Model, tea.Cmd) {
|
||||
// saveSettings validates and saves current settings
|
||||
func (m SettingsModel) saveSettings() (tea.Model, tea.Cmd) {
|
||||
if err := m.config.Validate(); err != nil {
|
||||
m.message = errorStyle.Render(fmt.Sprintf("❌ Validation failed: %s", err.Error()))
|
||||
m.message = errorStyle.Render(fmt.Sprintf("[FAIL] Validation failed: %s", err.Error()))
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Optimize CPU settings if auto-detect is enabled
|
||||
if m.config.AutoDetectCores {
|
||||
if err := m.config.OptimizeForCPU(); err != nil {
|
||||
m.message = errorStyle.Render(fmt.Sprintf("❌ CPU optimization failed: %s", err.Error()))
|
||||
m.message = errorStyle.Render(fmt.Sprintf("[FAIL] CPU optimization failed: %s", err.Error()))
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
m.message = successStyle.Render("✅ Settings validated and saved")
|
||||
m.message = successStyle.Render("[OK] Settings validated and saved")
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -671,11 +671,11 @@ func (m SettingsModel) cycleDatabaseType() (tea.Model, tea.Cmd) {
|
||||
|
||||
// Update config
|
||||
if err := m.config.SetDatabaseType(newType); err != nil {
|
||||
m.message = errorStyle.Render(fmt.Sprintf("❌ Failed to set database type: %s", err.Error()))
|
||||
m.message = errorStyle.Render(fmt.Sprintf("[FAIL] Failed to set database type: %s", err.Error()))
|
||||
return m, nil
|
||||
}
|
||||
|
||||
m.message = successStyle.Render(fmt.Sprintf("✅ Database type set to %s", m.config.DisplayDatabaseType()))
|
||||
m.message = successStyle.Render(fmt.Sprintf("[OK] Database type set to %s", m.config.DisplayDatabaseType()))
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -688,7 +688,7 @@ func (m SettingsModel) View() string {
|
||||
var b strings.Builder
|
||||
|
||||
// Header
|
||||
header := titleStyle.Render("⚙️ Configuration Settings")
|
||||
header := titleStyle.Render("[CFG] Configuration Settings")
|
||||
b.WriteString(fmt.Sprintf("\n%s\n\n", header))
|
||||
|
||||
// Settings list
|
||||
@@ -710,7 +710,7 @@ func (m SettingsModel) View() string {
|
||||
}
|
||||
line := fmt.Sprintf("%s %s: %s", cursor, setting.DisplayName, editValue)
|
||||
b.WriteString(selectedStyle.Render(line))
|
||||
b.WriteString(" ✏️")
|
||||
b.WriteString(" [EDIT]")
|
||||
} else {
|
||||
line := fmt.Sprintf("%s %s: %s", cursor, setting.DisplayName, displayValue)
|
||||
b.WriteString(selectedStyle.Render(line))
|
||||
@@ -747,7 +747,7 @@ func (m SettingsModel) View() string {
|
||||
// Current configuration summary
|
||||
if !m.editing {
|
||||
b.WriteString("\n")
|
||||
b.WriteString(infoStyle.Render("📋 Current Configuration:"))
|
||||
b.WriteString(infoStyle.Render("[LOG] Current Configuration:"))
|
||||
b.WriteString("\n")
|
||||
|
||||
summary := []string{
|
||||
@@ -775,16 +775,16 @@ func (m SettingsModel) View() string {
|
||||
// Footer with instructions
|
||||
var footer string
|
||||
if m.editing {
|
||||
footer = infoStyle.Render("\n⌨️ Type new value • Enter to save • Esc to cancel")
|
||||
footer = infoStyle.Render("\n[KEYS] Type new value | Enter to save | Esc to cancel")
|
||||
} else {
|
||||
if m.browsingDir {
|
||||
footer = infoStyle.Render("\n⌨️ ↑/↓ navigate directories • Enter open • Space select • Tab/Esc back to settings")
|
||||
footer = infoStyle.Render("\n[KEYS] Up/Down navigate directories | Enter open | Space select | Tab/Esc back to settings")
|
||||
} else {
|
||||
// Show different help based on current selection
|
||||
if m.cursor >= 0 && m.cursor < len(m.settings) && m.settings[m.cursor].Type == "path" {
|
||||
footer = infoStyle.Render("\n⌨️ ↑/↓ navigate • Enter edit • Tab browse directories • 's' save • 'r' reset • 'q' menu")
|
||||
footer = infoStyle.Render("\n[KEYS] Up/Down navigate | Enter edit | Tab browse directories | 's' save | 'r' reset | 'q' menu")
|
||||
} else {
|
||||
footer = infoStyle.Render("\n⌨️ ↑/↓ navigate • Enter edit • 's' save • 'r' reset • 'q' menu • Tab=dirs on path fields only")
|
||||
footer = infoStyle.Render("\n[KEYS] Up/Down navigate | Enter edit | 's' save | 'r' reset | 'q' menu | Tab=dirs on path fields only")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,25 +160,25 @@ func (m StatusViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
func (m StatusViewModel) View() string {
|
||||
var s strings.Builder
|
||||
|
||||
header := titleStyle.Render("📊 Database Status & Health Check")
|
||||
header := titleStyle.Render("[STATS] Database Status & Health Check")
|
||||
s.WriteString(fmt.Sprintf("\n%s\n\n", header))
|
||||
|
||||
if m.loading {
|
||||
spinner := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
|
||||
spinner := []string{"-", "\\", "|", "/"}
|
||||
frame := int(time.Now().UnixMilli()/100) % len(spinner)
|
||||
s.WriteString(fmt.Sprintf("%s Loading status information...\n", spinner[frame]))
|
||||
return s.String()
|
||||
}
|
||||
|
||||
if m.err != nil {
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("❌ Error: %v\n", m.err)))
|
||||
s.WriteString(errorStyle.Render(fmt.Sprintf("[FAIL] Error: %v\n", m.err)))
|
||||
s.WriteString("\n")
|
||||
} else {
|
||||
s.WriteString("Connection Status:\n")
|
||||
if m.connected {
|
||||
s.WriteString(successStyle.Render(" ✓ Connected\n"))
|
||||
s.WriteString(successStyle.Render(" [+] Connected\n"))
|
||||
} else {
|
||||
s.WriteString(errorStyle.Render(" ✗ Disconnected\n"))
|
||||
s.WriteString(errorStyle.Render(" [-] Disconnected\n"))
|
||||
}
|
||||
s.WriteString("\n")
|
||||
|
||||
@@ -193,9 +193,9 @@ func (m StatusViewModel) View() string {
|
||||
}
|
||||
|
||||
s.WriteString("\n")
|
||||
s.WriteString(successStyle.Render("✓ All systems operational\n"))
|
||||
s.WriteString(successStyle.Render("[+] All systems operational\n"))
|
||||
}
|
||||
|
||||
s.WriteString("\n⌨️ Press any key to return to menu\n")
|
||||
s.WriteString("\n[KEYS] Press any key to return to menu\n")
|
||||
return s.String()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user