dataraces + skins
parent
3e36512e19
commit
445b475c21
|
|
@ -7,7 +7,6 @@ k9s
|
|||
/k8s
|
||||
dist
|
||||
notes
|
||||
styles
|
||||
vendor
|
||||
go.mod1
|
||||
popeye1.go
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ snapcraft:
|
|||
By leveraging a terminal UI, you can easily traverse Kubernetes resources
|
||||
and view the state of you clusters in a single powerful session.
|
||||
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
||||
publish: false
|
||||
# publish: false
|
||||
publish: true
|
||||
replacements:
|
||||
amd64: 64-bit
|
||||
386: 32-bit
|
||||
|
|
@ -76,16 +77,15 @@ snapcraft:
|
|||
bit: Arm
|
||||
bitv6: Arm6
|
||||
bitv7: Arm7
|
||||
grade: devel
|
||||
confinement: devmode
|
||||
# grade: devel
|
||||
# confinement: devmode
|
||||
grade: stable
|
||||
confinement: strict
|
||||
apps:
|
||||
k9s:
|
||||
plugs: ["home", "network"]
|
||||
# plugs: ["home", "network", "personal-files"]
|
||||
# plugs:
|
||||
# personal-files:
|
||||
# read:
|
||||
# - $HOME/.k9s
|
||||
# - $HOME/.kube
|
||||
# write:
|
||||
# - $HOME/.k9s
|
||||
# plugs: ["home", "network"]
|
||||
plugs: ["home", "network", "personal-files"]
|
||||
plugs:
|
||||
personal-files:
|
||||
read:
|
||||
- $HOME/.kube
|
||||
221
README.md
221
README.md
|
|
@ -249,6 +249,227 @@ roleRef:
|
|||
|
||||
---
|
||||
|
||||
## Skins
|
||||
|
||||
You can style K9s based on your own sense of style and look. This is very much an experimental feature at this time, more will be added/modified if this feature has legs so thread accordingly!
|
||||
|
||||
Skins are YAML files, that enable a user to change K9s presentation layer. K9s skins are loaded from `$HOME/.k9s/skin.yml`. If a skin file is detected then the skin would be loaded if not the current stock skin remains in effect.
|
||||
|
||||
Below is a sample skin file, more skins would be available in the skins directory, just simply copy any of these in your user's home dir as `skin.yml`.
|
||||
|
||||
```yaml
|
||||
# InTheNavy Skin...
|
||||
k9s:
|
||||
# General K9s styles
|
||||
fgColor: dodgerblue
|
||||
bgColor: white
|
||||
logoColor: blue
|
||||
# ClusterInfoView styles.
|
||||
info:
|
||||
fgColor: lightskyblue
|
||||
sectionColor: steelblue
|
||||
# Borders styles.
|
||||
border:
|
||||
fgColor: dodgerblue
|
||||
focusColor: aliceblue
|
||||
# MenuView attributes and styles.
|
||||
menu:
|
||||
fgColor: darkblue
|
||||
keyColor: cornflowerblue
|
||||
# Used for favorite namespaces
|
||||
numKeyColor: cadetblue
|
||||
# CrumbView attributes for history navigation.
|
||||
crumb:
|
||||
fgColor: white
|
||||
bgColor: steelblue
|
||||
# Active view settings
|
||||
activeColor: skyblue
|
||||
# TableView attributes.
|
||||
table:
|
||||
fgColor: blue
|
||||
bgColor: darkblue
|
||||
cursorColor: aqua
|
||||
# Header row styles.
|
||||
header:
|
||||
fgColor: white
|
||||
bgColor: darkblue
|
||||
sorterColor: orange
|
||||
# Resource status and update styles
|
||||
status:
|
||||
newColor: blue
|
||||
modifyColor: powderblue
|
||||
addColor: lightskyblue
|
||||
errorColor: indianred
|
||||
highlightcolor: royalblue
|
||||
killColor: slategray
|
||||
completedColor: gray
|
||||
# Border title styles.
|
||||
title:
|
||||
fgColor: aqua
|
||||
bgColor: white
|
||||
highlightColor: skyblue
|
||||
counterColor: slateblue
|
||||
filterColor: slategray
|
||||
# YAML info styles.
|
||||
yaml:
|
||||
keyColor: steelblue
|
||||
colonColor: blue
|
||||
valueColor: royalblue
|
||||
```
|
||||
|
||||
Available color names are defined below:
|
||||
|
||||
| Color Names |
|
||||
|----------------------|
|
||||
| black |
|
||||
| maroon |
|
||||
| green |
|
||||
| olive |
|
||||
| navy |
|
||||
| purple |
|
||||
| teal |
|
||||
| silver |
|
||||
| gray |
|
||||
| red |
|
||||
| lime |
|
||||
| yellow |
|
||||
| blue |
|
||||
| fuchsia |
|
||||
| aqua |
|
||||
| white |
|
||||
| aliceblue |
|
||||
| antiquewhite |
|
||||
| aquamarine |
|
||||
| azure |
|
||||
| beige |
|
||||
| bisque |
|
||||
| blanchedalmond |
|
||||
| blueviolet |
|
||||
| brown |
|
||||
| burlywood |
|
||||
| cadetblue |
|
||||
| chartreuse |
|
||||
| chocolate |
|
||||
| coral |
|
||||
| cornflowerblue |
|
||||
| cornsilk |
|
||||
| crimson |
|
||||
| darkblue |
|
||||
| darkcyan |
|
||||
| darkgoldenrod |
|
||||
| darkgray |
|
||||
| darkgreen |
|
||||
| darkkhaki |
|
||||
| darkmagenta |
|
||||
| darkolivegreen |
|
||||
| darkorange |
|
||||
| darkorchid |
|
||||
| darkred |
|
||||
| darksalmon |
|
||||
| darkseagreen |
|
||||
| darkslateblue |
|
||||
| darkslategray |
|
||||
| darkturquoise |
|
||||
| darkviolet |
|
||||
| deeppink |
|
||||
| deepskyblue |
|
||||
| dimgray |
|
||||
| dodgerblue |
|
||||
| firebrick |
|
||||
| floralwhite |
|
||||
| forestgreen |
|
||||
| gainsboro |
|
||||
| ghostwhite |
|
||||
| gold |
|
||||
| goldenrod |
|
||||
| greenyellow |
|
||||
| honeydew |
|
||||
| hotpink |
|
||||
| indianred |
|
||||
| indigo |
|
||||
| ivory |
|
||||
| khaki |
|
||||
| lavender |
|
||||
| lavenderblush |
|
||||
| lawngreen |
|
||||
| lemonchiffon |
|
||||
| lightblue |
|
||||
| lightcoral |
|
||||
| lightcyan |
|
||||
| lightgoldenrodyellow |
|
||||
| lightgray |
|
||||
| lightgreen |
|
||||
| lightpink |
|
||||
| lightsalmon |
|
||||
| lightseagreen |
|
||||
| lightskyblue |
|
||||
| lightslategray |
|
||||
| lightsteelblue |
|
||||
| lightyellow |
|
||||
| limegreen |
|
||||
| linen |
|
||||
| mediumaquamarine |
|
||||
| mediumblue |
|
||||
| mediumorchid |
|
||||
| mediumpurple |
|
||||
| mediumseagreen |
|
||||
| mediumslateblue |
|
||||
| mediumspringgreen |
|
||||
| mediumturquoise |
|
||||
| mediumvioletred |
|
||||
| midnightblue |
|
||||
| mintcream |
|
||||
| mistyrose |
|
||||
| moccasin |
|
||||
| navajowhite |
|
||||
| oldlace |
|
||||
| olivedrab |
|
||||
| orange |
|
||||
| orangered |
|
||||
| orchid |
|
||||
| palegoldenrod |
|
||||
| palegreen |
|
||||
| paleturquoise |
|
||||
| palevioletred |
|
||||
| papayawhip |
|
||||
| peachpuff |
|
||||
| peru |
|
||||
| pink |
|
||||
| plum |
|
||||
| powderblue |
|
||||
| rebeccapurple |
|
||||
| rosybrown |
|
||||
| royalblue |
|
||||
| saddlebrown |
|
||||
| salmon |
|
||||
| sandybrown |
|
||||
| seagreen |
|
||||
| seashell |
|
||||
| sienna |
|
||||
| skyblue |
|
||||
| slateblue |
|
||||
| slategray |
|
||||
| snow |
|
||||
| springgreen |
|
||||
| steelblue |
|
||||
| tan |
|
||||
| thistle |
|
||||
| tomato |
|
||||
| turquoise |
|
||||
| violet |
|
||||
| wheat |
|
||||
| whitesmoke |
|
||||
| yellowgreen |
|
||||
| grey |
|
||||
| dimgrey |
|
||||
| darkgrey |
|
||||
| darkslategrey |
|
||||
| lightgrey |
|
||||
| lightslategrey |
|
||||
| slategrey |
|
||||
|
||||
---
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This is still work in progress! If there is enough interest in the Kubernetes
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
|
||||
|
||||
# Release v0.6.0
|
||||
|
||||
## Notes
|
||||
|
||||
Thank you to all that contributed with flushing out issues with K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes!
|
||||
|
||||
If you've filed an issue please help me verify and close.
|
||||
|
||||
Thank you so much for your support and awesome suggestions to make K9s better!!
|
||||
|
||||
Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
|
||||
|
||||
---
|
||||
|
||||
## Change Logs
|
||||
|
||||
### Skins
|
||||
|
||||
You can now skin K9s based on your own sense of style. Skinning, is currently limited to color variations and is still very much experimental. More details on how to achieve this is provided in the README and skins directory in this repo. This could be a prime opportunity for someone to contribute to this project and help us come up with some cooler LNF and share with all K9s folks. Any schemes contributed will be added and featured on this repo.
|
||||
|
||||
---
|
||||
|
||||
## Resolved Bugs
|
||||
|
||||
+ [Issue #169](https://github.com/derailed/k9s/issues/169)
|
||||
+ [Issue #171](https://github.com/derailed/k9s/issues/171)
|
||||
+ [Issue #172](https://github.com/derailed/k9s/issues/172)
|
||||
+ [Issue #175](https://github.com/derailed/k9s/issues/175)
|
||||
|
||||
---
|
||||
|
||||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
5
go.mod
5
go.mod
|
|
@ -12,10 +12,12 @@ replace (
|
|||
|
||||
require (
|
||||
github.com/Azure/go-autorest/autorest v0.1.0 // indirect
|
||||
github.com/derailed/tview v0.1.4
|
||||
github.com/derailed/tview v0.1.6
|
||||
github.com/evanphx/json-patch v4.1.0+incompatible // indirect
|
||||
github.com/fatih/camelcase v1.0.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/gdamore/tcell v1.1.1
|
||||
github.com/go-fsnotify/fsnotify v0.0.0-20180321022601-755488143dae // indirect
|
||||
github.com/gogo/protobuf v1.2.1 // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/google/gofuzz v1.0.0 // indirect
|
||||
|
|
@ -30,6 +32,7 @@ require (
|
|||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/petergtz/pegomock v0.0.0-20181206220228-b113d17a7e81
|
||||
github.com/pkg/profile v1.3.0
|
||||
github.com/rs/zerolog v1.14.3
|
||||
github.com/spf13/cobra v0.0.3
|
||||
github.com/spf13/pflag v1.0.3 // indirect
|
||||
|
|
|
|||
10
go.sum
10
go.sum
|
|
@ -47,6 +47,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/derailed/tview v0.1.4 h1:6ZtMtb5+2bbGNH7SldHGcVB8GnSTXKIQwKxWRNb6DxY=
|
||||
github.com/derailed/tview v0.1.4/go.mod h1:oLBQyhVeXqeUYWDZk7/5NJVbbq/JFXm3W7oEoEtpmSc=
|
||||
github.com/derailed/tview v0.1.5 h1:Gj6K73V9d+zsex208KX8YsAw68+nWVNr7oM9qfTWbXQ=
|
||||
github.com/derailed/tview v0.1.5/go.mod h1:g+ZyIsV5osK+lQ6LajiGQeLW10BQLJ6aMvy8Ldt2oa0=
|
||||
github.com/derailed/tview v0.1.6 h1:mmp6Yg78IgbdHapV9wFoVYrbtZbMXilCdAIHadm2uqc=
|
||||
github.com/derailed/tview v0.1.6/go.mod h1:g+ZyIsV5osK+lQ6LajiGQeLW10BQLJ6aMvy8Ldt2oa0=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
|
|
@ -71,6 +75,8 @@ github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2H
|
|||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-fsnotify/fsnotify v0.0.0-20180321022601-755488143dae h1:PeVNzgTRtWGm6fVic5i21t+n5ptPGCZuMcSPVMyTWjs=
|
||||
github.com/go-fsnotify/fsnotify v0.0.0-20180321022601-755488143dae/go.mod h1:BbhqyaehKPCLD83cqfRYdm177Ylm1cdGHu3txjbQSQI=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
|
|
@ -193,6 +199,8 @@ github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG
|
|||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.3.0 h1:OQIvuDgm00gWVWGTf4m4mCt6W1/0YqU7Ntg0mySWgaI=
|
||||
github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
|
@ -211,6 +219,8 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R
|
|||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
github.com/rivo/tview v0.0.0-20190213202703-b373355e9db4/go.mod h1:J4W+hErFfITUbyFAEXizpmkuxX7ZN56dopxHB4XQhMw=
|
||||
github.com/rivo/uniseg v0.0.0-20190313204849-f699dde9c340 h1:nOZbL5f2xmBAHWYrrHbHV1xatzZirN++oOQ3g83Ypgs=
|
||||
github.com/rivo/uniseg v0.0.0-20190313204849-f699dde9c340/go.mod h1:SOLvOL4ybwgLJ6TYoX/rtaJ8EGOulH4XU7E9/TLrTCE=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.14.3 h1:4EGfSkR2hJDB0s3oFfrlPqjU1e4WLncergLil3nEKW0=
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ func (n *Namespace) Validate(c Connection, ks KubeSettings) {
|
|||
func (n *Namespace) SetActive(ns string, ks KubeSettings) error {
|
||||
log.Debug().Msgf("Setting active ns %s", ns)
|
||||
n.Active = ns
|
||||
n.addFavNS(ns)
|
||||
if ns != "" {
|
||||
n.addFavNS(ns)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,302 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
// K9sStylesFile represents K9s config file location.
|
||||
K9sStylesFile = filepath.Join(K9sHome, "skin.yml")
|
||||
)
|
||||
|
||||
type (
|
||||
// Styles tracks K9s styling options.
|
||||
Styles struct {
|
||||
Style *Style `yaml:"k9s"`
|
||||
}
|
||||
|
||||
// Style tracks K9s styles.
|
||||
Style struct {
|
||||
FgColor string `yaml:"fgColor"`
|
||||
BgColor string `yaml:"bgColor"`
|
||||
LogoColor string `yaml:"logoColor"`
|
||||
|
||||
Info *Info `yaml:"info"`
|
||||
Border *Border `yaml:"border"`
|
||||
Menu *Menu `yaml:"menu"`
|
||||
Crumb *Crumb `yaml:"crumb"`
|
||||
Table *Table `yaml:"table"`
|
||||
Status *Status `yaml:"status"`
|
||||
Title *Title `yaml:"title"`
|
||||
Yaml *Yaml `yaml:"yaml"`
|
||||
}
|
||||
|
||||
// Status tracks resource status styles.
|
||||
Status struct {
|
||||
NewColor string `yaml:"newColor"`
|
||||
ModifyColor string `yaml:"modifyColor"`
|
||||
AddColor string `yaml:"addColor"`
|
||||
ErrorColor string `yaml:"errorColor"`
|
||||
HighlightColor string `yaml:"highlightColor"`
|
||||
KillColor string `yaml:"killColor"`
|
||||
CompletedColor string `yaml:"completedColor"`
|
||||
}
|
||||
|
||||
// Yaml tracks yaml styles.
|
||||
Yaml struct {
|
||||
KeyColor string `yaml:"keyColor"`
|
||||
ValueColor string `yaml:"valueColor"`
|
||||
ColonColor string `yaml:"colonColor"`
|
||||
}
|
||||
|
||||
// Title tracks title styles.
|
||||
Title struct {
|
||||
FgColor string `yaml:"fgColor"`
|
||||
BgColor string `yaml:"bgColor"`
|
||||
HighlightColor string `yaml:"highlightColor"`
|
||||
CounterColor string `yaml:"counterColor"`
|
||||
FilterColor string `yaml:"filterColor"`
|
||||
}
|
||||
|
||||
// Info tracks info styles.
|
||||
Info struct {
|
||||
SectionColor string `yaml:"sectionColor"`
|
||||
FgColor string `yaml:"fgColor"`
|
||||
}
|
||||
|
||||
// Border tracks border styles.
|
||||
Border struct {
|
||||
FgColor string `yaml:"fgColor"`
|
||||
FocusColor string `yaml:"focusColor"`
|
||||
}
|
||||
|
||||
// Crumb tracks crumbs styles.
|
||||
Crumb struct {
|
||||
FgColor string `yaml:"fgColor"`
|
||||
BgColor string `yaml:"bgColor"`
|
||||
ActiveColor string `yaml:"activeColor"`
|
||||
}
|
||||
|
||||
// Table tracks table styles.
|
||||
Table struct {
|
||||
FgColor string `yaml:"fgColor"`
|
||||
BgColor string `yaml:"bgColor"`
|
||||
CursorColor string `yaml:"cursorColor"`
|
||||
Header *TableHeader `yaml:"header"`
|
||||
}
|
||||
|
||||
// TableHeader tracks table header styles.
|
||||
TableHeader struct {
|
||||
FgColor string `yaml:"fgColor"`
|
||||
BgColor string `yaml:"bgColor"`
|
||||
SorterColor string `yaml:"sorterColor"`
|
||||
}
|
||||
|
||||
// Menu tracks menu styles.
|
||||
Menu struct {
|
||||
FgColor string `yaml:"fgColor"`
|
||||
KeyColor string `yaml:"keyColor"`
|
||||
NumKeyColor string `yaml:"numKeyColor"`
|
||||
}
|
||||
)
|
||||
|
||||
func newStyle() *Style {
|
||||
return &Style{
|
||||
FgColor: "cadetblue",
|
||||
BgColor: "black",
|
||||
LogoColor: "orange",
|
||||
Info: newInfo(),
|
||||
Border: newBorder(),
|
||||
Menu: newMenu(),
|
||||
Crumb: newCrumb(),
|
||||
Table: newTable(),
|
||||
Status: newStatus(),
|
||||
Title: newTitle(),
|
||||
Yaml: newYaml(),
|
||||
}
|
||||
}
|
||||
|
||||
func newStatus() *Status {
|
||||
return &Status{
|
||||
NewColor: "lightskyblue",
|
||||
ModifyColor: "greenyellow",
|
||||
AddColor: "white",
|
||||
ErrorColor: "orangered",
|
||||
HighlightColor: "aqua",
|
||||
KillColor: "mediumpurple",
|
||||
CompletedColor: "gray",
|
||||
}
|
||||
}
|
||||
|
||||
// NewYaml returns a new yaml style.
|
||||
func newYaml() *Yaml {
|
||||
return &Yaml{
|
||||
KeyColor: "steelblue",
|
||||
ColonColor: "white",
|
||||
ValueColor: "papayawhip",
|
||||
}
|
||||
}
|
||||
|
||||
// NewTitle returns a new title style.
|
||||
func newTitle() *Title {
|
||||
return &Title{
|
||||
FgColor: "aqua",
|
||||
BgColor: "black",
|
||||
HighlightColor: "fuchsia",
|
||||
CounterColor: "papayawhip",
|
||||
FilterColor: "steelblue",
|
||||
}
|
||||
}
|
||||
|
||||
// NewInfo returns a new info style.
|
||||
func newInfo() *Info {
|
||||
return &Info{
|
||||
SectionColor: "white",
|
||||
FgColor: "orange",
|
||||
}
|
||||
}
|
||||
|
||||
// NewTable returns a new table style.
|
||||
func newTable() *Table {
|
||||
return &Table{
|
||||
FgColor: "aqua",
|
||||
BgColor: "black",
|
||||
CursorColor: "aqua",
|
||||
Header: newTableHeader(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewTableHeader returns a new table header style.
|
||||
func newTableHeader() *TableHeader {
|
||||
return &TableHeader{
|
||||
FgColor: "white",
|
||||
BgColor: "black",
|
||||
SorterColor: "orange",
|
||||
}
|
||||
}
|
||||
|
||||
// NewCrumb returns a new crumbs style.
|
||||
func newCrumb() *Crumb {
|
||||
return &Crumb{
|
||||
FgColor: "black",
|
||||
BgColor: "aqua",
|
||||
ActiveColor: "orange",
|
||||
}
|
||||
}
|
||||
|
||||
// NewBorder returns a new border style.
|
||||
func newBorder() *Border {
|
||||
return &Border{
|
||||
FgColor: "dodgerblue",
|
||||
FocusColor: "lightskyblue",
|
||||
}
|
||||
}
|
||||
|
||||
// NewMenu returns a new menu style.
|
||||
func newMenu() *Menu {
|
||||
return &Menu{
|
||||
FgColor: "white",
|
||||
KeyColor: "dodgerblue",
|
||||
NumKeyColor: "fuchsia",
|
||||
}
|
||||
}
|
||||
|
||||
// NewStyles creates a new default config.
|
||||
func NewStyles() (*Styles, error) {
|
||||
s := &Styles{Style: newStyle()}
|
||||
err := s.load(K9sStylesFile)
|
||||
|
||||
return s, err
|
||||
}
|
||||
|
||||
// Ensure default styles are applied in not in stylesheet.
|
||||
func (s *Styles) ensure() {
|
||||
if s.Style == nil {
|
||||
s.Style = newStyle()
|
||||
}
|
||||
|
||||
if s.Style.Info == nil {
|
||||
s.Style.Info = newInfo()
|
||||
}
|
||||
|
||||
if s.Style.Border == nil {
|
||||
s.Style.Border = newBorder()
|
||||
}
|
||||
|
||||
if s.Style.Table == nil {
|
||||
s.Style.Table = newTable()
|
||||
}
|
||||
|
||||
if s.Style.Menu == nil {
|
||||
s.Style.Menu = newMenu()
|
||||
}
|
||||
|
||||
if s.Style.Crumb == nil {
|
||||
s.Style.Crumb = newCrumb()
|
||||
}
|
||||
|
||||
if s.Style.Status == nil {
|
||||
s.Style.Status = newStatus()
|
||||
}
|
||||
|
||||
if s.Style.Title == nil {
|
||||
s.Style.Title = newTitle()
|
||||
}
|
||||
|
||||
if s.Style.Yaml == nil {
|
||||
s.Style.Yaml = newYaml()
|
||||
}
|
||||
}
|
||||
|
||||
// FgColor returns the foreground color.
|
||||
func (s *Styles) FgColor() tcell.Color {
|
||||
return AsColor(s.Style.FgColor)
|
||||
}
|
||||
|
||||
// BgColor returns the background color.
|
||||
func (s *Styles) BgColor() tcell.Color {
|
||||
return AsColor(s.Style.BgColor)
|
||||
}
|
||||
|
||||
// Load K9s configuration from file
|
||||
func (s *Styles) load(path string) error {
|
||||
f, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var cfg Styles
|
||||
if err := yaml.Unmarshal(f, &cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg.Style != nil {
|
||||
s.Style = cfg.Style
|
||||
}
|
||||
s.ensure()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update apply terminal colors based on styles.
|
||||
func (s *Styles) Update() {
|
||||
tview.Styles.PrimitiveBackgroundColor = AsColor(s.Style.BgColor)
|
||||
tview.Styles.ContrastBackgroundColor = AsColor(s.Style.BgColor)
|
||||
tview.Styles.PrimaryTextColor = AsColor(s.Style.FgColor)
|
||||
tview.Styles.BorderColor = AsColor(s.Style.Border.FgColor)
|
||||
tview.Styles.FocusColor = AsColor(s.Style.Border.FocusColor)
|
||||
}
|
||||
|
||||
// AsColor checks color index, if match return color otherwise pink it is.
|
||||
func AsColor(c string) tcell.Color {
|
||||
if color, ok := tcell.ColorNames[c]; ok {
|
||||
return color
|
||||
}
|
||||
|
||||
return tcell.ColorPink
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@ package resource
|
|||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
|
|
@ -22,6 +22,7 @@ type (
|
|||
instance v1.Container
|
||||
MetricsServer MetricsServer
|
||||
metrics k8s.PodMetrics
|
||||
mx sync.RWMutex
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -82,30 +83,49 @@ func (r *Container) Logs(c chan<- string, ns, n, co string, lines int64, prev bo
|
|||
go func() {
|
||||
select {
|
||||
case <-time.After(defaultTimeout):
|
||||
if blocked {
|
||||
var closes bool
|
||||
r.mx.RLock()
|
||||
{
|
||||
closes = blocked
|
||||
}
|
||||
r.mx.RUnlock()
|
||||
if closes {
|
||||
log.Debug().Msg(">>Closing Channel<<")
|
||||
close(c)
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// This call will block if nothing is in the stream!!
|
||||
stream, err := req.Stream()
|
||||
blocked = false
|
||||
if err != nil {
|
||||
log.Error().Msgf("Tail logs failed `%s/%s:%s -- %v", ns, n, co, err)
|
||||
return cancel, fmt.Errorf("%v", err)
|
||||
return cancel, err
|
||||
}
|
||||
|
||||
r.mx.Lock()
|
||||
{
|
||||
blocked = false
|
||||
}
|
||||
r.mx.Unlock()
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
log.Debug().Msg("!!!Closing Stream!!!")
|
||||
close(c)
|
||||
stream.Close()
|
||||
cancel()
|
||||
close(c)
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(stream)
|
||||
for scanner.Scan() {
|
||||
c <- scanner.Text()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
|
|
@ -40,6 +41,7 @@ type (
|
|||
instance *v1.Pod
|
||||
MetricsServer MetricsServer
|
||||
metrics k8s.PodMetrics
|
||||
mx sync.RWMutex
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -55,7 +57,11 @@ func NewPodList(c Connection, mx MetricsServer, ns string) List {
|
|||
|
||||
// NewPod instantiates a new Pod.
|
||||
func NewPod(c Connection, mx MetricsServer) *Pod {
|
||||
p := &Pod{&Base{Connection: c, Resource: k8s.NewPod(c)}, nil, mx, k8s.PodMetrics{}}
|
||||
p := &Pod{
|
||||
Base: &Base{Connection: c, Resource: k8s.NewPod(c)},
|
||||
MetricsServer: mx,
|
||||
metrics: k8s.PodMetrics{},
|
||||
}
|
||||
p.Factory = p
|
||||
|
||||
return p
|
||||
|
|
@ -123,30 +129,49 @@ func (r *Pod) Logs(c chan<- string, ns, n, co string, lines int64, prev bool) (c
|
|||
go func() {
|
||||
select {
|
||||
case <-time.After(defaultTimeout):
|
||||
if blocked {
|
||||
var closes bool
|
||||
r.mx.RLock()
|
||||
{
|
||||
closes = blocked
|
||||
}
|
||||
r.mx.RUnlock()
|
||||
if closes {
|
||||
log.Debug().Msg(">>Closing Channel<<")
|
||||
close(c)
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// This call will block if nothing is in the stream!!
|
||||
stream, err := req.Stream()
|
||||
blocked = false
|
||||
if err != nil {
|
||||
log.Error().Msgf("Tail logs failed `%s/%s:%s -- %v", ns, n, co, err)
|
||||
return cancel, fmt.Errorf("%v", err)
|
||||
return cancel, err
|
||||
}
|
||||
|
||||
r.mx.Lock()
|
||||
{
|
||||
blocked = false
|
||||
}
|
||||
r.mx.Unlock()
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
log.Debug().Msg("!!!Closing Stream!!!")
|
||||
close(c)
|
||||
stream.Close()
|
||||
cancel()
|
||||
close(c)
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(stream)
|
||||
for scanner.Scan() {
|
||||
c <- scanner.Text()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
|
|||
|
|
@ -3,18 +3,22 @@ package views
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
const splashTime = 1
|
||||
const (
|
||||
splashTime = 1
|
||||
devMode = "dev"
|
||||
)
|
||||
|
||||
type (
|
||||
focusHandler func(tview.Primitive)
|
||||
|
|
@ -45,6 +49,7 @@ type (
|
|||
*tview.Application
|
||||
|
||||
config *config.Config
|
||||
styles *config.Styles
|
||||
version string
|
||||
flags *genericclioptions.ConfigFlags
|
||||
pages *tview.Pages
|
||||
|
|
@ -61,7 +66,6 @@ type (
|
|||
cmdBuff *cmdBuff
|
||||
cmdView *cmdView
|
||||
actions keyActions
|
||||
mx sync.Mutex
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -69,15 +73,16 @@ type (
|
|||
func NewApp(cfg *config.Config) *appView {
|
||||
v := appView{Application: tview.NewApplication(), config: cfg}
|
||||
{
|
||||
v.refreshStyles()
|
||||
v.pages = tview.NewPages()
|
||||
v.actions = make(keyActions)
|
||||
v.menuView = newMenuView()
|
||||
v.menuView = newMenuView(&v)
|
||||
v.content = tview.NewPages()
|
||||
v.cmdBuff = newCmdBuff(':')
|
||||
v.cmdView = newCmdView('🐶')
|
||||
v.cmdView = newCmdView(&v, '🐶')
|
||||
v.command = newCommand(&v)
|
||||
v.flashView = newFlashView(v.Application, "Initializing...")
|
||||
v.crumbsView = newCrumbsView(v.Application)
|
||||
v.flashView = newFlashView(&v, "Initializing...")
|
||||
v.crumbsView = newCrumbsView(&v)
|
||||
v.clusterInfoView = newClusterInfoView(&v, k8s.NewMetricsServer(cfg.GetConnection()))
|
||||
v.focusChanged = v.changedFocus
|
||||
v.SetInputCapture(v.keyboard)
|
||||
|
|
@ -108,21 +113,21 @@ func (a *appView) Init(v string, rate int, flags *genericclioptions.ConfigFlags)
|
|||
header.SetDirection(tview.FlexColumn)
|
||||
header.AddItem(a.clusterInfoView, 35, 1, false)
|
||||
header.AddItem(a.menuView, 0, 1, false)
|
||||
header.AddItem(logoView(), 26, 1, false)
|
||||
header.AddItem(a.logoView(), 26, 1, false)
|
||||
}
|
||||
|
||||
main := tview.NewFlex()
|
||||
{
|
||||
main.SetDirection(tview.FlexRow)
|
||||
main.AddItem(header, 7, 1, false)
|
||||
main.AddItem(a.cmdView, 1, 1, false)
|
||||
main.AddItem(a.cmdView, 3, 1, false)
|
||||
main.AddItem(a.content, 0, 10, true)
|
||||
main.AddItem(a.crumbsView, 2, 1, false)
|
||||
main.AddItem(a.flashView, 1, 1, false)
|
||||
}
|
||||
|
||||
a.pages.AddPage("main", main, true, false)
|
||||
a.pages.AddPage("splash", newSplash(a.version), true, true)
|
||||
a.pages.AddPage("splash", newSplash(a), true, true)
|
||||
a.SetRoot(a.pages, true)
|
||||
}
|
||||
|
||||
|
|
@ -130,12 +135,52 @@ func (a *appView) conn() k8s.Connection {
|
|||
return a.config.GetConnection()
|
||||
}
|
||||
|
||||
func (a *appView) stylesUpdater() (*fsnotify.Watcher, error) {
|
||||
w, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("File notifier failed")
|
||||
return w, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case evt := <-w.Events:
|
||||
log.Debug().Msgf("Evt %#v", evt)
|
||||
a.QueueUpdateDraw(func() {
|
||||
a.refreshStyles()
|
||||
})
|
||||
case err := <-w.Errors:
|
||||
log.Error().Err(err).Msg("Watcher failed")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := w.Add(config.K9sStylesFile); err != nil {
|
||||
log.Error().Err(err).Msg("Styles file watch failed")
|
||||
return w, err
|
||||
}
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Run starts the application loop
|
||||
func (a *appView) Run() {
|
||||
// Only enable updater while in dev mode.
|
||||
if a.version == devMode {
|
||||
w, err := a.stylesUpdater()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
w.Close()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-time.After(splashTime * time.Second)
|
||||
a.showPage("main")
|
||||
a.Draw()
|
||||
a.QueueUpdateDraw(func() {
|
||||
a.showPage("main")
|
||||
})
|
||||
}()
|
||||
|
||||
a.command.defaultCmd()
|
||||
|
|
@ -145,9 +190,6 @@ func (a *appView) Run() {
|
|||
}
|
||||
|
||||
func (a *appView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
a.mx.Lock()
|
||||
defer a.mx.Unlock()
|
||||
|
||||
key := evt.Key()
|
||||
if key == tcell.KeyRune {
|
||||
if a.cmdBuff.isActive() {
|
||||
|
|
@ -156,10 +198,12 @@ func (a *appView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
key = tcell.Key(evt.Rune())
|
||||
}
|
||||
|
||||
if a, ok := a.actions[key]; ok {
|
||||
log.Debug().Msgf(">> AppView handled key: %s", tcell.KeyNames[key])
|
||||
log.Debug().Msgf(">> AppView handled key: %s -- %d", tcell.KeyNames[key], runtime.NumGoroutine())
|
||||
return a.action(evt)
|
||||
}
|
||||
|
||||
return evt
|
||||
}
|
||||
|
||||
|
|
@ -216,8 +260,8 @@ func (a *appView) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
if a.cmdView.inCmdMode() {
|
||||
return evt
|
||||
}
|
||||
a.flash(flashInfo, "Entering command mode...")
|
||||
log.Debug().Msg("Entering app command mode...")
|
||||
a.flash(flashInfo, "Command mode activated.")
|
||||
log.Debug().Msg("Entering command mode...")
|
||||
a.cmdBuff.setActive(true)
|
||||
a.cmdBuff.clear()
|
||||
return nil
|
||||
|
|
@ -290,10 +334,6 @@ func (a *appView) cmdMode() bool {
|
|||
return a.cmdView.inCmdMode()
|
||||
}
|
||||
|
||||
func (a *appView) refresh() {
|
||||
a.clusterInfoView.refresh()
|
||||
}
|
||||
|
||||
func (a *appView) flash(level flashLevel, m ...string) {
|
||||
a.flashView.setMessage(level, m...)
|
||||
}
|
||||
|
|
@ -302,14 +342,14 @@ func (a *appView) setHints(h hints) {
|
|||
a.menuView.populateMenu(h)
|
||||
}
|
||||
|
||||
func logoView() tview.Primitive {
|
||||
func (a *appView) logoView() tview.Primitive {
|
||||
v := tview.NewTextView()
|
||||
{
|
||||
v.SetWordWrap(false)
|
||||
v.SetWrap(false)
|
||||
v.SetDynamicColors(true)
|
||||
for i, s := range LogoSmall {
|
||||
fmt.Fprintf(v, "[orange::b]%s", s)
|
||||
fmt.Fprintf(v, "[%s::b]%s", a.styles.Style.LogoColor, s)
|
||||
if i+1 < len(LogoSmall) {
|
||||
fmt.Fprintf(v, "\n")
|
||||
}
|
||||
|
|
@ -348,9 +388,17 @@ func (a *appView) nextFocus() {
|
|||
return
|
||||
}
|
||||
|
||||
func initStyles() {
|
||||
tview.Styles.PrimitiveBackgroundColor = tcell.ColorBlack
|
||||
tview.Styles.ContrastBackgroundColor = tcell.ColorBlack
|
||||
tview.Styles.FocusColor = tcell.ColorLightSkyBlue
|
||||
tview.Styles.BorderColor = tcell.ColorDodgerBlue
|
||||
func (a *appView) refreshStyles() {
|
||||
var err error
|
||||
if a.styles, err = config.NewStyles(); err != nil {
|
||||
log.Error().Err(err).Msg("No skin file found. Loading defaults.")
|
||||
}
|
||||
a.styles.Update()
|
||||
|
||||
stdColor = config.AsColor(a.styles.Style.Status.NewColor)
|
||||
addColor = config.AsColor(a.styles.Style.Status.AddColor)
|
||||
modColor = config.AsColor(a.styles.Style.Status.ModifyColor)
|
||||
errColor = config.AsColor(a.styles.Style.Status.ErrorColor)
|
||||
highlightColor = config.AsColor(a.styles.Style.Status.HighlightColor)
|
||||
completedColor = config.AsColor(a.styles.Style.Status.CompletedColor)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package views
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
|
|
@ -62,20 +63,25 @@ func (v *clusterInfoView) init() {
|
|||
v.SetCell(row, 1, v.infoCell("n/a"))
|
||||
v.SetCell(row+1, 0, v.sectionCell("MEM"))
|
||||
v.SetCell(row+1, 1, v.infoCell("n/a"))
|
||||
|
||||
v.refresh()
|
||||
}
|
||||
|
||||
func (*clusterInfoView) sectionCell(t string) *tview.TableCell {
|
||||
func (v *clusterInfoView) sectionCell(t string) *tview.TableCell {
|
||||
c := tview.NewTableCell(t + ":")
|
||||
c.SetAlign(tview.AlignLeft)
|
||||
var s tcell.Style
|
||||
c.SetStyle(s.Bold(true).Foreground(config.AsColor(v.app.styles.Style.Info.SectionColor)))
|
||||
c.SetBackgroundColor(v.app.styles.BgColor())
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (*clusterInfoView) infoCell(t string) *tview.TableCell {
|
||||
func (v *clusterInfoView) infoCell(t string) *tview.TableCell {
|
||||
c := tview.NewTableCell(t)
|
||||
c.SetExpansion(2)
|
||||
c.SetTextColor(tcell.ColorOrange)
|
||||
c.SetTextColor(config.AsColor(v.app.styles.Style.Info.FgColor))
|
||||
c.SetBackgroundColor(v.app.styles.BgColor())
|
||||
|
||||
return c
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ package views
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const defaultPrompt = "%c> %s"
|
||||
|
|
@ -16,16 +15,20 @@ type cmdView struct {
|
|||
activated bool
|
||||
icon rune
|
||||
text string
|
||||
app *appView
|
||||
}
|
||||
|
||||
func newCmdView(ic rune) *cmdView {
|
||||
v := cmdView{icon: ic, TextView: tview.NewTextView()}
|
||||
func newCmdView(app *appView, ic rune) *cmdView {
|
||||
v := cmdView{app: app, icon: ic, TextView: tview.NewTextView()}
|
||||
{
|
||||
v.SetWordWrap(true)
|
||||
v.SetWrap(true)
|
||||
v.SetDynamicColors(true)
|
||||
v.SetBorder(true)
|
||||
v.SetBorderPadding(0, 0, 1, 1)
|
||||
v.SetTextColor(tcell.ColorAqua)
|
||||
v.SetBackgroundColor(app.styles.BgColor())
|
||||
v.SetBorderColor(config.AsColor(app.styles.Style.Border.FocusColor))
|
||||
v.SetTextColor(app.styles.FgColor())
|
||||
}
|
||||
return &v
|
||||
}
|
||||
|
|
@ -62,11 +65,12 @@ func (v *cmdView) changed(s string) {
|
|||
func (v *cmdView) active(f bool) {
|
||||
v.activated = f
|
||||
if f {
|
||||
log.Debug().Msg("CmdView was activated...")
|
||||
v.SetBackgroundColor(tcell.ColorDodgerBlue)
|
||||
v.SetBorder(true)
|
||||
v.SetTextColor(v.app.styles.FgColor())
|
||||
v.activate()
|
||||
} else {
|
||||
v.SetBackgroundColor(tcell.ColorDefault)
|
||||
v.SetBorder(false)
|
||||
v.SetBackgroundColor(v.app.styles.BgColor())
|
||||
v.Clear()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const maxBuff = 10
|
||||
|
||||
type buffWatcher interface {
|
||||
|
|
@ -37,6 +41,7 @@ func (c *cmdBuff) String() string {
|
|||
}
|
||||
|
||||
func (c *cmdBuff) add(r rune) {
|
||||
log.Debug().Msgf("Add %s", string(r))
|
||||
c.buff = append(c.buff, r)
|
||||
c.fireChanged()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ import (
|
|||
"k8s.io/apimachinery/pkg/watch"
|
||||
)
|
||||
|
||||
const (
|
||||
modColor = tcell.ColorGreenYellow
|
||||
addColor = tcell.ColorLightSkyBlue
|
||||
errColor = tcell.ColorOrangeRed
|
||||
stdColor = tcell.ColorWhite
|
||||
highlightColor = tcell.ColorAqua
|
||||
killColor = tcell.ColorMediumPurple
|
||||
completedColor = tcell.ColorDodgerBlue
|
||||
var (
|
||||
modColor tcell.Color
|
||||
addColor tcell.Color
|
||||
errColor tcell.Color
|
||||
stdColor tcell.Color
|
||||
highlightColor tcell.Color
|
||||
killColor tcell.Color
|
||||
completedColor tcell.Color
|
||||
)
|
||||
|
||||
func defaultColorer(ns string, r *resource.RowEvent) tcell.Color {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ func newContainerView(t string, app *appView, list resource.List, path string) r
|
|||
}
|
||||
v.AddPage("logs", newLogsView(list.GetName(), &v), true, false)
|
||||
v.switchPage("co")
|
||||
v.selChanged(1, 0)
|
||||
|
||||
return &v
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,33 +4,36 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
type crumbsView struct {
|
||||
*tview.TextView
|
||||
|
||||
app *tview.Application
|
||||
app *appView
|
||||
}
|
||||
|
||||
func newCrumbsView(app *tview.Application) *crumbsView {
|
||||
func newCrumbsView(app *appView) *crumbsView {
|
||||
v := crumbsView{app: app, TextView: tview.NewTextView()}
|
||||
{
|
||||
v.SetTextColor(tcell.ColorAqua)
|
||||
v.SetBackgroundColor(app.styles.BgColor())
|
||||
v.SetTextAlign(tview.AlignLeft)
|
||||
v.SetBorderPadding(0, 0, 1, 1)
|
||||
v.SetDynamicColors(true)
|
||||
}
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
func (v *crumbsView) update(crumbs []string) {
|
||||
v.Clear()
|
||||
last, bgColor := len(crumbs)-1, "aqua"
|
||||
last, bgColor := len(crumbs)-1, v.app.styles.Style.Crumb.BgColor
|
||||
for i, c := range crumbs {
|
||||
if i == last {
|
||||
bgColor = "orange"
|
||||
bgColor = v.app.styles.Style.Crumb.ActiveColor
|
||||
}
|
||||
fmt.Fprintf(v, "[black:%s:b] <%s> [-:-:-] ", bgColor, c)
|
||||
fmt.Fprintf(v, "[%s:%s:b] <%s> [-:%s:-] ",
|
||||
v.app.styles.Style.Crumb.FgColor,
|
||||
bgColor, c,
|
||||
v.app.styles.Style.BgColor)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const detailsTitleFmt = " [aqua::b]%s([fuchsia::b]%s[aqua::-])[aqua::-] "
|
||||
const detailsTitleFmt = "[fg:bg:b] %s([hilite:bg:b]%s[fg:bg:-])[fg:bg:-] "
|
||||
|
||||
// detailsView displays text output.
|
||||
type detailsView struct {
|
||||
|
|
@ -25,7 +25,6 @@ type detailsView struct {
|
|||
cmdBuff *cmdBuff
|
||||
backFn actionHandler
|
||||
numSelections int
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
func newDetailsView(app *appView, backFn actionHandler) *detailsView {
|
||||
|
|
@ -37,6 +36,7 @@ func newDetailsView(app *appView, backFn actionHandler) *detailsView {
|
|||
v.SetDynamicColors(true)
|
||||
v.SetRegions(true)
|
||||
v.SetBorder(true)
|
||||
v.SetBorderFocusColor(config.AsColor(v.app.styles.Style.Border.FocusColor))
|
||||
v.SetHighlightColor(tcell.ColorOrange)
|
||||
v.SetTitleColor(tcell.ColorAqua)
|
||||
v.SetInputCapture(v.keyboard)
|
||||
|
|
@ -65,9 +65,6 @@ func (v *detailsView) setCategory(n string) {
|
|||
}
|
||||
|
||||
func (v *detailsView) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||
v.mx.Lock()
|
||||
defer v.mx.Unlock()
|
||||
|
||||
key := evt.Key()
|
||||
if key == tcell.KeyRune {
|
||||
if v.cmdBuff.isActive() {
|
||||
|
|
@ -198,9 +195,16 @@ func (v *detailsView) refreshTitle() {
|
|||
|
||||
func (v *detailsView) setTitle(t string) {
|
||||
v.title = t
|
||||
title := fmt.Sprintf(detailsTitleFmt, v.category, t)
|
||||
|
||||
fmat := strings.Replace(detailsTitleFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
|
||||
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
|
||||
fmat = strings.Replace(fmat, "[hilite", "["+v.app.styles.Style.Title.CounterColor, 1)
|
||||
title := fmt.Sprintf(fmat, v.category, t)
|
||||
if !v.cmdBuff.empty() {
|
||||
title += fmt.Sprintf(searchFmt, v.cmdBuff.String())
|
||||
fmat := strings.Replace(searchFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
|
||||
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
|
||||
fmat = strings.Replace(fmat, "[filter", "["+v.app.styles.Style.Title.FilterColor, 1)
|
||||
title += fmt.Sprintf(fmat, v.cmdBuff.String())
|
||||
}
|
||||
v.SetTitle(title)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,42 +30,44 @@ type (
|
|||
*tview.TextView
|
||||
|
||||
cancel context.CancelFunc
|
||||
app *tview.Application
|
||||
app *appView
|
||||
}
|
||||
)
|
||||
|
||||
func newFlashView(app *tview.Application, m string) *flashView {
|
||||
func newFlashView(app *appView, m string) *flashView {
|
||||
f := flashView{app: app, TextView: tview.NewTextView()}
|
||||
{
|
||||
f.SetTextColor(tcell.ColorAqua)
|
||||
f.SetTextAlign(tview.AlignLeft)
|
||||
f.SetBorderPadding(0, 0, 1, 1)
|
||||
f.SetText("")
|
||||
}
|
||||
f.SetTextColor(tcell.ColorAqua)
|
||||
f.SetTextAlign(tview.AlignLeft)
|
||||
f.SetBorderPadding(0, 0, 1, 1)
|
||||
f.SetText("")
|
||||
|
||||
return &f
|
||||
}
|
||||
|
||||
func (f *flashView) setMessage(level flashLevel, msg ...string) {
|
||||
if f.cancel != nil {
|
||||
f.cancel()
|
||||
func (v *flashView) setMessage(level flashLevel, msg ...string) {
|
||||
if v.cancel != nil {
|
||||
v.cancel()
|
||||
}
|
||||
|
||||
_, _, width, _ := v.GetRect()
|
||||
if width <= 15 {
|
||||
width = 100
|
||||
}
|
||||
m := strings.Join(msg, " ")
|
||||
v.SetTextColor(flashColor(level))
|
||||
v.SetText(resource.Truncate(flashEmoji(level)+" "+m, width-3))
|
||||
|
||||
var ctx context.Context
|
||||
{
|
||||
ctx, f.cancel = context.WithTimeout(context.TODO(), flashDelay*time.Second)
|
||||
ctx, v.cancel = context.WithTimeout(context.TODO(), flashDelay*time.Second)
|
||||
go func(ctx context.Context) {
|
||||
_, _, width, _ := f.GetRect()
|
||||
if width <= 15 {
|
||||
width = 100
|
||||
}
|
||||
m := strings.Join(msg, " ")
|
||||
f.SetTextColor(flashColor(level))
|
||||
f.SetText(resource.Truncate(flashEmoji(level)+" "+m, width-3))
|
||||
f.app.Draw()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
f.Clear()
|
||||
f.app.Draw()
|
||||
v.app.QueueUpdateDraw(func() {
|
||||
v.Clear()
|
||||
// v.app.Draw()
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package views
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/tview"
|
||||
)
|
||||
|
|
@ -28,14 +29,23 @@ func newLogView(title string, parent loggable) *logView {
|
|||
return &v
|
||||
}
|
||||
|
||||
func (l *logView) logLine(line string, scroll bool) {
|
||||
func (l *logView) logLine(line string) {
|
||||
fmt.Fprintln(l.ansiWriter, tview.Escape(line))
|
||||
if scroll {
|
||||
l.ScrollToEnd()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logView) log(lines fmt.Stringer) {
|
||||
l.Clear()
|
||||
fmt.Fprintln(l.ansiWriter, lines.String())
|
||||
}
|
||||
|
||||
func (l *logView) flush(index int, buff []string, scroll bool) {
|
||||
if index > 0 {
|
||||
l.logLine(strings.Join(buff[:index], "\n"))
|
||||
if scroll {
|
||||
l.app.QueueUpdate(func() {
|
||||
l.ScrollToEnd()
|
||||
})
|
||||
}
|
||||
index = 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package views
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
|
|
@ -17,6 +17,7 @@ const (
|
|||
maxBuff1 int64 = 200
|
||||
refreshRate = 200 * time.Millisecond
|
||||
maxCleanse = 100
|
||||
logBuffSize = 100
|
||||
)
|
||||
|
||||
type logsView struct {
|
||||
|
|
@ -29,7 +30,6 @@ type logsView struct {
|
|||
cancelFunc context.CancelFunc
|
||||
autoScroll bool
|
||||
showPrevious bool
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
func newLogsView(pview string, parent loggable) *logsView {
|
||||
|
|
@ -130,8 +130,9 @@ func (v *logsView) stop() {
|
|||
if v.cancelFunc == nil {
|
||||
return
|
||||
}
|
||||
log.Debug().Msg("Canceling logs...")
|
||||
|
||||
v.cancelFunc()
|
||||
log.Debug().Msgf("Canceling logs... %d", runtime.NumGoroutine())
|
||||
v.cancelFunc = nil
|
||||
}
|
||||
|
||||
|
|
@ -139,47 +140,57 @@ func (v *logsView) load(i int) {
|
|||
if i < 0 || i > len(v.containers)-1 {
|
||||
return
|
||||
}
|
||||
|
||||
v.SwitchToPage(v.containers[i])
|
||||
if err := v.doLoad(v.parent.getSelection(), v.containers[i]); err != nil {
|
||||
v.parent.appView().flash(flashErr, err.Error())
|
||||
l := v.CurrentPage().Item.(*logView)
|
||||
l.logLine("😂 Doh! No logs are available at this time. Check again later on...", false)
|
||||
l.logLine("😂 Doh! No logs are available at this time. Check again later on...")
|
||||
return
|
||||
}
|
||||
v.parent.appView().SetFocus(v)
|
||||
}
|
||||
|
||||
func (v *logsView) doLoad(path, co string) error {
|
||||
v.mx.Lock()
|
||||
defer v.mx.Unlock()
|
||||
|
||||
v.stop()
|
||||
|
||||
c := make(chan string)
|
||||
go func() {
|
||||
l := v.CurrentPage().Item.(*logView)
|
||||
l.Clear()
|
||||
l.setTitle(path + ":" + co)
|
||||
maxBuff := int64(v.parent.appView().config.K9s.LogRequestSize)
|
||||
l := v.CurrentPage().Item.(*logView)
|
||||
l.Clear()
|
||||
l.setTitle(path + ":" + co)
|
||||
|
||||
c := make(chan string, 10)
|
||||
go func(l *logView) {
|
||||
buff, index := make([]string, logBuffSize), 0
|
||||
for {
|
||||
select {
|
||||
case line, ok := <-c:
|
||||
if !ok {
|
||||
if v.autoScroll {
|
||||
l.ScrollToEnd()
|
||||
}
|
||||
l.flush(index, buff, v.autoScroll)
|
||||
index = 0
|
||||
return
|
||||
}
|
||||
l.logLine(line, v.autoScroll)
|
||||
if index < logBuffSize {
|
||||
buff[index] = line
|
||||
index++
|
||||
continue
|
||||
}
|
||||
l.flush(index, buff, v.autoScroll)
|
||||
index = 0
|
||||
buff[index] = line
|
||||
case <-time.After(1 * time.Second):
|
||||
l.flush(index, buff, v.autoScroll)
|
||||
index = 0
|
||||
}
|
||||
}
|
||||
}()
|
||||
}(l)
|
||||
|
||||
ns, po := namespaced(path)
|
||||
res, ok := v.parent.getList().Resource().(resource.Tailable)
|
||||
if !ok {
|
||||
return fmt.Errorf("Resource %T is not tailable", v.parent.getList().Resource)
|
||||
}
|
||||
maxBuff := int64(v.parent.appView().config.K9s.LogRequestSize)
|
||||
|
||||
cancelFn, err := res.Logs(c, ns, po, co, maxBuff, v.showPrevious)
|
||||
if err != nil {
|
||||
cancelFn()
|
||||
|
|
@ -196,9 +207,9 @@ func (v *logsView) doLoad(path, co string) error {
|
|||
func (v *logsView) toggleScrollCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
v.autoScroll = !v.autoScroll
|
||||
if v.autoScroll {
|
||||
v.parent.appView().flash(flashInfo, "Autoscroll is on")
|
||||
v.parent.appView().flash(flashInfo, "Autoscroll is on.")
|
||||
} else {
|
||||
v.parent.appView().flash(flashInfo, "Autoscroll is off")
|
||||
v.parent.appView().flash(flashInfo, "Autoscroll is off.")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -208,7 +219,7 @@ func (v *logsView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
v.stop()
|
||||
v.parent.switchPage(v.parentView)
|
||||
|
||||
return nil
|
||||
return evt
|
||||
}
|
||||
|
||||
func (v *logsView) topCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/tview"
|
||||
|
|
@ -16,12 +15,10 @@ import (
|
|||
|
||||
func init() {
|
||||
initKeys()
|
||||
initStyles()
|
||||
}
|
||||
|
||||
const (
|
||||
menuSepFmt = " [dodgerblue::b]%-%ds [white::d]%s "
|
||||
menuIndexFmt = " [fuchsia::b]<%d> [white::d]%s "
|
||||
menuIndexFmt = " [key:bg:b]<%d> [fg:bg:d]%s "
|
||||
maxRows = 7
|
||||
)
|
||||
|
||||
|
|
@ -77,11 +74,6 @@ func newKeyAction(d string, a actionHandler, display bool) keyAction {
|
|||
return keyAction{description: d, action: a, visible: display}
|
||||
}
|
||||
|
||||
func newMenuView() *menuView {
|
||||
v := menuView{Table: tview.NewTable()}
|
||||
return &v
|
||||
}
|
||||
|
||||
func (a keyActions) toHints() hints {
|
||||
kk := make([]int, 0, len(a))
|
||||
for k, v := range a {
|
||||
|
|
@ -108,16 +100,19 @@ func (a keyActions) toHints() hints {
|
|||
type menuView struct {
|
||||
*tview.Table
|
||||
|
||||
mx sync.Mutex
|
||||
app *appView
|
||||
}
|
||||
|
||||
func newMenuView(app *appView) *menuView {
|
||||
v := menuView{Table: tview.NewTable(), app: app}
|
||||
v.SetBackgroundColor(app.styles.BgColor())
|
||||
|
||||
return &v
|
||||
}
|
||||
|
||||
func (v *menuView) populateMenu(hh hints) {
|
||||
v.mx.Lock()
|
||||
defer v.mx.Unlock()
|
||||
|
||||
v.Clear()
|
||||
sort.Sort(hh)
|
||||
|
||||
t := v.buildMenuTable(hh)
|
||||
for row := 0; row < len(t); row++ {
|
||||
for col := 0; col < len(t[row]); col++ {
|
||||
|
|
@ -125,6 +120,7 @@ func (v *menuView) populateMenu(hh hints) {
|
|||
continue
|
||||
}
|
||||
c := tview.NewTableCell(t[row][col])
|
||||
c.SetBackgroundColor(v.app.styles.BgColor())
|
||||
v.SetCell(row, col, c)
|
||||
}
|
||||
}
|
||||
|
|
@ -143,9 +139,6 @@ func (v *menuView) buildMenuTable(hh hints) [][]string {
|
|||
maxKeys := make([]int, colCount+1)
|
||||
for _, h := range hh {
|
||||
isDigit := menuRX.MatchString(h.mnemonic)
|
||||
// if isDigit && firstNS {
|
||||
// row, col, firstNS = 0, 2, false
|
||||
// }
|
||||
if !isDigit && firstCmd {
|
||||
row, col, firstCmd = 0, col+1, false
|
||||
}
|
||||
|
|
@ -184,11 +177,17 @@ func (*menuView) toMnemonic(s string) string {
|
|||
func (v *menuView) formatMenu(h hint, size int) string {
|
||||
i, err := strconv.Atoi(h.mnemonic)
|
||||
if err == nil {
|
||||
return fmt.Sprintf(menuIndexFmt, i, resource.Truncate(h.description, 14))
|
||||
fmat := strings.Replace(menuIndexFmt, "[key", "["+v.app.styles.Style.Menu.NumKeyColor, 1)
|
||||
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
|
||||
fmat = strings.Replace(fmat, "[fg", "["+v.app.styles.Style.Menu.FgColor, 1)
|
||||
return fmt.Sprintf(fmat, i, resource.Truncate(h.description, 14))
|
||||
}
|
||||
|
||||
menuFmt := " [dodgerblue::b]%-" + strconv.Itoa(size+2) + "s [white::d]%s "
|
||||
return fmt.Sprintf(menuFmt, v.toMnemonic(h.mnemonic), h.description)
|
||||
menuFmt := " [key:bg:b]%-" + strconv.Itoa(size+2) + "s [fg:bg:d]%s "
|
||||
fmat := strings.Replace(menuFmt, "[key", "["+v.app.styles.Style.Menu.KeyColor, 1)
|
||||
fmat = strings.Replace(fmat, "[fg", "["+v.app.styles.Style.Menu.FgColor, 1)
|
||||
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
|
||||
return fmt.Sprintf(fmat, v.toMnemonic(h.mnemonic), h.description)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -63,8 +63,11 @@ func showPods(app *appView, ns, res, selected, labelSel, fieldSel string, b acti
|
|||
|
||||
title := fmt.Sprintf("%s:%s Pods", res, selected)
|
||||
pv := newPodView(title, app, list)
|
||||
pv.setColorerFn(podColorer)
|
||||
pv.setExtraActionsFn(func(aa keyActions) {
|
||||
aa[tcell.KeyEsc] = newKeyAction("Back", b, true)
|
||||
})
|
||||
// Reset active namespace to all.
|
||||
app.config.SetActiveNamespace("")
|
||||
app.inject(pv)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package views
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/k8s"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
|
|
@ -10,7 +11,7 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const fmat = "[aqua::b]%s([fuchsia::b]%s[aqua::-])"
|
||||
const containerFmt = "[fg:bg:b]%s([hilite:bg:b]%s[fg:bg:-])"
|
||||
|
||||
type podView struct {
|
||||
*resourceView
|
||||
|
|
@ -61,7 +62,17 @@ func (v *podView) listContainers(app *appView, _, res, sel string) {
|
|||
|
||||
mx := k8s.NewMetricsServer(app.conn())
|
||||
list := resource.NewContainerList(app.conn(), mx, po)
|
||||
app.inject(newContainerView(fmt.Sprintf(fmat, "Containers", sel), app, list, namespacedName(po.Namespace, po.Name)))
|
||||
|
||||
fmat := strings.Replace(containerFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
|
||||
fmat = strings.Replace(containerFmt, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
|
||||
fmat = strings.Replace(fmat, "[hilite", "["+v.app.styles.Style.Title.CounterColor, 1)
|
||||
|
||||
app.inject(newContainerView(
|
||||
fmt.Sprintf(fmat, "Containers", sel),
|
||||
app,
|
||||
list,
|
||||
namespacedName(po.Namespace, po.Name),
|
||||
))
|
||||
}
|
||||
|
||||
// Protocol...
|
||||
|
|
@ -88,6 +99,7 @@ func (v *podView) logsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
if v.viewLogs(false) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return evt
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +107,7 @@ func (v *podView) prevLogsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
if v.viewLogs(true) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return evt
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const (
|
|||
|
||||
all = "*"
|
||||
rbacTitle = "RBAC"
|
||||
rbacTitleFmt = " [aqua::b]%s([fuchsia::b]%s[aqua::-])"
|
||||
rbacTitleFmt = " [fg:bg:b]%s([hilite:bg:b]%s[fg:bg:-])"
|
||||
)
|
||||
|
||||
type (
|
||||
|
|
@ -28,6 +28,7 @@ type (
|
|||
rbacView struct {
|
||||
*tableView
|
||||
|
||||
app *appView
|
||||
current igniter
|
||||
cancel context.CancelFunc
|
||||
roleType roleKind
|
||||
|
|
@ -78,7 +79,7 @@ var (
|
|||
)
|
||||
|
||||
func newRBACView(app *appView, ns, name string, kind roleKind) *rbacView {
|
||||
v := rbacView{}
|
||||
v := rbacView{app: app}
|
||||
{
|
||||
v.roleName, v.roleType = name, kind
|
||||
v.tableView = newTableView(app, v.getTitle())
|
||||
|
|
@ -125,7 +126,11 @@ func (v *rbacView) bindKeys() {
|
|||
}
|
||||
|
||||
func (v *rbacView) getTitle() string {
|
||||
return fmt.Sprintf(rbacTitleFmt, rbacTitle, v.roleName)
|
||||
fmat := strings.Replace(rbacTitleFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
|
||||
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
|
||||
fmat = strings.Replace(fmat, "[hilite", "["+v.app.styles.Style.Title.HighlightColor, 1)
|
||||
|
||||
return fmt.Sprintf(fmat, rbacTitle, v.roleName)
|
||||
}
|
||||
|
||||
func (v *rbacView) hints() hints {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
|
|
@ -17,10 +16,7 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
refreshDelay = 0.1
|
||||
noSelection = ""
|
||||
)
|
||||
const noSelection = ""
|
||||
|
||||
type (
|
||||
details interface {
|
||||
|
|
@ -40,7 +36,6 @@ type (
|
|||
selectedRow int
|
||||
namespaces map[int]string
|
||||
selectedNS string
|
||||
update sync.Mutex
|
||||
list resource.List
|
||||
enterFn enterFn
|
||||
extraActionsFn func(keyActions)
|
||||
|
|
@ -90,13 +85,16 @@ func (v *resourceView) init(ctx context.Context, ns string) {
|
|||
log.Debug().Msgf("%s watcher canceled!", v.title)
|
||||
return
|
||||
case <-time.After(time.Duration(v.app.config.K9s.RefreshRate) * time.Second):
|
||||
v.refresh()
|
||||
v.app.QueueUpdate(func() {
|
||||
v.refresh()
|
||||
})
|
||||
}
|
||||
}
|
||||
}(ctx)
|
||||
v.refresh()
|
||||
if tv, ok := v.CurrentPage().Item.(*tableView); ok {
|
||||
tv.Select(0, 0)
|
||||
tv.Select(1, 0)
|
||||
v.selChanged(1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,6 +116,7 @@ func (v *resourceView) getSelectedItem() string {
|
|||
if v.selectedFn != nil {
|
||||
return v.selectedFn()
|
||||
}
|
||||
|
||||
return v.selectedItem
|
||||
}
|
||||
|
||||
|
|
@ -220,7 +219,7 @@ func (v *resourceView) defaultEnter(app *appView, ns, resource, selection string
|
|||
details.setCategory("Describe")
|
||||
details.setTitle(sel)
|
||||
details.SetTextColor(tcell.ColorAqua)
|
||||
details.SetText(colorizeYAML(yaml))
|
||||
details.SetText(colorizeYAML(v.app.styles.Style, yaml))
|
||||
details.ScrollToBeginning()
|
||||
}
|
||||
v.switchPage("details")
|
||||
|
|
@ -239,6 +238,7 @@ func (v *resourceView) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
if !v.rowSelected() {
|
||||
return evt
|
||||
}
|
||||
|
||||
sel := v.getSelectedItem()
|
||||
raw, err := v.list.Resource().Marshal(sel)
|
||||
if err != nil {
|
||||
|
|
@ -251,10 +251,11 @@ func (v *resourceView) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
details.setCategory("View")
|
||||
details.setTitle(sel)
|
||||
details.SetTextColor(tcell.ColorMediumAquamarine)
|
||||
details.SetText(colorizeYAML(raw))
|
||||
details.SetText(colorizeYAML(v.app.styles.Style, raw))
|
||||
details.ScrollToBeginning()
|
||||
}
|
||||
v.switchPage("details")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -262,6 +263,7 @@ func (v *resourceView) editCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
if !v.rowSelected() {
|
||||
return evt
|
||||
}
|
||||
|
||||
ns, po := namespaced(v.selectedItem)
|
||||
args := make([]string, 0, 10)
|
||||
args = append(args, "edit")
|
||||
|
|
@ -270,6 +272,7 @@ func (v *resourceView) editCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
args = append(args, "--context", v.app.config.K9s.CurrentContext)
|
||||
args = append(args, po)
|
||||
runK(true, v.app, args...)
|
||||
|
||||
return evt
|
||||
}
|
||||
|
||||
|
|
@ -282,21 +285,17 @@ func (v *resourceView) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
|
||||
func (v *resourceView) doSwitchNamespace(ns string) {
|
||||
v.update.Lock()
|
||||
{
|
||||
if ns == noSelection {
|
||||
ns = resource.AllNamespace
|
||||
}
|
||||
v.selectedNS = ns
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Viewing `%s namespace...", ns))
|
||||
v.list.SetNamespace(v.selectedNS)
|
||||
if ns == "" {
|
||||
ns = resource.AllNamespace
|
||||
}
|
||||
v.update.Unlock()
|
||||
v.selectedNS = ns
|
||||
v.app.flash(flashInfo, fmt.Sprintf("Viewing `%s namespace...", ns))
|
||||
v.list.SetNamespace(v.selectedNS)
|
||||
|
||||
v.refresh()
|
||||
v.selectItem(0, 0)
|
||||
v.getTV().resetTitle()
|
||||
v.getTV().Select(0, 0)
|
||||
v.getTV().Select(1, 0)
|
||||
v.selectItem(1, 0)
|
||||
v.app.cmdBuff.reset()
|
||||
v.app.config.SetActiveNamespace(v.selectedNS)
|
||||
v.app.config.Save()
|
||||
|
|
@ -307,32 +306,32 @@ func (v *resourceView) refresh() {
|
|||
return
|
||||
}
|
||||
|
||||
v.update.Lock()
|
||||
{
|
||||
if v.list.Namespaced() {
|
||||
v.list.SetNamespace(v.selectedNS)
|
||||
}
|
||||
if err := v.list.Reconcile(); err != nil {
|
||||
log.Error().Err(err).Msg("Reconciliation failed")
|
||||
v.app.flash(flashErr, err.Error())
|
||||
}
|
||||
data := v.list.Data()
|
||||
if v.decorateFn != nil {
|
||||
data = v.decorateFn(data)
|
||||
}
|
||||
v.getTV().update(data)
|
||||
v.selectItem(v.selectedRow, 0)
|
||||
v.refreshActions()
|
||||
v.app.clusterInfoView.refresh()
|
||||
v.app.Draw()
|
||||
if v.list.Namespaced() {
|
||||
v.list.SetNamespace(v.selectedNS)
|
||||
}
|
||||
v.update.Unlock()
|
||||
|
||||
v.refreshActions()
|
||||
v.app.clusterInfoView.refresh()
|
||||
|
||||
if err := v.list.Reconcile(); err != nil {
|
||||
log.Error().Err(err).Msg("Reconciliation failed")
|
||||
v.app.flash(flashErr, err.Error())
|
||||
}
|
||||
data := v.list.Data()
|
||||
if v.decorateFn != nil {
|
||||
data = v.decorateFn(data)
|
||||
}
|
||||
|
||||
v.getTV().update(data)
|
||||
v.selectItem(v.selectedRow, 0)
|
||||
v.app.Draw()
|
||||
}
|
||||
|
||||
func (v *resourceView) getTV() *tableView {
|
||||
if tv, ok := v.GetPrimitive(v.list.GetName()).(*tableView); ok {
|
||||
return tv
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -360,18 +359,11 @@ func (v *resourceView) selectItem(r, c int) {
|
|||
}
|
||||
|
||||
func (v *resourceView) switchPage(p string) {
|
||||
v.update.Lock()
|
||||
{
|
||||
v.SwitchToPage(p)
|
||||
v.selectedNS = v.list.GetNamespace()
|
||||
if h, ok := v.GetPrimitive(p).(hinter); ok {
|
||||
v.app.setHints(h.hints())
|
||||
v.app.SetFocus(v.CurrentPage().Item)
|
||||
} else {
|
||||
log.Error().Msgf("Hinter not implemented on %s", p)
|
||||
}
|
||||
v.SwitchToPage(p)
|
||||
v.selectedNS = v.list.GetNamespace()
|
||||
if h, ok := v.GetPrimitive(p).(hinter); ok {
|
||||
v.app.setHints(h.hints())
|
||||
}
|
||||
v.update.Unlock()
|
||||
}
|
||||
|
||||
func (v *resourceView) rowSelected() bool {
|
||||
|
|
@ -389,7 +381,7 @@ func (v *resourceView) refreshActions() {
|
|||
}
|
||||
|
||||
var nn []interface{}
|
||||
if !v.list.HasSelectors() && k8s.CanIAccess(v.app.conn().Config(), log.Logger, "", "list", "namespaces", "namespace.v1") {
|
||||
if k8s.CanIAccess(v.app.conn().Config(), log.Logger, "", "list", "namespaces", "namespace.v1") {
|
||||
var err error
|
||||
nn, err = k8s.NewNamespace(v.app.conn()).List(resource.AllNamespaces)
|
||||
if err != nil {
|
||||
|
|
@ -413,7 +405,6 @@ func (v *resourceView) refreshActions() {
|
|||
}
|
||||
|
||||
v.actions[tcell.KeyEnter] = newKeyAction("Enter", v.enterCmd, false)
|
||||
|
||||
v.actions[tcell.KeyCtrlR] = newKeyAction("Refresh", v.refreshCmd, false)
|
||||
v.actions[KeyHelp] = newKeyAction("Help", v.app.noopCmd, false)
|
||||
v.actions[KeyP] = newKeyAction("Previous", v.app.prevCmd, false)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func (v *secretView) decodeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
details.setCategory("Decoder")
|
||||
details.setTitle(sel)
|
||||
details.SetTextColor(tcell.ColorMediumAquamarine)
|
||||
details.SetText(colorizeYAML(string(raw)))
|
||||
details.SetText(colorizeYAML(v.app.styles.Style, string(raw)))
|
||||
details.ScrollToBeginning()
|
||||
}
|
||||
v.switchPage("details")
|
||||
|
|
|
|||
|
|
@ -36,11 +36,13 @@ var Logo = []string{
|
|||
// Splash screen definition
|
||||
type splashView struct {
|
||||
*tview.Flex
|
||||
|
||||
app *appView
|
||||
}
|
||||
|
||||
// NewSplash instantiates a new splash screen with product and company info.
|
||||
func newSplash(rev string) *splashView {
|
||||
v := splashView{tview.NewFlex()}
|
||||
func newSplash(app *appView) *splashView {
|
||||
v := splashView{Flex: tview.NewFlex(), app: app}
|
||||
|
||||
logo := tview.NewTextView()
|
||||
{
|
||||
|
|
@ -56,7 +58,7 @@ func newSplash(rev string) *splashView {
|
|||
vers.SetBackgroundColor(tcell.ColorDefault)
|
||||
vers.SetTextAlign(tview.AlignCenter)
|
||||
}
|
||||
v.layoutRev(vers, rev)
|
||||
v.layoutRev(vers, app.version)
|
||||
|
||||
v.SetDirection(tview.FlexRow)
|
||||
v.AddItem(logo, 10, 1, false)
|
||||
|
|
@ -65,10 +67,13 @@ func newSplash(rev string) *splashView {
|
|||
}
|
||||
|
||||
func (v *splashView) layoutLogo(t *tview.TextView) {
|
||||
logo := strings.Join(Logo, "\n[orange::b]")
|
||||
fmt.Fprintf(t, "%s[orange::b]%s\n", strings.Repeat("\n", 2), logo)
|
||||
logo := strings.Join(Logo, fmt.Sprintf("\n[%s::b]", v.app.styles.Style.LogoColor))
|
||||
fmt.Fprintf(t, "%s[%s::b]%s\n",
|
||||
strings.Repeat("\n", 2),
|
||||
v.app.styles.Style.LogoColor,
|
||||
logo)
|
||||
}
|
||||
|
||||
func (v *splashView) layoutRev(t *tview.TextView, rev string) {
|
||||
fmt.Fprintf(t, "[white::b]Revision [red::b]%s", rev)
|
||||
fmt.Fprintf(t, "[%s::b]Revision [red::b]%s", v.app.styles.Style.FgColor, rev)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/watch"
|
||||
)
|
||||
|
||||
const subjectTitleFmt = " [aqua::b]%s([fuchsia::b]%s[aqua::-])"
|
||||
|
||||
var subjectHeader = resource.Row{"NAME", "KIND", "FIRST LOCATION"}
|
||||
|
||||
type (
|
||||
|
|
@ -177,13 +175,11 @@ func (v *subjectView) reconcile() (resource.TableData, error) {
|
|||
if err != nil {
|
||||
return table, err
|
||||
}
|
||||
log.Debug().Msgf("Cluster evts %d", len(evts))
|
||||
|
||||
nevts, err := v.namespacedSubjects()
|
||||
if err != nil {
|
||||
return table, err
|
||||
}
|
||||
log.Debug().Msgf("NS evts %d", len(nevts))
|
||||
for k, v := range nevts {
|
||||
evts[k] = v
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import (
|
|||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/resource"
|
||||
"github.com/derailed/tview"
|
||||
"github.com/gdamore/tcell"
|
||||
|
|
@ -17,9 +17,9 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
titleFmt = " [aqua::b]%s[aqua::-][[fuchsia::b]%d[aqua::-]] "
|
||||
searchFmt = "<[green::b]/%s[aqua::]> "
|
||||
nsTitleFmt = " [aqua::b]%s([fuchsia::b]%s[aqua::-])[aqua::-][[aqua::b]%d[aqua::-]][aqua::-] "
|
||||
titleFmt = "[fg:bg:b] %s[fg:bg:-][[count:bg:b]%d[fg:bg:-]] "
|
||||
searchFmt = "<[filter:bg:b]/%s[fg:bg:]> "
|
||||
nsTitleFmt = "[fg:bg:b] %s([hilite:bg:b]%s[fg:bg:-])[fg:bg:-][[count:bg:b]%d[fg:bg:-]][fg:bg:-] "
|
||||
)
|
||||
|
||||
type (
|
||||
|
|
@ -38,7 +38,6 @@ type (
|
|||
app *appView
|
||||
baseTitle string
|
||||
currentNS string
|
||||
refreshMX sync.Mutex
|
||||
actions keyActions
|
||||
colorerFn colorerFn
|
||||
sortFn sortFn
|
||||
|
|
@ -46,7 +45,6 @@ type (
|
|||
data resource.TableData
|
||||
cmdBuff *cmdBuff
|
||||
sortBuff *cmdBuff
|
||||
tableMX sync.Mutex
|
||||
sortCol sortColumn
|
||||
}
|
||||
)
|
||||
|
|
@ -58,15 +56,20 @@ func newTableView(app *appView, title string) *tableView {
|
|||
v.actions = make(keyActions)
|
||||
v.SetFixed(1, 0)
|
||||
v.SetBorder(true)
|
||||
v.SetFixed(1, 0)
|
||||
v.SetBorderColor(tcell.ColorDodgerBlue)
|
||||
v.SetBackgroundColor(config.AsColor(app.styles.Style.Table.BgColor))
|
||||
v.SetBorderColor(config.AsColor(app.styles.Style.Table.FgColor))
|
||||
v.SetBorderFocusColor(config.AsColor(app.styles.Style.Border.FocusColor))
|
||||
v.SetBorderAttributes(tcell.AttrBold)
|
||||
v.SetBorderPadding(0, 0, 1, 1)
|
||||
v.cmdBuff = newCmdBuff('/')
|
||||
v.cmdBuff.addListener(app.cmdView)
|
||||
v.cmdBuff.reset()
|
||||
v.SetSelectable(true, false)
|
||||
v.SetSelectedStyle(tcell.ColorBlack, tcell.ColorAqua, tcell.AttrBold)
|
||||
v.SetSelectedStyle(
|
||||
tcell.ColorBlack,
|
||||
config.AsColor(app.styles.Style.Table.CursorColor),
|
||||
tcell.AttrBold,
|
||||
)
|
||||
v.SetInputCapture(v.keyboard)
|
||||
v.bindKeys()
|
||||
}
|
||||
|
|
@ -205,7 +208,7 @@ func (v *tableView) activateCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
return evt
|
||||
}
|
||||
|
||||
v.app.flash(flashInfo, "Filtering...")
|
||||
v.app.flash(flashInfo, "Filter mode activated.")
|
||||
v.cmdBuff.reset()
|
||||
v.cmdBuff.setActive(true)
|
||||
|
||||
|
|
@ -227,13 +230,13 @@ func (v *tableView) setColorer(f colorerFn) {
|
|||
|
||||
// SetActions sets up keyboard action listener.
|
||||
func (v *tableView) setActions(aa keyActions) {
|
||||
v.tableMX.Lock()
|
||||
{
|
||||
for k, a := range aa {
|
||||
v.actions[k] = a
|
||||
}
|
||||
// v.mx.Lock()
|
||||
// {
|
||||
for k, a := range aa {
|
||||
v.actions[k] = a
|
||||
}
|
||||
v.tableMX.Unlock()
|
||||
// }
|
||||
// v.mx.Unlock()
|
||||
}
|
||||
|
||||
// Hints options
|
||||
|
|
@ -251,16 +254,12 @@ func (v *tableView) refresh() {
|
|||
|
||||
// Update table content
|
||||
func (v *tableView) update(data resource.TableData) {
|
||||
v.refreshMX.Lock()
|
||||
{
|
||||
v.data = data
|
||||
if !v.cmdBuff.empty() {
|
||||
v.doUpdate(v.filtered())
|
||||
} else {
|
||||
v.doUpdate(data)
|
||||
}
|
||||
v.data = data
|
||||
if !v.cmdBuff.empty() {
|
||||
v.doUpdate(v.filtered())
|
||||
} else {
|
||||
v.doUpdate(v.data)
|
||||
}
|
||||
v.refreshMX.Unlock()
|
||||
v.resetTitle()
|
||||
}
|
||||
|
||||
|
|
@ -300,7 +299,7 @@ func (v *tableView) sortIndicator(index int, name string) string {
|
|||
if v.sortCol.asc {
|
||||
order = "↑"
|
||||
}
|
||||
return fmt.Sprintf("%s [green::]%s[::]", name, order)
|
||||
return fmt.Sprintf("%s [%s::]%s[::]", name, v.app.styles.Style.Table.Header.SorterColor, order)
|
||||
}
|
||||
|
||||
func (v *tableView) doUpdate(data resource.TableData) {
|
||||
|
|
@ -326,7 +325,11 @@ func (v *tableView) doUpdate(data resource.TableData) {
|
|||
}
|
||||
|
||||
pads := make(maxyPad, len(data.Header))
|
||||
// v.mx.Lock()
|
||||
// {
|
||||
computeMaxColumns(pads, v.sortCol.index, data)
|
||||
// }
|
||||
// v.mx.Unlock()
|
||||
var row int
|
||||
for col, h := range data.Header {
|
||||
v.addHeaderCell(col, h, pads)
|
||||
|
|
@ -340,7 +343,7 @@ func (v *tableView) doUpdate(data resource.TableData) {
|
|||
prim, sec := v.sortAllRows(data.Rows, sortFn)
|
||||
for _, pk := range prim {
|
||||
for _, sk := range sec[pk] {
|
||||
fgColor := tcell.ColorGray
|
||||
fgColor := config.AsColor(v.app.styles.Style.Table.FgColor)
|
||||
if v.colorerFn != nil {
|
||||
fgColor = v.colorerFn(data.Namespace, data.Rows[sk])
|
||||
}
|
||||
|
|
@ -381,7 +384,8 @@ func (v *tableView) addHeaderCell(col int, name string, pads maxyPad) {
|
|||
c := tview.NewTableCell(v.sortIndicator(col, name))
|
||||
{
|
||||
c.SetExpansion(1)
|
||||
c.SetTextColor(tcell.ColorAntiqueWhite)
|
||||
c.SetTextColor(config.AsColor(v.app.styles.Style.Table.Header.FgColor))
|
||||
c.SetBackgroundColor(config.AsColor(v.app.styles.Style.Table.Header.BgColor))
|
||||
}
|
||||
v.SetCell(0, col, c)
|
||||
}
|
||||
|
|
@ -438,17 +442,28 @@ func (v *tableView) resetTitle() {
|
|||
}
|
||||
switch v.currentNS {
|
||||
case resource.NotNamespaced, "*":
|
||||
title = fmt.Sprintf(titleFmt, v.baseTitle, rc)
|
||||
fmat := strings.Replace(titleFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
|
||||
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
|
||||
fmat = strings.Replace(fmat, "[count", "["+v.app.styles.Style.Title.CounterColor, 1)
|
||||
title = fmt.Sprintf(fmat, v.baseTitle, rc)
|
||||
default:
|
||||
ns := v.currentNS
|
||||
if v.currentNS == resource.AllNamespaces {
|
||||
ns = resource.AllNamespace
|
||||
}
|
||||
title = fmt.Sprintf(nsTitleFmt, v.baseTitle, ns, rc)
|
||||
fmat := strings.Replace(nsTitleFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, -1)
|
||||
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
|
||||
fmat = strings.Replace(fmat, "[hilite", "["+v.app.styles.Style.Title.HighlightColor, 1)
|
||||
fmat = strings.Replace(fmat, "[count", "["+v.app.styles.Style.Title.CounterColor, 1)
|
||||
title = fmt.Sprintf(fmat, v.baseTitle, ns, rc)
|
||||
}
|
||||
|
||||
if !v.cmdBuff.isActive() && !v.cmdBuff.empty() {
|
||||
title += fmt.Sprintf(searchFmt, v.cmdBuff)
|
||||
fmat := strings.Replace(searchFmt, "[fg", "["+v.app.styles.Style.Title.FgColor, 1)
|
||||
fmat = strings.Replace(fmat, ":bg:", ":"+v.app.styles.Style.Title.BgColor+":", -1)
|
||||
fmat = strings.Replace(fmat, "[filter", "["+v.app.styles.Style.Title.FilterColor, 1)
|
||||
|
||||
title += fmt.Sprintf(fmat, v.cmdBuff)
|
||||
}
|
||||
v.SetTitle(title)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -11,24 +13,39 @@ var (
|
|||
keyRX = regexp.MustCompile(`\A(\s*)([\w|\-|\.|\s]+):\s*\z`)
|
||||
)
|
||||
|
||||
func colorizeYAML(raw string) string {
|
||||
const (
|
||||
yamlFullFmt = "%s[key::b]%s[colon::-]: [val::]%s"
|
||||
yamlKeyFmt = "%s[key::b]%s[colon::-]:"
|
||||
yamlValueFmt = "[val::]%s"
|
||||
)
|
||||
|
||||
func colorizeYAML(style *config.Style, raw string) string {
|
||||
lines := strings.Split(raw, "\n")
|
||||
|
||||
fullFmt := strings.Replace(yamlFullFmt, "[key", "["+style.Yaml.KeyColor, 1)
|
||||
fullFmt = strings.Replace(fullFmt, "[colon", "["+style.Yaml.ColonColor, 1)
|
||||
fullFmt = strings.Replace(fullFmt, "[val", "["+style.Yaml.ValueColor, 1)
|
||||
|
||||
keyFmt := strings.Replace(yamlKeyFmt, "[key", "["+style.Yaml.KeyColor, 1)
|
||||
keyFmt = strings.Replace(keyFmt, "[colon", "["+style.Yaml.ColonColor, 1)
|
||||
|
||||
valFmt := strings.Replace(yamlValueFmt, "[val", "["+style.Yaml.ValueColor, 1)
|
||||
|
||||
buff := make([]string, 0, len(lines))
|
||||
for _, l := range lines {
|
||||
res := keyValRX.FindStringSubmatch(l)
|
||||
if len(res) == 4 {
|
||||
buff = append(buff, fmt.Sprintf("%s[steelblue::b]%s[white::-]: [papayawhip::]%s", res[1], res[2], res[3]))
|
||||
buff = append(buff, fmt.Sprintf(fullFmt, res[1], res[2], res[3]))
|
||||
continue
|
||||
}
|
||||
|
||||
res = keyRX.FindStringSubmatch(l)
|
||||
if len(res) == 3 {
|
||||
buff = append(buff, fmt.Sprintf("%s[steelblue::b]%s[white::-]:", res[1], res[2]))
|
||||
buff = append(buff, fmt.Sprintf(keyFmt, res[1], res[2]))
|
||||
continue
|
||||
}
|
||||
|
||||
buff = append(buff, fmt.Sprintf("[papayawhip::]%s", l))
|
||||
buff = append(buff, fmt.Sprintf(valFmt, l))
|
||||
}
|
||||
|
||||
return strings.Join(buff, "\n")
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package views
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
|
@ -40,7 +41,8 @@ func TestYaml(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
s, _ := config.NewStyles()
|
||||
for _, u := range uu {
|
||||
assert.Equal(t, u.e, colorizeYAML(u.s))
|
||||
assert.Equal(t, u.e, colorizeYAML(s.Style, u.s))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
k9s:
|
||||
fgColor: white
|
||||
bgColor: black
|
||||
logoColor: white
|
||||
info:
|
||||
fgColor: navajowhite
|
||||
sectionColor: white
|
||||
border:
|
||||
fgColor: white
|
||||
focusColor: white
|
||||
menu:
|
||||
fgColor: white
|
||||
keyColor: white
|
||||
numKeyColor: navajowhite
|
||||
crumb:
|
||||
fgColor: black
|
||||
bgColor: navajowhite
|
||||
activeColor: whitesmoke
|
||||
table:
|
||||
fgColor: white
|
||||
bgColor: black
|
||||
cursorColor: white
|
||||
header:
|
||||
fgColor: darkgray
|
||||
bgColor: black
|
||||
sorterColor: white
|
||||
status:
|
||||
newColor: ghostwhite
|
||||
modifyColor: navajowhite
|
||||
addColor: darkslategray
|
||||
errorColor: whitesmoke
|
||||
highlightcolor: dimgray
|
||||
killColor: slategray
|
||||
completedColor: gray
|
||||
title:
|
||||
fgColor: ghostwhite
|
||||
highlightColor: navajowhite
|
||||
counterColor: navajowhite
|
||||
filterColor: slategray
|
||||
yaml:
|
||||
keyColor: ghostwhite
|
||||
colorColor: slategray
|
||||
valueColor: navajowhite
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
k9s:
|
||||
fgColor: dodgerblue
|
||||
bgColor: white
|
||||
logoColor: blue
|
||||
info:
|
||||
fgColor: lightskyblue
|
||||
sectionColor: steelblue
|
||||
border:
|
||||
fgColor: dodgerblue
|
||||
focusColor: aliceblue
|
||||
menu:
|
||||
fgColor: darkblue
|
||||
keyColor: cornflowerblue
|
||||
numKeyColor: cadetblue
|
||||
crumb:
|
||||
fgColor: white
|
||||
bgColor: steelblue
|
||||
activeColor: skyblue
|
||||
table:
|
||||
fgColor: blue
|
||||
bgColor: darkblue
|
||||
cursorColor: aqua
|
||||
header:
|
||||
fgColor: white
|
||||
bgColor: darkblue
|
||||
sorterColor: orange
|
||||
status:
|
||||
newColor: blue
|
||||
modifyColor: powderblue
|
||||
addColor: lightskyblue
|
||||
errorColor: indianred
|
||||
highlightcolor: royalblue
|
||||
killColor: slategray
|
||||
completedColor: gray
|
||||
title:
|
||||
fgColor: aqua
|
||||
bgColor: white
|
||||
highlightColor: skyblue
|
||||
counterColor: slateblue
|
||||
filterColor: slategray
|
||||
yaml:
|
||||
keyColor: steelblue
|
||||
colorColor: blue
|
||||
valueColor: royalblue
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
k9s:
|
||||
fgColor: dodgerblue
|
||||
bgColor: black
|
||||
logoColor: orange
|
||||
info:
|
||||
fgColor: white
|
||||
sectionColor: dodgerblue
|
||||
border:
|
||||
fgColor: dodgerblue
|
||||
focusColor: aqua
|
||||
menu:
|
||||
fgColor: white
|
||||
keyColor: dodgerblue
|
||||
numKeyColor: fuchsia
|
||||
crumb:
|
||||
fgColor: black
|
||||
bgColor: steelblue
|
||||
activeColor: orange
|
||||
table:
|
||||
fgColor: blue
|
||||
bgColor: black
|
||||
cursorColor: aqua
|
||||
header:
|
||||
fgColor: aqua
|
||||
bgColor: black
|
||||
sorterColor: orange
|
||||
yaml:
|
||||
keyColor: steelblue
|
||||
colonColor: white
|
||||
valueColor: papayawhip
|
||||
Loading…
Reference in New Issue