fix: improve CLI routes list display
- Convert all icon constants to Unicode escape sequences for portability - Add new container status icons (running, stopped, paused, restarting, exited) - Render status badges with icons instead of text labels - Fix gray/faded alternating table rows (use same color for all rows) - Use container icon prefix for attachment tree display
- commit
- 64cd897aaec2090d0baf5798a7dc1783c0aa916e
- author
- brice · · 2026-01-17 14:17 UTC
- parent
- 9e79967
- signature
- unsigned
4 files · +120 -69
M internal/adapters/in/cli/routes.go +3 -6
| @@ -169,12 +169,9 @@ func runRoutesListRemote(ctx context.Context, client *remote.Client) error { | |||
| 169 | 169 | displayNetwork := truncateNetwork(route.Network, networkColWidth) | |
| 170 | 170 | rows = append(rows, []string{route.Domain, displayImage, displayNetwork, containerBadge, httpStatus}) | |
| 171 | 171 | ||
| 172 | - | for i, attachment := range route.Attachments { | |
| 173 | - | prefix := "|-" | |
| 174 | - | if i == len(route.Attachments)-1 { | |
| 175 | - | prefix = "`-" | |
| 176 | - | } | |
| 177 | - | attachmentName := prefix + " " + attachment.Name | |
| 172 | + | for _, attachment := range route.Attachments { | |
| 173 | + | // Use container icon with indent for attachment tree display | |
| 174 | + | attachmentName := " " + styles.IconContainer + " " + attachment.Name | |
| 178 | 175 | if len(attachmentName) > 25 { | |
| 179 | 176 | attachmentName = attachmentName[:22] + "..." | |
| 180 | 177 | } | |
| @@ -169,12 +169,9 @@ func runRoutesListRemote(ctx context.Context, client *remote.Client) error { | |||||
| 169 | displayNetwork := truncateNetwork(route.Network, networkColWidth) | 169 | displayNetwork := truncateNetwork(route.Network, networkColWidth) | ||
| 170 | rows = append(rows, []string{route.Domain, displayImage, displayNetwork, containerBadge, httpStatus}) | 170 | rows = append(rows, []string{route.Domain, displayImage, displayNetwork, containerBadge, httpStatus}) | ||
| 171 | 171 | ||||
| 172 | - | for i, attachment := range route.Attachments { | 172 | + | for _, attachment := range route.Attachments { |
| 173 | - | prefix := "|-" | 173 | + | // Use container icon with indent for attachment tree display |
| 174 | - | if i == len(route.Attachments)-1 { | 174 | + | attachmentName := " " + styles.IconContainer + " " + attachment.Name |
| 175 | - | prefix = "`-" | |||
| 176 | - | } | |||
| 177 | - | attachmentName := prefix + " " + attachment.Name | |||
| 178 | if len(attachmentName) > 25 { | 175 | if len(attachmentName) > 25 { | ||
| 179 | attachmentName = attachmentName[:22] + "..." | 176 | attachmentName = attachmentName[:22] + "..." | ||
| 180 | } | 177 | } | ||
M internal/adapters/in/cli/ui/components/status.go +41 -10
| @@ -19,6 +19,10 @@ const ( | |||
| 19 | 19 | StatusPending | |
| 20 | 20 | StatusRunning | |
| 21 | 21 | StatusStopped | |
| 22 | + | StatusPaused | |
| 23 | + | StatusRestarting | |
| 24 | + | StatusExited | |
| 25 | + | StatusUnknown | |
| 22 | 26 | ) | |
| 23 | 27 | ||
| 24 | 28 | // StatusConfig holds configuration for status rendering. | |
| @@ -54,13 +58,29 @@ var DefaultStatusConfigs = map[Status]StatusConfig{ | |||
| 54 | 58 | Style: styles.Theme.Muted, | |
| 55 | 59 | }, | |
| 56 | 60 | StatusRunning: { | |
| 57 | - | Icon: styles.IconSuccess, | |
| 61 | + | Icon: styles.IconRunning, | |
| 58 | 62 | Style: styles.Theme.Success, | |
| 59 | 63 | }, | |
| 60 | 64 | StatusStopped: { | |
| 61 | - | Icon: styles.IconError, | |
| 65 | + | Icon: styles.IconStopped, | |
| 66 | + | Style: styles.Theme.Error, | |
| 67 | + | }, | |
| 68 | + | StatusPaused: { | |
| 69 | + | Icon: styles.IconPaused, | |
| 70 | + | Style: styles.Theme.Warning, | |
| 71 | + | }, | |
| 72 | + | StatusRestarting: { | |
| 73 | + | Icon: styles.IconRestarting, | |
| 74 | + | Style: styles.Theme.Warning, | |
| 75 | + | }, | |
| 76 | + | StatusExited: { | |
| 77 | + | Icon: styles.IconExited, | |
| 62 | 78 | Style: styles.Theme.Error, | |
| 63 | 79 | }, | |
| 80 | + | StatusUnknown: { | |
| 81 | + | Icon: styles.IconUnknown, | |
| 82 | + | Style: styles.Theme.Muted, | |
| 83 | + | }, | |
| 64 | 84 | } | |
| 65 | 85 | ||
| 66 | 86 | // RenderStatus renders a status with icon and optional label. | |
| @@ -72,22 +92,25 @@ func RenderStatus(status Status, label string) string { | |||
| 72 | 92 | return config.Style.Render(config.Icon + " " + label) | |
| 73 | 93 | } | |
| 74 | 94 | ||
| 75 | - | // RenderStatusBadge renders a status as a badge with background. | |
| 76 | - | func RenderStatusBadge(status Status, label string) string { | |
| 95 | + | // RenderStatusBadge renders a status as a badge with background and icon. | |
| 96 | + | func RenderStatusBadge(status Status, _ string) string { | |
| 97 | + | config := DefaultStatusConfigs[status] | |
| 77 | 98 | var badgeStyle lipgloss.Style | |
| 78 | 99 | switch status { | |
| 79 | 100 | case StatusSuccess, StatusRunning: | |
| 80 | 101 | badgeStyle = styles.Theme.BadgeSuccess | |
| 81 | - | case StatusError, StatusStopped: | |
| 102 | + | case StatusError, StatusStopped, StatusExited: | |
| 82 | 103 | badgeStyle = styles.Theme.BadgeError | |
| 83 | - | case StatusWarning: | |
| 104 | + | case StatusWarning, StatusPaused, StatusRestarting: | |
| 84 | 105 | badgeStyle = styles.Theme.BadgeWarning | |
| 85 | 106 | case StatusPending: | |
| 86 | 107 | badgeStyle = styles.Theme.BadgePending | |
| 108 | + | case StatusUnknown: | |
| 109 | + | badgeStyle = styles.Theme.Muted | |
| 87 | 110 | default: | |
| 88 | 111 | badgeStyle = styles.Theme.BadgeInfo | |
| 89 | 112 | } | |
| 90 | - | return badgeStyle.Render(label) | |
| 113 | + | return badgeStyle.Render(config.Icon) | |
| 91 | 114 | } | |
| 92 | 115 | ||
| 93 | 116 | // ParseStatus converts a string status to Status type. | |
| @@ -101,14 +124,22 @@ func ParseStatus(s string) Status { | |||
| 101 | 124 | return StatusWarning | |
| 102 | 125 | case "info": | |
| 103 | 126 | return StatusInfo | |
| 104 | - | case "pending", "starting", "waiting": | |
| 127 | + | case "pending", "starting", "waiting", "created": | |
| 105 | 128 | return StatusPending | |
| 106 | 129 | case "running", "up": | |
| 107 | 130 | return StatusRunning | |
| 108 | - | case "stopped", "down", "exited": | |
| 131 | + | case "stopped", "down": | |
| 109 | 132 | return StatusStopped | |
| 133 | + | case "paused": | |
| 134 | + | return StatusPaused | |
| 135 | + | case "restarting": | |
| 136 | + | return StatusRestarting | |
| 137 | + | case "exited", "dead": | |
| 138 | + | return StatusExited | |
| 139 | + | case "unknown": | |
| 140 | + | return StatusUnknown | |
| 110 | 141 | default: | |
| 111 | - | return StatusInfo | |
| 142 | + | return StatusUnknown | |
| 112 | 143 | } | |
| 113 | 144 | } | |
| 114 | 145 | ||
| @@ -19,6 +19,10 @@ const ( | |||||
| 19 | StatusPending | 19 | StatusPending | ||
| 20 | StatusRunning | 20 | StatusRunning | ||
| 21 | StatusStopped | 21 | StatusStopped | ||
| 22 | + | StatusPaused | |||
| 23 | + | StatusRestarting | |||
| 24 | + | StatusExited | |||
| 25 | + | StatusUnknown | |||
| 22 | ) | 26 | ) | ||
| 23 | 27 | ||||
| 24 | // StatusConfig holds configuration for status rendering. | 28 | // StatusConfig holds configuration for status rendering. | ||
| @@ -54,13 +58,29 @@ var DefaultStatusConfigs = map[Status]StatusConfig{ | |||||
| 54 | Style: styles.Theme.Muted, | 58 | Style: styles.Theme.Muted, | ||
| 55 | }, | 59 | }, | ||
| 56 | StatusRunning: { | 60 | StatusRunning: { | ||
| 57 | - | Icon: styles.IconSuccess, | 61 | + | Icon: styles.IconRunning, |
| 58 | Style: styles.Theme.Success, | 62 | Style: styles.Theme.Success, | ||
| 59 | }, | 63 | }, | ||
| 60 | StatusStopped: { | 64 | StatusStopped: { | ||
| 61 | - | Icon: styles.IconError, | 65 | + | Icon: styles.IconStopped, |
| 66 | + | Style: styles.Theme.Error, | |||
| 67 | + | }, | |||
| 68 | + | StatusPaused: { | |||
| 69 | + | Icon: styles.IconPaused, | |||
| 70 | + | Style: styles.Theme.Warning, | |||
| 71 | + | }, | |||
| 72 | + | StatusRestarting: { | |||
| 73 | + | Icon: styles.IconRestarting, | |||
| 74 | + | Style: styles.Theme.Warning, | |||
| 75 | + | }, | |||
| 76 | + | StatusExited: { | |||
| 77 | + | Icon: styles.IconExited, | |||
| 62 | Style: styles.Theme.Error, | 78 | Style: styles.Theme.Error, | ||
| 63 | }, | 79 | }, | ||
| 80 | + | StatusUnknown: { | |||
| 81 | + | Icon: styles.IconUnknown, | |||
| 82 | + | Style: styles.Theme.Muted, | |||
| 83 | + | }, | |||
| 64 | } | 84 | } | ||
| 65 | 85 | ||||
| 66 | // RenderStatus renders a status with icon and optional label. | 86 | // RenderStatus renders a status with icon and optional label. | ||
| @@ -72,22 +92,25 @@ func RenderStatus(status Status, label string) string { | |||||
| 72 | return config.Style.Render(config.Icon + " " + label) | 92 | return config.Style.Render(config.Icon + " " + label) | ||
| 73 | } | 93 | } | ||
| 74 | 94 | ||||
| 75 | - | // RenderStatusBadge renders a status as a badge with background. | 95 | + | // RenderStatusBadge renders a status as a badge with background and icon. |
| 76 | - | func RenderStatusBadge(status Status, label string) string { | 96 | + | func RenderStatusBadge(status Status, _ string) string { |
| 97 | + | config := DefaultStatusConfigs[status] | |||
| 77 | var badgeStyle lipgloss.Style | 98 | var badgeStyle lipgloss.Style | ||
| 78 | switch status { | 99 | switch status { | ||
| 79 | case StatusSuccess, StatusRunning: | 100 | case StatusSuccess, StatusRunning: | ||
| 80 | badgeStyle = styles.Theme.BadgeSuccess | 101 | badgeStyle = styles.Theme.BadgeSuccess | ||
| 81 | - | case StatusError, StatusStopped: | 102 | + | case StatusError, StatusStopped, StatusExited: |
| 82 | badgeStyle = styles.Theme.BadgeError | 103 | badgeStyle = styles.Theme.BadgeError | ||
| 83 | - | case StatusWarning: | 104 | + | case StatusWarning, StatusPaused, StatusRestarting: |
| 84 | badgeStyle = styles.Theme.BadgeWarning | 105 | badgeStyle = styles.Theme.BadgeWarning | ||
| 85 | case StatusPending: | 106 | case StatusPending: | ||
| 86 | badgeStyle = styles.Theme.BadgePending | 107 | badgeStyle = styles.Theme.BadgePending | ||
| 108 | + | case StatusUnknown: | |||
| 109 | + | badgeStyle = styles.Theme.Muted | |||
| 87 | default: | 110 | default: | ||
| 88 | badgeStyle = styles.Theme.BadgeInfo | 111 | badgeStyle = styles.Theme.BadgeInfo | ||
| 89 | } | 112 | } | ||
| 90 | - | return badgeStyle.Render(label) | 113 | + | return badgeStyle.Render(config.Icon) |
| 91 | } | 114 | } | ||
| 92 | 115 | ||||
| 93 | // ParseStatus converts a string status to Status type. | 116 | // ParseStatus converts a string status to Status type. | ||
| @@ -101,14 +124,22 @@ func ParseStatus(s string) Status { | |||||
| 101 | return StatusWarning | 124 | return StatusWarning | ||
| 102 | case "info": | 125 | case "info": | ||
| 103 | return StatusInfo | 126 | return StatusInfo | ||
| 104 | - | case "pending", "starting", "waiting": | 127 | + | case "pending", "starting", "waiting", "created": |
| 105 | return StatusPending | 128 | return StatusPending | ||
| 106 | case "running", "up": | 129 | case "running", "up": | ||
| 107 | return StatusRunning | 130 | return StatusRunning | ||
| 108 | - | case "stopped", "down", "exited": | 131 | + | case "stopped", "down": |
| 109 | return StatusStopped | 132 | return StatusStopped | ||
| 133 | + | case "paused": | |||
| 134 | + | return StatusPaused | |||
| 135 | + | case "restarting": | |||
| 136 | + | return StatusRestarting | |||
| 137 | + | case "exited", "dead": | |||
| 138 | + | return StatusExited | |||
| 139 | + | case "unknown": | |||
| 140 | + | return StatusUnknown | |||
| 110 | default: | 141 | default: | ||
| 111 | - | return StatusInfo | 142 | + | return StatusUnknown |
| 112 | } | 143 | } | ||
| 113 | } | 144 | } | ||
| 114 | 145 | ||||
M internal/adapters/in/cli/ui/components/table.go +1 -1
| @@ -45,7 +45,7 @@ func NewTable(opts ...TableOption) *TableModel { | |||
| 45 | 45 | Foreground(styles.ColorText). | |
| 46 | 46 | Padding(0, 1), | |
| 47 | 47 | evenStyle: lipgloss.NewStyle(). | |
| 48 | - | Foreground(styles.ColorTextMuted). | |
| 48 | + | Foreground(styles.ColorText). | |
| 49 | 49 | Padding(0, 1), | |
| 50 | 50 | } | |
| 51 | 51 | ||
| @@ -45,7 +45,7 @@ func NewTable(opts ...TableOption) *TableModel { | |||||
| 45 | Foreground(styles.ColorText). | 45 | Foreground(styles.ColorText). | ||
| 46 | Padding(0, 1), | 46 | Padding(0, 1), | ||
| 47 | evenStyle: lipgloss.NewStyle(). | 47 | evenStyle: lipgloss.NewStyle(). | ||
| 48 | - | Foreground(styles.ColorTextMuted). | 48 | + | Foreground(styles.ColorText). |
| 49 | Padding(0, 1), | 49 | Padding(0, 1), | ||
| 50 | } | 50 | } | ||
| 51 | 51 | ||||
M internal/adapters/in/cli/ui/styles/icons.go +75 -52
| @@ -2,78 +2,101 @@ package styles | |||
| 2 | 2 | ||
| 3 | 3 | // Nerd Font icons for terminal UI. | |
| 4 | 4 | // These icons require a Nerd Font compatible terminal font. | |
| 5 | + | // All icons are defined using Unicode escape sequences for portability. | |
| 5 | 6 | // Fallback ASCII alternatives are provided where practical. | |
| 6 | 7 | const ( | |
| 7 | 8 | // Status indicators | |
| 8 | - | IconSuccess = "" // nf-fa-check (U+F00C) | |
| 9 | - | IconError = "" // nf-fa-times (U+F00D) | |
| 10 | - | IconWarning = "" // nf-fa-exclamation_triangle (U+F071) | |
| 11 | - | IconInfo = "" // nf-fa-info_circle (U+F05A) | |
| 12 | - | IconPending = "" // nf-fa-clock_o (U+F017) | |
| 9 | + | IconSuccess = "\uf00c" // nf-fa-check | |
| 10 | + | IconError = "\uf00d" // nf-fa-times | |
| 11 | + | IconWarning = "\uf071" // nf-fa-exclamation_triangle | |
| 12 | + | IconInfo = "\uf05a" // nf-fa-info_circle | |
| 13 | + | IconPending = "\uf017" // nf-fa-clock | |
| 14 | + | ||
| 15 | + | // Container status icons | |
| 16 | + | IconRunning = "\uf00c" // nf-fa-check (green) | |
| 17 | + | IconStopped = "\uf04d" // nf-fa-stop | |
| 18 | + | IconExited = "\uf00d" // nf-fa-times | |
| 19 | + | IconPaused = "\uf04c" // nf-fa-pause | |
| 20 | + | IconRestarting = "\uf021" // nf-fa-refresh | |
| 21 | + | IconUnknown = "\uf071" // nf-fa-exclamation_triangle | |
| 13 | 22 | ||
| 14 | 23 | // Objects | |
| 15 | - | IconRoute = "" // nf-fa-sitemap (U+F0E8) | |
| 16 | - | IconSecret = "" // nf-fa-lock (U+F023) | |
| 17 | - | IconToken = "" // nf-fa-key (U+F084) | |
| 18 | - | IconContainer = "" // nf-oct-container (U+F489) | |
| 19 | - | IconServer = "" // nf-fa-server (U+F233) | |
| 20 | - | IconNetwork = "" // nf-md-lan (U+F06F3) | |
| 21 | - | IconVolume = "" // nf-fa-database (U+F1C0) | |
| 22 | - | IconImage = "" // nf-fa-archive (U+F187) | |
| 24 | + | IconRoute = "\uf0e8" // nf-fa-sitemap | |
| 25 | + | IconSecret = "\uf023" // nf-fa-lock | |
| 26 | + | IconToken = "\uf084" // nf-fa-key | |
| 27 | + | IconContainer = "\uf4b7" // nf-oct-container | |
| 28 | + | IconServer = "\uf233" // nf-fa-server | |
| 29 | + | IconNetwork = "\U000f0317" // nf-md-lan | |
| 30 | + | IconVolume = "\uf1c0" // nf-fa-database | |
| 31 | + | IconImage = "\uf187" // nf-fa-archive | |
| 32 | + | IconDocker = "\ue7b0" // nf-dev-docker | |
| 23 | 33 | ||
| 24 | 34 | // Actions | |
| 25 | - | IconAdd = "" // nf-fa-plus (U+F067) | |
| 26 | - | IconRemove = "" // nf-fa-minus (U+F068) | |
| 27 | - | IconEdit = "" // nf-fa-pencil (U+F040) | |
| 28 | - | IconRefresh = "" // nf-fa-refresh (U+F021) | |
| 29 | - | IconUpload = "" // nf-fa-cloud_upload (U+F0EE) | |
| 30 | - | IconDelete = "" // nf-fa-trash (U+F1F8) | |
| 35 | + | IconAdd = "\uf067" // nf-fa-plus | |
| 36 | + | IconRemove = "\uf068" // nf-fa-minus | |
| 37 | + | IconEdit = "\uf040" // nf-fa-pencil | |
| 38 | + | IconRefresh = "\uf021" // nf-fa-refresh | |
| 39 | + | IconUpload = "\uf0ee" // nf-fa-cloud_upload | |
| 40 | + | IconDelete = "\uf1f8" // nf-fa-trash | |
| 31 | 41 | ||
| 32 | 42 | // Navigation | |
| 33 | - | IconArrowRight = "" // nf-fa-arrow_right (U+F061) | |
| 34 | - | IconArrowLeft = "" // nf-fa-arrow_left (U+F060) | |
| 35 | - | IconArrowUp = "" // nf-fa-arrow_up (U+F062) | |
| 36 | - | IconArrowDown = "" // nf-fa-arrow_down (U+F063) | |
| 37 | - | IconChevron = "" // nf-fa-chevron_right (U+F054) | |
| 43 | + | IconArrowRight = "\uf061" // nf-fa-arrow_right | |
| 44 | + | IconArrowLeft = "\uf060" // nf-fa-arrow_left | |
| 45 | + | IconArrowUp = "\uf062" // nf-fa-arrow_up | |
| 46 | + | IconArrowDown = "\uf063" // nf-fa-arrow_down | |
| 47 | + | IconChevron = "\uf054" // nf-fa-chevron_right | |
| 38 | 48 | ||
| 39 | 49 | // UI elements | |
| 40 | - | IconBullet = "▸" // Simple triangle bullet | |
| 41 | - | IconDot = "●" // Filled circle | |
| 42 | - | IconDotEmpty = "○" // Empty circle | |
| 43 | - | IconCheckbox = "" // nf-fa-square_o (U+F096) | |
| 44 | - | IconChecked = "" // nf-fa-check_square_o (U+F046) | |
| 45 | - | IconRadio = "" // nf-fa-circle_o (U+F10C) | |
| 46 | - | IconRadioCheck = "" // nf-fa-dot_circle_o (U+F192) | |
| 50 | + | IconBullet = "\u25b8" // ▸ Simple triangle bullet | |
| 51 | + | IconDot = "\u25cf" // ● Filled circle | |
| 52 | + | IconDotEmpty = "\u25cb" // ○ Empty circle | |
| 53 | + | IconCheckbox = "\uf096" // nf-fa-square_o | |
| 54 | + | IconChecked = "\uf046" // nf-fa-check_square_o | |
| 55 | + | IconRadio = "\uf10c" // nf-fa-circle_o | |
| 56 | + | IconRadioCheck = "\uf192" // nf-fa-dot_circle_o | |
| 57 | + | IconPlay = "\uf04b" // nf-fa-play | |
| 58 | + | IconStop = "\uf04d" // nf-fa-stop | |
| 59 | + | IconPause = "\uf04c" // nf-fa-pause | |
| 60 | + | ||
| 61 | + | // Tree structure | |
| 62 | + | IconTreeBranch = "\u251c" // ├ | |
| 63 | + | IconTreeLast = "\u2514" // └ | |
| 64 | + | IconTreeLine = "\u2500" // ─ | |
| 65 | + | IconTreeVert = "\u2502" // │ | |
| 47 | 66 | ||
| 48 | 67 | // Spinners (individual frames) | |
| 49 | - | SpinnerDot = "⣾⣽⣻⢿⡿⣟⣯⣷" | |
| 68 | + | SpinnerDot = "\u28fe\u28fd\u28fb\u28bf\u287f\u28df\u28ef\u28f7" | |
| 50 | 69 | SpinnerLine = "|/-\\" | |
| 51 | - | SpinnerCircle = "◐◓◑◒" | |
| 52 | - | SpinnerBrail = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" | |
| 70 | + | SpinnerCircle = "\u25d0\u25d3\u25d1\u25d2" | |
| 71 | + | SpinnerBrail = "\u280b\u2819\u2839\u2838\u283c\u2834\u2826\u2827\u2807\u280f" | |
| 53 | 72 | ||
| 54 | 73 | // Borders and decorations | |
| 55 | - | BorderHorizontal = "─" | |
| 56 | - | BorderVertical = "│" | |
| 57 | - | BorderCornerTL = "┌" | |
| 58 | - | BorderCornerTR = "┐" | |
| 59 | - | BorderCornerBL = "└" | |
| 60 | - | BorderCornerBR = "┘" | |
| 61 | - | BorderT = "┬" | |
| 62 | - | BorderB = "┴" | |
| 63 | - | BorderL = "├" | |
| 64 | - | BorderR = "┤" | |
| 65 | - | BorderCross = "┼" | |
| 74 | + | BorderHorizontal = "\u2500" // ─ | |
| 75 | + | BorderVertical = "\u2502" // │ | |
| 76 | + | BorderCornerTL = "\u250c" // ┌ | |
| 77 | + | BorderCornerTR = "\u2510" // ┐ | |
| 78 | + | BorderCornerBL = "\u2514" // └ | |
| 79 | + | BorderCornerBR = "\u2518" // ┘ | |
| 80 | + | BorderT = "\u252c" // ┬ | |
| 81 | + | BorderB = "\u2534" // ┴ | |
| 82 | + | BorderL = "\u251c" // ├ | |
| 83 | + | BorderR = "\u2524" // ┤ | |
| 84 | + | BorderCross = "\u253c" // ┼ | |
| 66 | 85 | ||
| 67 | 86 | // Gordon branding | |
| 68 | - | IconGordon = "" // nf-fa-cube (U+F1B2) | |
| 87 | + | IconGordon = "\uf1b2" // nf-fa-cube | |
| 69 | 88 | ) | |
| 70 | 89 | ||
| 71 | 90 | // ASCII fallback alternatives for terminals without Nerd Fonts. | |
| 72 | 91 | const ( | |
| 73 | - | AsciiSuccess = "[OK]" | |
| 74 | - | AsciiError = "[X]" | |
| 75 | - | AsciiWarning = "[!]" | |
| 76 | - | AsciiInfo = "[i]" | |
| 77 | - | AsciiBullet = ">" | |
| 78 | - | AsciiDot = "*" | |
| 92 | + | AsciiSuccess = "[OK]" | |
| 93 | + | AsciiError = "[X]" | |
| 94 | + | AsciiWarning = "[!]" | |
| 95 | + | AsciiInfo = "[i]" | |
| 96 | + | AsciiBullet = ">" | |
| 97 | + | AsciiDot = "*" | |
| 98 | + | AsciiRunning = "[R]" | |
| 99 | + | AsciiStopped = "[S]" | |
| 100 | + | AsciiTreeBranch = "|-" | |
| 101 | + | AsciiTreeLast = "`-" | |
| 79 | 102 | ) | |
| @@ -2,78 +2,101 @@ package styles | |||||
| 2 | 2 | ||||
| 3 | // Nerd Font icons for terminal UI. | 3 | // Nerd Font icons for terminal UI. | ||
| 4 | // These icons require a Nerd Font compatible terminal font. | 4 | // These icons require a Nerd Font compatible terminal font. | ||
| 5 | + | // All icons are defined using Unicode escape sequences for portability. | |||
| 5 | // Fallback ASCII alternatives are provided where practical. | 6 | // Fallback ASCII alternatives are provided where practical. | ||
| 6 | const ( | 7 | const ( | ||
| 7 | // Status indicators | 8 | // Status indicators | ||
| 8 | - | IconSuccess = "" // nf-fa-check (U+F00C) | 9 | + | IconSuccess = "\uf00c" // nf-fa-check |
| 9 | - | IconError = "" // nf-fa-times (U+F00D) | 10 | + | IconError = "\uf00d" // nf-fa-times |
| 10 | - | IconWarning = "" // nf-fa-exclamation_triangle (U+F071) | 11 | + | IconWarning = "\uf071" // nf-fa-exclamation_triangle |
| 11 | - | IconInfo = "" // nf-fa-info_circle (U+F05A) | 12 | + | IconInfo = "\uf05a" // nf-fa-info_circle |
| 12 | - | IconPending = "" // nf-fa-clock_o (U+F017) | 13 | + | IconPending = "\uf017" // nf-fa-clock |
| 14 | + | ||||
| 15 | + | // Container status icons | |||
| 16 | + | IconRunning = "\uf00c" // nf-fa-check (green) | |||
| 17 | + | IconStopped = "\uf04d" // nf-fa-stop | |||
| 18 | + | IconExited = "\uf00d" // nf-fa-times | |||
| 19 | + | IconPaused = "\uf04c" // nf-fa-pause | |||
| 20 | + | IconRestarting = "\uf021" // nf-fa-refresh | |||
| 21 | + | IconUnknown = "\uf071" // nf-fa-exclamation_triangle | |||
| 13 | 22 | ||||
| 14 | // Objects | 23 | // Objects | ||
| 15 | - | IconRoute = "" // nf-fa-sitemap (U+F0E8) | 24 | + | IconRoute = "\uf0e8" // nf-fa-sitemap |
| 16 | - | IconSecret = "" // nf-fa-lock (U+F023) | 25 | + | IconSecret = "\uf023" // nf-fa-lock |
| 17 | - | IconToken = "" // nf-fa-key (U+F084) | 26 | + | IconToken = "\uf084" // nf-fa-key |
| 18 | - | IconContainer = "" // nf-oct-container (U+F489) | 27 | + | IconContainer = "\uf4b7" // nf-oct-container |
| 19 | - | IconServer = "" // nf-fa-server (U+F233) | 28 | + | IconServer = "\uf233" // nf-fa-server |
| 20 | - | IconNetwork = "" // nf-md-lan (U+F06F3) | 29 | + | IconNetwork = "\U000f0317" // nf-md-lan |
| 21 | - | IconVolume = "" // nf-fa-database (U+F1C0) | 30 | + | IconVolume = "\uf1c0" // nf-fa-database |
| 22 | - | IconImage = "" // nf-fa-archive (U+F187) | 31 | + | IconImage = "\uf187" // nf-fa-archive |
| 32 | + | IconDocker = "\ue7b0" // nf-dev-docker | |||
| 23 | 33 | ||||
| 24 | // Actions | 34 | // Actions | ||
| 25 | - | IconAdd = "" // nf-fa-plus (U+F067) | 35 | + | IconAdd = "\uf067" // nf-fa-plus |
| 26 | - | IconRemove = "" // nf-fa-minus (U+F068) | 36 | + | IconRemove = "\uf068" // nf-fa-minus |
| 27 | - | IconEdit = "" // nf-fa-pencil (U+F040) | 37 | + | IconEdit = "\uf040" // nf-fa-pencil |
| 28 | - | IconRefresh = "" // nf-fa-refresh (U+F021) | 38 | + | IconRefresh = "\uf021" // nf-fa-refresh |
| 29 | - | IconUpload = "" // nf-fa-cloud_upload (U+F0EE) | 39 | + | IconUpload = "\uf0ee" // nf-fa-cloud_upload |
| 30 | - | IconDelete = "" // nf-fa-trash (U+F1F8) | 40 | + | IconDelete = "\uf1f8" // nf-fa-trash |
| 31 | 41 | ||||
| 32 | // Navigation | 42 | // Navigation | ||
| 33 | - | IconArrowRight = "" // nf-fa-arrow_right (U+F061) | 43 | + | IconArrowRight = "\uf061" // nf-fa-arrow_right |
| 34 | - | IconArrowLeft = "" // nf-fa-arrow_left (U+F060) | 44 | + | IconArrowLeft = "\uf060" // nf-fa-arrow_left |
| 35 | - | IconArrowUp = "" // nf-fa-arrow_up (U+F062) | 45 | + | IconArrowUp = "\uf062" // nf-fa-arrow_up |
| 36 | - | IconArrowDown = "" // nf-fa-arrow_down (U+F063) | 46 | + | IconArrowDown = "\uf063" // nf-fa-arrow_down |
| 37 | - | IconChevron = "" // nf-fa-chevron_right (U+F054) | 47 | + | IconChevron = "\uf054" // nf-fa-chevron_right |
| 38 | 48 | ||||
| 39 | // UI elements | 49 | // UI elements | ||
| 40 | - | IconBullet = "▸" // Simple triangle bullet | 50 | + | IconBullet = "\u25b8" // ▸ Simple triangle bullet |
| 41 | - | IconDot = "●" // Filled circle | 51 | + | IconDot = "\u25cf" // ● Filled circle |
| 42 | - | IconDotEmpty = "○" // Empty circle | 52 | + | IconDotEmpty = "\u25cb" // ○ Empty circle |
| 43 | - | IconCheckbox = "" // nf-fa-square_o (U+F096) | 53 | + | IconCheckbox = "\uf096" // nf-fa-square_o |
| 44 | - | IconChecked = "" // nf-fa-check_square_o (U+F046) | 54 | + | IconChecked = "\uf046" // nf-fa-check_square_o |
| 45 | - | IconRadio = "" // nf-fa-circle_o (U+F10C) | 55 | + | IconRadio = "\uf10c" // nf-fa-circle_o |
| 46 | - | IconRadioCheck = "" // nf-fa-dot_circle_o (U+F192) | 56 | + | IconRadioCheck = "\uf192" // nf-fa-dot_circle_o |
| 57 | + | IconPlay = "\uf04b" // nf-fa-play | |||
| 58 | + | IconStop = "\uf04d" // nf-fa-stop | |||
| 59 | + | IconPause = "\uf04c" // nf-fa-pause | |||
| 60 | + | ||||
| 61 | + | // Tree structure | |||
| 62 | + | IconTreeBranch = "\u251c" // ├ | |||
| 63 | + | IconTreeLast = "\u2514" // └ | |||
| 64 | + | IconTreeLine = "\u2500" // ─ | |||
| 65 | + | IconTreeVert = "\u2502" // │ | |||
| 47 | 66 | ||||
| 48 | // Spinners (individual frames) | 67 | // Spinners (individual frames) | ||
| 49 | - | SpinnerDot = "⣾⣽⣻⢿⡿⣟⣯⣷" | 68 | + | SpinnerDot = "\u28fe\u28fd\u28fb\u28bf\u287f\u28df\u28ef\u28f7" |
| 50 | SpinnerLine = "|/-\\" | 69 | SpinnerLine = "|/-\\" | ||
| 51 | - | SpinnerCircle = "◐◓◑◒" | 70 | + | SpinnerCircle = "\u25d0\u25d3\u25d1\u25d2" |
| 52 | - | SpinnerBrail = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" | 71 | + | SpinnerBrail = "\u280b\u2819\u2839\u2838\u283c\u2834\u2826\u2827\u2807\u280f" |
| 53 | 72 | ||||
| 54 | // Borders and decorations | 73 | // Borders and decorations | ||
| 55 | - | BorderHorizontal = "─" | 74 | + | BorderHorizontal = "\u2500" // ─ |
| 56 | - | BorderVertical = "│" | 75 | + | BorderVertical = "\u2502" // │ |
| 57 | - | BorderCornerTL = "┌" | 76 | + | BorderCornerTL = "\u250c" // ┌ |
| 58 | - | BorderCornerTR = "┐" | 77 | + | BorderCornerTR = "\u2510" // ┐ |
| 59 | - | BorderCornerBL = "└" | 78 | + | BorderCornerBL = "\u2514" // └ |
| 60 | - | BorderCornerBR = "┘" | 79 | + | BorderCornerBR = "\u2518" // ┘ |
| 61 | - | BorderT = "┬" | 80 | + | BorderT = "\u252c" // ┬ |
| 62 | - | BorderB = "┴" | 81 | + | BorderB = "\u2534" // ┴ |
| 63 | - | BorderL = "├" | 82 | + | BorderL = "\u251c" // ├ |
| 64 | - | BorderR = "┤" | 83 | + | BorderR = "\u2524" // ┤ |
| 65 | - | BorderCross = "┼" | 84 | + | BorderCross = "\u253c" // ┼ |
| 66 | 85 | ||||
| 67 | // Gordon branding | 86 | // Gordon branding | ||
| 68 | - | IconGordon = "" // nf-fa-cube (U+F1B2) | 87 | + | IconGordon = "\uf1b2" // nf-fa-cube |
| 69 | ) | 88 | ) | ||
| 70 | 89 | ||||
| 71 | // ASCII fallback alternatives for terminals without Nerd Fonts. | 90 | // ASCII fallback alternatives for terminals without Nerd Fonts. | ||
| 72 | const ( | 91 | const ( | ||
| 73 | - | AsciiSuccess = "[OK]" | 92 | + | AsciiSuccess = "[OK]" |
| 74 | - | AsciiError = "[X]" | 93 | + | AsciiError = "[X]" |
| 75 | - | AsciiWarning = "[!]" | 94 | + | AsciiWarning = "[!]" |
| 76 | - | AsciiInfo = "[i]" | 95 | + | AsciiInfo = "[i]" |
| 77 | - | AsciiBullet = ">" | 96 | + | AsciiBullet = ">" |
| 78 | - | AsciiDot = "*" | 97 | + | AsciiDot = "*" |
| 98 | + | AsciiRunning = "[R]" | |||
| 99 | + | AsciiStopped = "[S]" | |||
| 100 | + | AsciiTreeBranch = "|-" | |||
| 101 | + | AsciiTreeLast = "`-" | |||
| 79 | ) | 102 | ) | ||