├── .gitignore ├── README.md ├── load ├── load.widget.zip ├── screenshot.png ├── screenshot2.png ├── widget.json ├── README.md └── load.widget │ └── index.coffee ├── pingo ├── screenshot.png ├── pingo.widget.zip ├── widget.json ├── README.md └── pingo.widget │ └── index.coffee ├── cpumeter ├── screenshot.png ├── screenshot2.png ├── cpumeter.widget.zip ├── widget.json └── cpumeter.widget │ ├── ubercpumeter.rb │ └── cpumeter.coffee ├── inverter ├── screenshot.png ├── inverter.widget │ ├── togglejacket.applescript │ └── index.coffee └── README.md ├── loadbar ├── screenshot.png ├── screenshot2.png ├── loadbar.widget.zip ├── widget.json ├── README.md └── loadbar.widget │ └── index.coffee ├── memmeter ├── screenshot.png ├── screenshot2.png ├── memmeter.widget.zip ├── widget.json └── memmeter.widget │ ├── memmeter.coffee │ └── ubermemmeter.rb ├── loadchart ├── screenshot.png ├── screenshot2.png ├── loadchart.widget.zip ├── widget.json ├── README.md └── loadchart.widget │ └── index.coffee ├── loadpercent ├── screenshot.png ├── widget.json ├── README.md └── loadpercent.widget │ └── index.coffee └── timemachine ├── screenshot.png ├── timemachine.widget.zip ├── widget.json ├── README.md └── timemachine.widget └── index.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | # created by git-ignore 2 | *.sublime-* 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A couple few widgets for [Übersicht](http://tracesof.net/uebersicht/). 2 | -------------------------------------------------------------------------------- /load/load.widget.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/load/load.widget.zip -------------------------------------------------------------------------------- /load/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/load/screenshot.png -------------------------------------------------------------------------------- /load/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/load/screenshot2.png -------------------------------------------------------------------------------- /pingo/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/pingo/screenshot.png -------------------------------------------------------------------------------- /cpumeter/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/cpumeter/screenshot.png -------------------------------------------------------------------------------- /inverter/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/inverter/screenshot.png -------------------------------------------------------------------------------- /loadbar/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/loadbar/screenshot.png -------------------------------------------------------------------------------- /loadbar/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/loadbar/screenshot2.png -------------------------------------------------------------------------------- /memmeter/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/memmeter/screenshot.png -------------------------------------------------------------------------------- /pingo/pingo.widget.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/pingo/pingo.widget.zip -------------------------------------------------------------------------------- /cpumeter/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/cpumeter/screenshot2.png -------------------------------------------------------------------------------- /loadbar/loadbar.widget.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/loadbar/loadbar.widget.zip -------------------------------------------------------------------------------- /loadchart/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/loadchart/screenshot.png -------------------------------------------------------------------------------- /loadchart/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/loadchart/screenshot2.png -------------------------------------------------------------------------------- /loadpercent/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/loadpercent/screenshot.png -------------------------------------------------------------------------------- /memmeter/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/memmeter/screenshot2.png -------------------------------------------------------------------------------- /timemachine/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/timemachine/screenshot.png -------------------------------------------------------------------------------- /cpumeter/cpumeter.widget.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/cpumeter/cpumeter.widget.zip -------------------------------------------------------------------------------- /memmeter/memmeter.widget.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/memmeter/memmeter.widget.zip -------------------------------------------------------------------------------- /loadchart/loadchart.widget.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/loadchart/loadchart.widget.zip -------------------------------------------------------------------------------- /timemachine/timemachine.widget.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/ubersicht-widgets/HEAD/timemachine/timemachine.widget.zip -------------------------------------------------------------------------------- /cpumeter/widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CPUMeter", 3 | "description": "Displays top CPU processes.", 4 | "author": "Brett Terpstra", 5 | "email": "me+ubersicht@brettterpstra.com" 6 | } 7 | -------------------------------------------------------------------------------- /memmeter/widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MemMeter", 3 | "description": "Displays top RAM processes", 4 | "author": "Brett Terpstra", 5 | "email": "me+ubersicht@brettterpstra.com" 6 | } 7 | -------------------------------------------------------------------------------- /timemachine/widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TimeMachine", 3 | "description": "Displays Time Machine backup status.", 4 | "author": "Brett Terpstra", 5 | "email": "me+ubersicht@brettterpstra.com" 6 | } 7 | -------------------------------------------------------------------------------- /loadchart/widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LoadChart", 3 | "description": "Displays the 5m average load over time as a graph.", 4 | "author": "Brett Terpstra", 5 | "email": "me+ubersicht@brettterpstra.com" 6 | } 7 | -------------------------------------------------------------------------------- /load/widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Load", 3 | "description": "Displays the 5m average load as a large number with color coding.", 4 | "author": "Brett Terpstra", 5 | "email": "me+ubersicht@brettterpstra.com" 6 | } 7 | -------------------------------------------------------------------------------- /pingo/widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Pingo", 3 | "description": "Displays 10-ping average, packet loss, and chart of results over time.", 4 | "author": "Brett Terpstra", 5 | "email": "me+ubersicht@brettterpstra.com" 6 | } 7 | -------------------------------------------------------------------------------- /loadpercent/widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LoadPercent", 3 | "description": "Displays the 5m average load as a percentage with color coding.", 4 | "author": "Brett Terpstra", 5 | "email": "me+ubersicht@brettterpstra.com" 6 | } 7 | -------------------------------------------------------------------------------- /loadbar/widget.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Average load bar", 3 | "description": "Displays the 5m average load as a colored bar on the left side of the screen.", 4 | "author": "Brett Terpstra", 5 | "email": "me+ubersicht@brettterpstra.com" 6 | } 7 | -------------------------------------------------------------------------------- /inverter/inverter.widget/togglejacket.applescript: -------------------------------------------------------------------------------- 1 | tell application "Simplify" 2 | set _jacket to name of active jacket 3 | 4 | if _jacket is "Sidecar" or _jacket is "Sidecar13" then 5 | if active variation of jacket _jacket is "Absence of dark" then 6 | make active jacket _jacket with variation "Wallflower" 7 | else 8 | make active jacket _jacket with variation "Absence of dark" 9 | end if 10 | move jacket to {top:0.0, left:0.0} 11 | 12 | end if 13 | end tell -------------------------------------------------------------------------------- /pingo/README.md: -------------------------------------------------------------------------------- 1 | ## Pingo: average server ping times and chart 2 | 3 | A widget for [Übersicht](http://tracesof.net/uebersicht/) that Displays 10-ping average, packet loss, and chart of results over time. Packet loss turns red when over a defined threshold. 4 | 5 | ![](screenshot.png) 6 | 7 | ### Notes 8 | 9 | * Change the server as desired, defaults to google.com. 10 | * Defaults to 10 pings every 5 minutes. Adjust the refresh rate and `-c` value in the command to alter. 11 | -------------------------------------------------------------------------------- /load/README.md: -------------------------------------------------------------------------------- 1 | ## Average load display 2 | 3 | A widget for [Übersicht](http://tracesof.net/uebersicht/) that displays a large number showing the average system load. The decimal point in the number is animated and color coded based on load (low, normal, high, higher, highest) and pulses faster as load increases. An arrow to the left shows whether the load is increasing or decreasing. 4 | 5 | ![](screenshot2.png) 6 | 7 | ### Notes 8 | 9 | Adjust the refresh rate as desired. 10 | -------------------------------------------------------------------------------- /timemachine/README.md: -------------------------------------------------------------------------------- 1 | ## TimeMachine: Time Machine status and progress 2 | 3 | A widget for [Übersicht](http://tracesof.net/uebersicht/) that displays a progress meter for Time Machine. 4 | 5 | Animations and color coding for loading status, prep procedures, progress, and post procedures. 6 | 7 | ![](screenshot.png) 8 | 9 | ### Notes 10 | 11 | Changing the font size of the h1 will change the size of the widget. It will scale as large or as small as you like. 12 | 13 | The @keyframe definitions break if placed in the Stylus section, thus must be loaded in the render section as a ` 27 |

#{output}

28 | """ 29 | 30 | update: (output, domEl) -> 31 | 32 | storageKey = 'com.brettterpstra.storedLoadVal' 33 | 34 | val = parseFloat(output) 35 | $stat = $(domEl).find('h1') 36 | 37 | $stat.removeClass('highest higher high normal low') 38 | $stat.html(output.replace(/\./,'.')) 39 | 40 | if @settings.animation 41 | colorclass = switch 42 | when val > 6 then 'highest' 43 | when val > 4 then 'higher' 44 | when val > 2 then 'high' 45 | when val > 1 then 'normal' 46 | else 'low' 47 | $stat.find('i').addClass colorclass 48 | 49 | prevVal = parseFloat(localStorage.getItem(storageKey)) 50 | if prevVal > val 51 | $stat.prepend '' 52 | else 53 | $stat.prepend '' 54 | 55 | localStorage.setItem('com.brettterpstra.storedLoadVal', val) 56 | 57 | style: """ 58 | border none 59 | box-sizing border-box 60 | color #141f33 61 | font-family Avenir, Helvetica Neue 62 | font-weight 300 63 | line-height 1.5 64 | padding 0 65 | left 323px 66 | top 95px 67 | width 400px 68 | text-align justify 69 | 70 | h1 71 | font-size 100px 72 | font-weight 700 73 | margin 0 74 | line-height 1 75 | font-family Avenir 76 | color rgba(255,255,255,.35) 77 | transition all 1s ease-in-out 78 | 79 | i 80 | display inline-block 81 | text-shadow none 82 | font-style normal 83 | animation-direction alternate 84 | animation-timing-function ease-out 85 | 86 | b 87 | font-size 36px 88 | 89 | .low 90 | color rgba(255, 255, 255, .5) 91 | animation pulseOpacity 10s infinite 92 | 93 | .normal 94 | color rgba(243, 255, 134, .5) 95 | animation pulseOpacity 5s infinite 96 | 97 | .high 98 | color rgba(195, 147, 59, .75) 99 | animation pulseOpacity 3s infinite 100 | 101 | .higher 102 | color rgba(255, 141, 77, .75) 103 | animation pulse 2s infinite 104 | 105 | .highest 106 | color rgba(255, 71, 71, .8) 107 | animation pulse .6s infinite 108 | """ 109 | -------------------------------------------------------------------------------- /inverter/README.md: -------------------------------------------------------------------------------- 1 | ## Inverter 2 | 3 | A widget for [Übersicht](http://tracesof.net/uebersicht/) that can be used to allow toggling between light and dark (or any two style) modes. It provides an "inverted" class that you can use in widget styling to provide a secondary appearance. Handy if you want to use Übersicht in multiple spaces with different brightness desktop images. 4 | 5 | ![](screenshot.png) 6 | 7 | Here's a [YouTube video of Inverter in action](https://www.youtube.com/watch?v=EcD_3wirU6A&feature=youtu.be), in case you're trying to figure out if you'd actually care about this or not. 8 | 9 | ### How to use it 10 | 11 | Hold down your "interaction" modifier key (set in application Preferences) and hover over the semicircle in the corner of your screen. The bubble will expand to a page corner, and clicking it will apply the "inverted" class to the body tag of the invisible Ubersicht browser window. To get click/hover interaction started, you sometimes need to click the desktop while holding the modifier key. 12 | 13 | ### What it does 14 | 15 | It simply applies the class "inverted" to the body tag of the entire Ubersicht window, which you can then use to add alternate styling to your widgets. 16 | 17 | ### Making it work with widgets 18 | 19 | To style widgets to make use of this, just add a section with `body.inverted` as the parent of the elements you want to change, making sure to increase specificity as needed to override the default when the class is applied. 20 | 21 | In Stylus, a simple: 22 | 23 | body.inverted & 24 | -webkit-filter invert(100%) 25 | 26 | will usually do the trick, but you'll often want to compensate more manually for hue shifts and contrast issues (which can also be handled with -webkit-filter, but they can get RAM expensive). 27 | 28 | ### Additional configuration 29 | 30 | You can position the click target in any corner of the window by changing the classes on the `` element in the render method. The options are "top," "botom," "left," and "right" in any combination. The default is the bottom right corner of the screen: 31 | 32 | #{output} 33 | 34 | _Don't change the ID!_ 35 | 36 | ### Notes 37 | 38 | There's a commented line in the "on.click()" section of the afterRender method containing a "@run()" command. An AppleScript is also included in the widget folder (It's for toggling between two versions of my [Sidecar jacket](http://brettterpstra.com/projects/sidecar/) for [Simplify](http://mmth.us/simplify/)). This is just to demo what you can do with the click handler and a shell script, but it's nothing new. 39 | 40 | This thing needs some work, I know. It also needs a way to trigger automatically, but I think that would require having an API to access Übersicht from AppleScript, Lua, JavaScript for Automation or anything, and I don't think we have that. Do we? ... 41 | 42 | Here's [an 11MB animated gif "demo"](http://assets.brettterpstra.com.s3.amazonaws.com/inverter.gif) because that's how I roll. Actually, I made a mistake when sizing it but didn't feel like starting over. It's your bandwidth, man. 43 | -------------------------------------------------------------------------------- /loadpercent/loadpercent.widget/index.coffee: -------------------------------------------------------------------------------- 1 | settings: 2 | animation: true 3 | 4 | command: "echo $(sysctl -n vm.loadavg|awk '{print $2}')':'$(sysctl -n hw.logicalcpu_max)" 5 | 6 | refreshFrequency: 10000 7 | render: (output) -> """ 8 | 28 |

#{output}%

29 | """ 30 | 31 | update: (output, domEl) -> 32 | 33 | storageKey = 'com.brettterpstra.storedLoadVal' 34 | vals = output.split(/:/) 35 | cpu = parseFloat(vals[0]) 36 | cores = parseInt(vals[1], 10) 37 | val = cpu / cores * 100 38 | $stat = $(domEl).find('h1') 39 | 40 | $stat.removeClass('highest higher high normal low') 41 | $stat.html(Math.round(val) + "% (#{cpu.toFixed(2)})") 42 | 43 | if @settings.animation 44 | colorclass = switch 45 | when val > 200 then 'highest' 46 | when val > 125 then 'higher' 47 | when val > 75 then 'high' 48 | when val > 25 then 'normal' 49 | else 'low' 50 | $stat.find('i').addClass colorclass 51 | 52 | prevVal = parseFloat(localStorage.getItem(storageKey)) 53 | if prevVal > val 54 | $stat.prepend '' 55 | else 56 | $stat.prepend '' 57 | 58 | localStorage.setItem storageKey, val 59 | 60 | style: """ 61 | border none 62 | box-sizing border-box 63 | color #141f33 64 | font-family Avenir, Helvetica Neue 65 | font-weight 300 66 | line-height 1.5 67 | padding 0 68 | left 323px 69 | top 95px 70 | width 400px 71 | text-align justify 72 | 73 | h1 74 | font-size 100px 75 | font-weight 700 76 | margin 0 77 | line-height 1 78 | font-family Avenir 79 | color rgba(255,255,255,.35) 80 | transition all .3s ease-in-out 81 | 82 | i 83 | display inline-block 84 | text-shadow none 85 | font-style normal 86 | animation-direction alternate 87 | animation-timing-function ease-out 88 | 89 | b 90 | font-size 36px 91 | 92 | .low 93 | color rgba(255, 255, 255, .5) 94 | animation pulseOpacity 10s infinite 95 | 96 | .normal 97 | color rgba(243, 255, 134, .5) 98 | animation pulseOpacity 5s infinite 99 | 100 | .high 101 | color rgba(195, 147, 59, .75) 102 | animation pulseOpacity 3s infinite 103 | 104 | .higher 105 | color rgba(255, 141, 77, .75) 106 | animation pulse 2s infinite 107 | 108 | .highest 109 | color rgba(255, 71, 71, .8) 110 | animation pulse .6s infinite 111 | 112 | .actual 113 | font-size: 30px 114 | color: rgba(255,255,255,.5) 115 | 116 | body.inverted & 117 | -webkit-filter invert(100%) contrast(120%) 118 | h1 119 | transition all .3s ease-in-out 120 | color rgba(255,255,255,1) 121 | i 122 | -webkit-filter invert(100%) 123 | """ 124 | -------------------------------------------------------------------------------- /pingo/pingo.widget/index.coffee: -------------------------------------------------------------------------------- 1 | # Pingo by Brett Terpstra 2 | # Change the server as needed 3 | # Defaults to a 10-ping average run every 5 minutes 4 | command: "ping -c 10 199.19.85.141 | tail -n 2" 5 | refreshFrequency: 300000 6 | render: (output) -> """ 7 |
8 |

9 |

10 |
11 |
12 | """ 13 | 14 | update: (output, domEl) -> 15 | max_rows = 24 16 | $container = $(domEl).find '#pingo' 17 | $chart = $container.find '#chart' 18 | packet_loss_threshold = 0.2 19 | 20 | packet_loss = /(\d+\.\d+)% packet loss/.exec(output)[1] 21 | pings = /([\d\.]+)\/([\d\.]+)\/([\d\.]+)\/([\d\.]+)/.exec(output) 22 | ping_avg = pings[2] 23 | 24 | if parseFloat packet_loss > packet_loss_threshold 25 | colorclass = 'dropping' 26 | else 27 | colorclass = 'perfect' 28 | 29 | # If the chart container is empty, set up bars 30 | if $chart.find('.bar').length != max_rows 31 | $chart.empty() 32 | i = 0 33 | while i < max_rows 34 | $chart.append('
') 35 | i++ 36 | 37 | $container.find('h1').text ping_avg 38 | $container.find('h2').removeClass() 39 | $container.find('h2').addClass colorclass 40 | $container.find('h2').text packet_loss + '%' 41 | 42 | storageKey = 'com.brettterpstra.storedPingVal' 43 | 44 | totals = JSON.parse(localStorage.getItem(storageKey)) 45 | if totals == null 46 | totals = [] 47 | 48 | # Turn array strings into floating point values 49 | totals = totals.map (v, a, i) -> 50 | parseFloat(v) 51 | 52 | # Add the current load result to the load values array 53 | totals.push parseFloat ping_avg 54 | 55 | # Trim the array to match the number of bars we want 56 | totals = totals.slice(-max_rows) 57 | 58 | # Store the ammended array 59 | localStorage.setItem(storageKey, JSON.stringify(totals)) 60 | 61 | 62 | # Create a new array and sort it to determine min/max values 63 | sorted = totals.slice(0) 64 | sorted.sort (a,b) -> 65 | a-b 66 | max = sorted[sorted.length - 1] 67 | min = sorted[0] 68 | div = (max - min) / (1000 / max_rows) / .15 69 | 70 | i = 0 71 | while i < max_rows 72 | load = totals[i] 73 | value = ((load - min) / div) 74 | 75 | $bar = $($chart.find('.bar').get(i)) 76 | 77 | $bar.css 78 | paddingTop: value + 2 + '%' 79 | left: (i * 7.5) + 'px' 80 | i++ 81 | 82 | style: """ 83 | left 350px 84 | top 680px 85 | width 180px 86 | height 45px 87 | border-radius 5px 88 | font-family: Avenir, Helvetica 89 | border solid 1px rgba(#fff,.3) 90 | padding 5px 91 | 92 | h1, h2 93 | font-size 24px 94 | h1 95 | color rgba(#fff,.5) 96 | line-height 1 97 | margin 0 98 | padding 0 99 | float left 100 | h2 101 | color rgba(#aaa,.5) 102 | line-height 1 103 | margin 0 0 0 12px 104 | padding 0 0 0 12px 105 | float left 106 | border-left solid 2px rgba(#ccc,.2) 107 | &.dropping 108 | color rgba(#f18383, .8) 109 | 110 | #chart 111 | position absolute 112 | bottom 0 113 | left 0 114 | padding 6px 0 0 6px 115 | height 20px 116 | width 100% 117 | border-top solid 1px rgba(#fff,.5) 118 | box-sizing border-box 119 | 120 | .bar 121 | color rgba(#fff,.65) 122 | font-size 10px 123 | text-align center 124 | display block 125 | font-family Menlo 126 | border-left 4px solid white 127 | width 9px 128 | bottom 0 129 | padding 0 130 | line-height 1 131 | position absolute 132 | 133 | &.animated .bar 134 | -webkit-backface-visibility hidden; 135 | -webkit-perspective 1000; 136 | -webkit-transform translate3d(0, 0, 0); 137 | transition all 4s linear 138 | 139 | .bar 140 | opacity .4 141 | 142 | """ 143 | -------------------------------------------------------------------------------- /loadchart/loadchart.widget/index.coffee: -------------------------------------------------------------------------------- 1 | colors = 2 | low : "rgb(60, 160, 189)" 3 | normal : "rgb(88, 189, 60)" 4 | high : "rgb(243, 255, 134)" 5 | higher : "rgb(255, 168, 80)" 6 | highest: "rgb(255, 71, 71)" 7 | 8 | settings: 9 | background: true 10 | color : true 11 | brighter : false 12 | inverse : false 13 | bars : 100 14 | animated : true 15 | 16 | command: "sysctl -n vm.loadavg|awk '{print $2}'" 17 | 18 | refreshFrequency: 10000 19 | 20 | style: """ 21 | left 600px 22 | top 85px 23 | width 315px 24 | height 90px 25 | line-height: @height 26 | border-radius 5px 27 | 28 | &.bg 29 | background rgba(0,0,0,.25) 30 | 31 | &.inverse 32 | &.bg 33 | background rgba(255,255,255,.25) 34 | .bar 35 | border-left 3px solid rgb(0,0,0) 36 | 37 | #chartcontainer 38 | position absolute 39 | bottom 0 40 | width 100% 41 | height 100% 42 | left 10px 43 | font-size 0 44 | transform-origin 50% 100% 45 | overflow hidden 46 | 47 | .bar 48 | color rgba(255,255,255,.65) 49 | display inline-block 50 | vertical-align bottom 51 | border-left 3px solid rgb(255,255,255) 52 | width 2px 53 | padding 0 54 | height 1px 55 | transform-origin 50% 100% 56 | 57 | &.animated .bar 58 | -webkit-backface-visibility hidden; 59 | -webkit-perspective 1000; 60 | -webkit-transform translate3d(0, 0, 0); 61 | transition all 500ms linear 62 | 63 | color-low = #{colors.low} 64 | color-normal = #{colors.normal} 65 | color-high = #{colors.high} 66 | color-important = #{colors.higher} 67 | color-urgent = #{colors.highest} 68 | 69 | &.color 70 | .low 71 | border-left-color color-low 72 | 73 | .normal 74 | border-left-color color-normal 75 | 76 | .high 77 | border-left-color color-high 78 | 79 | .important 80 | border-left-color color-important 81 | 82 | .urgent 83 | border-left-color color-urgent 84 | 85 | .bar 86 | opacity .4 87 | 88 | &.brighter .bar 89 | opacity 1 90 | """ 91 | 92 | 93 | render: (output) -> """ 94 |
95 | """ 96 | 97 | afterRender: (domEl) -> 98 | # clean old storage 99 | localStorage.removeItem('com.brettterpstra.loadArray2') 100 | 101 | el = $(domEl) 102 | @$chart = $(domEl).find('#chartcontainer') 103 | @chartHeight = @$chart.height() - 10 # leave some padding at the top 104 | 105 | el.addClass('bg') if @settings.background 106 | el.addClass('animated') if @settings.animated 107 | el.addClass('color') if @settings.color 108 | el.addClass('brighter') if @settings.brighter 109 | el.addClass('inverse') if @settings.inverse 110 | 111 | el.css width: @settings.bars * 5 + 18 112 | 113 | 114 | update: (output, domEl) -> 115 | # figure out new max load 116 | load = parseFloat output.replace(/\n/g,'') 117 | max = Math.max.apply(Math, @loads) 118 | max = Math.max load, max 119 | 120 | # resize all bars if necessary 121 | bars = @$chart.children() 122 | if max != @prevMax 123 | @setBarHeight(bar, @loads[i], max) for bar, i in bars 124 | @prevMax = max 125 | 126 | # store current load 127 | @loads.push load 128 | 129 | # create new bar 130 | ($bar = $('
')) 131 | .addClass @colorClass(load) 132 | 133 | # remove old bars and loads 134 | if bars.length >= @settings.bars 135 | @loads.shift() 136 | $(bars[0]).remove() 137 | 138 | # render 139 | @$chart.append $bar 140 | requestAnimationFrame => 141 | @setBarHeight($bar[0], load, max) 142 | 143 | colorClass: (load) -> 144 | switch 145 | when load > 10 then 'urgent' 146 | when load > 7 then 'important' 147 | when load > 4 then 'high' 148 | when load > 2 then 'normal' 149 | else 'low' 150 | 151 | setBarHeight: (bar, load, max) -> 152 | bar.style.webkitTransform = "scale(1, #{@chartHeight * load / max})" 153 | 154 | loads: [] 155 | prevMax: null 156 | -------------------------------------------------------------------------------- /timemachine/timemachine.widget/index.coffee: -------------------------------------------------------------------------------- 1 | command: "/usr/bin/tmutil status" 2 | 3 | refreshFrequency: 5000 4 | 5 | render: (output) -> """ 6 | 41 |

Y

42 |

43 | """ 44 | 45 | update: (output, domEl) -> 46 | parsePlist = (input) -> 47 | ALPHABET = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"] 48 | if /Running = 1;/.test input 49 | raw_percent = /\s+Percent = "?(\d(?:\.\d+)?)"?;/.exec input 50 | if raw_percent 51 | percent = parseInt ((parseFloat (raw_percent[1] * 100)) / 2) 52 | [2,"#{ALPHABET[percent]}"] 53 | else 54 | backup_phase = /BackupPhase = (.*?);/.exec input 55 | if /MountingBackupVol/.test backup_phase[1] 56 | [-1,""] 57 | else if /(Starting|ThinningPreBackup)/.test backup_phase[1] 58 | [1,"b"] 59 | else if /ThinningPostBackup/.test backup_phase[1] 60 | [3,"b"] 61 | else 62 | [0,""] 63 | else 64 | [0,""] 65 | 66 | if output.length 67 | data = parsePlist(output) 68 | $all = $(domEl).find('h1') 69 | $background = $(domEl).find('.background') 70 | $progress = $(domEl).find('.progress') 71 | 72 | $progress.text data[1] 73 | if data[0] == -1 74 | $background.addClass 'prepping' 75 | else if data[0] == 1 76 | $background.removeClass 'prepping' 77 | $background.addClass 'starting' 78 | $all.addClass 'spinning' 79 | else if data[0] == 2 80 | $all.removeClass 'prepping starting spinning' 81 | $all.addClass 'running' 82 | else if data[0] == 3 83 | $background.addClass 'finishing' 84 | $all.addClass 'spinning' 85 | else if data[0] == 0 86 | $all.removeClass 'prepping running starting finishing spinning' 87 | 88 | style: """ 89 | border none 90 | box-sizing border-box 91 | color #141f33 92 | font-family Helvetica Neue 93 | font-weight 300 94 | line-height 1.5 95 | padding 0 96 | left 20px 97 | top 30px 98 | height auto 99 | opacity 1 100 | 101 | @font-face 102 | font-family 'arcfontregular' 103 | src url("data:font/woff;base64,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") format("woff") 104 | font-weight normal 105 | font-style normal 106 | 107 | h1 108 | line-height 1 109 | position absolute 110 | font-size 100px 111 | font-weight 700 112 | margin 0 113 | // TODO: Centering on the circle outlines (borders) [github.com/ttscoff/ubersicht-widgets/issues/1] 114 | padding .026em 0 0 .033em 115 | font-family arcfontregular 116 | color rgba(255,255,255, .1) 117 | animation-direction alternate 118 | border solid 1px rgba(255,255,255, 0) 119 | border-radius 100% 120 | &.spinning 121 | animation-direction reverse 122 | animation-timing-function linear !important 123 | animation spinning 2s infinite 124 | &.background 125 | border solid 1px rgba(255,255,255, .2) 126 | border-radius 100px 127 | &.prepping 128 | border-color rgba(#c33b3b, .5) 129 | box-shadow 0 0 15px rgba(#c33b3b, .35) 130 | animation-timing-function linear 131 | animation prepping 3s infinite 132 | &.starting 133 | border-color rgba(#ce9a54,.5) 134 | box-shadow 0 0 15px rgba(#ce9a54,.5) 135 | animation-timing-function ease !important 136 | &.running 137 | border-color rgba(96, 210, 255, .5) 138 | box-shadow 0 0 8px rgba(96, 210, 255, .4) 139 | &.finishing 140 | border-color rgba(96, 255, 137, .5) 141 | box-shadow 0 0 15px rgba(96, 255, 137, .35) 142 | &.spinning 143 | color: rgba(255,255,255,0) 144 | &.starting 145 | animation-timing-function ease-in-out 146 | animation starting 2s infinite 147 | &.running 148 | animation-timing-function ease-out 149 | animation running 4s infinite 150 | 151 | """ 152 | --------------------------------------------------------------------------------