diff --git a/docs/index.xml b/docs/index.xml
index 2b3ff365..ff336a7b 100644
--- a/docs/index.xml
+++ b/docs/index.xml
@@ -6,11 +6,29 @@
Recent content on WTF - A Terminal DashboardHugo -- gohugo.ioen-us
- Sat, 02 Jun 2018 05:32:04 -0700
+ Sun, 03 Jun 2018 20:06:40 -0700
+
+ Cryptolive
+ https://wtfutil.com/posts/modules/cryptocurrencies/cryptolive/
+ Sun, 03 Jun 2018 20:06:40 -0700
+
+ https://wtfutil.com/posts/modules/cryptocurrencies/cryptolive/
+ Added in v0.0.5.
+Compare crypto currencies using CryptoCompare.
+Source Code wtf/cryptocurrencies/cryptolive/ Required ENV Vars None.
+Keyboard Commands None.
+Configuration cryptolive:colors:from:name:coraldisplayName:greyto:name:whiteprice:greencurrencies:BTC:displayName:Bitcointo:-USD-EUR-ETHETH:displayName:Ethereumto:-USD-EUR-ETHenabled:trueposition:top:5left:2height:1width:2refreshInterval:30updateInterval:15 Attributes colors.from.name Values: Any X11 color name.
+colors.from.dispayName Values: Any X11 color name.
+colors.to.name Values: Any X11 color name.
+colors.to.price Values: Any X11 color name.
+currencies enabled Determines whether or not this module is executed and if its data displayed onscreen. Values: true, false.
+position Defines where in the grid this module’s widget will be displayed.
+
+
Prettyweather
https://wtfutil.com/posts/modules/prettyweather/
diff --git a/docs/posts/configuration/attributes/index.html b/docs/posts/configuration/attributes/index.html
index 76a1a2cb..471b05aa 100644
--- a/docs/posts/configuration/attributes/index.html
+++ b/docs/posts/configuration/attributes/index.html
@@ -65,6 +65,7 @@
Prettyweather
diff --git a/docs/posts/index.xml b/docs/posts/index.xml
index 21b14d1e..89b03cfe 100644
--- a/docs/posts/index.xml
+++ b/docs/posts/index.xml
@@ -6,11 +6,29 @@
Recent content in Posts on WTF - A Terminal DashboardHugo -- gohugo.ioen-us
- Sat, 02 Jun 2018 05:32:04 -0700
+ Sun, 03 Jun 2018 20:06:40 -0700
+
+ Cryptolive
+ https://wtfutil.com/posts/modules/cryptocurrencies/cryptolive/
+ Sun, 03 Jun 2018 20:06:40 -0700
+
+ https://wtfutil.com/posts/modules/cryptocurrencies/cryptolive/
+ Added in v0.0.5.
+Compare crypto currencies using CryptoCompare.
+Source Code wtf/cryptocurrencies/cryptolive/ Required ENV Vars None.
+Keyboard Commands None.
+Configuration cryptolive:colors:from:name:coraldisplayName:greyto:name:whiteprice:greencurrencies:BTC:displayName:Bitcointo:-USD-EUR-ETHETH:displayName:Ethereumto:-USD-EUR-ETHenabled:trueposition:top:5left:2height:1width:2refreshInterval:30updateInterval:15 Attributes colors.from.name Values: Any X11 color name.
+colors.from.dispayName Values: Any X11 color name.
+colors.to.name Values: Any X11 color name.
+colors.to.price Values: Any X11 color name.
+currencies enabled Determines whether or not this module is executed and if its data displayed onscreen. Values: true, false.
+position Defines where in the grid this module’s widget will be displayed.
+
+
Prettyweather
https://wtfutil.com/posts/modules/prettyweather/
diff --git a/docs/posts/installation/index.html b/docs/posts/installation/index.html
index 3b10455f..2e247ca7 100644
--- a/docs/posts/installation/index.html
+++ b/docs/posts/installation/index.html
@@ -65,6 +65,7 @@
diff --git a/docs/index.xml b/docs/index.xml
index 2b3ff365..ff336a7b 100644
--- a/docs/index.xml
+++ b/docs/index.xml
@@ -6,11 +6,29 @@
Recent content on WTF - A Terminal DashboardHugo -- gohugo.ioen-us
- Sat, 02 Jun 2018 05:32:04 -0700
+ Sun, 03 Jun 2018 20:06:40 -0700
+
+ Cryptolive
+ https://wtfutil.com/posts/modules/cryptocurrencies/cryptolive/
+ Sun, 03 Jun 2018 20:06:40 -0700
+
+ https://wtfutil.com/posts/modules/cryptocurrencies/cryptolive/
+ Added in v0.0.5.
+Compare crypto currencies using CryptoCompare.
+Source Code wtf/cryptocurrencies/cryptolive/ Required ENV Vars None.
+Keyboard Commands None.
+Configuration cryptolive:colors:from:name:coraldisplayName:greyto:name:whiteprice:greencurrencies:BTC:displayName:Bitcointo:-USD-EUR-ETHETH:displayName:Ethereumto:-USD-EUR-ETHenabled:trueposition:top:5left:2height:1width:2refreshInterval:30updateInterval:15 Attributes colors.from.name Values: Any X11 color name.
+colors.from.dispayName Values: Any X11 color name.
+colors.to.name Values: Any X11 color name.
+colors.to.price Values: Any X11 color name.
+currencies enabled Determines whether or not this module is executed and if its data displayed onscreen. Values: true, false.
+position Defines where in the grid this module’s widget will be displayed.
+
+
Prettyweather
https://wtfutil.com/posts/modules/prettyweather/
diff --git a/docs/posts/configuration/attributes/index.html b/docs/posts/configuration/attributes/index.html
index 76a1a2cb..471b05aa 100644
--- a/docs/posts/configuration/attributes/index.html
+++ b/docs/posts/configuration/attributes/index.html
@@ -65,6 +65,7 @@
Prettyweather
diff --git a/docs/posts/index.xml b/docs/posts/index.xml
index 21b14d1e..89b03cfe 100644
--- a/docs/posts/index.xml
+++ b/docs/posts/index.xml
@@ -6,11 +6,29 @@
Recent content in Posts on WTF - A Terminal DashboardHugo -- gohugo.ioen-us
- Sat, 02 Jun 2018 05:32:04 -0700
+ Sun, 03 Jun 2018 20:06:40 -0700
+
+ Cryptolive
+ https://wtfutil.com/posts/modules/cryptocurrencies/cryptolive/
+ Sun, 03 Jun 2018 20:06:40 -0700
+
+ https://wtfutil.com/posts/modules/cryptocurrencies/cryptolive/
+ Added in v0.0.5.
+Compare crypto currencies using CryptoCompare.
+Source Code wtf/cryptocurrencies/cryptolive/ Required ENV Vars None.
+Keyboard Commands None.
+Configuration cryptolive:colors:from:name:coraldisplayName:greyto:name:whiteprice:greencurrencies:BTC:displayName:Bitcointo:-USD-EUR-ETHETH:displayName:Ethereumto:-USD-EUR-ETHenabled:trueposition:top:5left:2height:1width:2refreshInterval:30updateInterval:15 Attributes colors.from.name Values: Any X11 color name.
+colors.from.dispayName Values: Any X11 color name.
+colors.to.name Values: Any X11 color name.
+colors.to.price Values: Any X11 color name.
+currencies enabled Determines whether or not this module is executed and if its data displayed onscreen. Values: true, false.
+position Defines where in the grid this module’s widget will be displayed.
+
+
Prettyweather
https://wtfutil.com/posts/modules/prettyweather/
diff --git a/docs/posts/installation/index.html b/docs/posts/installation/index.html
index 3b10455f..2e247ca7 100644
--- a/docs/posts/installation/index.html
+++ b/docs/posts/installation/index.html
@@ -65,6 +65,7 @@
From c14902abb6ff9b7c67c3ff94941e48143235e379 Mon Sep 17 00:00:00 2001
From: Chris Cummer
Date: Sun, 3 Jun 2018 20:22:40 -0700
Subject: [PATCH 16/19] Tweak the formatting of the CryptoLive module a bit
---
cryptoexchanges/cryptolive/widget.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cryptoexchanges/cryptolive/widget.go b/cryptoexchanges/cryptolive/widget.go
index b7f76103..c45cf0b1 100644
--- a/cryptoexchanges/cryptolive/widget.go
+++ b/cryptoexchanges/cryptolive/widget.go
@@ -98,7 +98,7 @@ func display(widget *Widget) {
toPriceColor = Config.UString("wtf.mods.cryptolive.colors.to.price", "green")
)
for _, item := range widget.list.items {
- str += fmt.Sprintf("[%s]%s[%s](%s):\n", fromNameColor, item.displayName, fromDisplayNameColor, item.name)
+ str += fmt.Sprintf(" [%s]%s[%s] (%s)\n", fromNameColor, item.displayName, fromDisplayNameColor, item.name)
for _, toItem := range item.to {
str += fmt.Sprintf("\t[%s]%s: [%s]%f\n", toNameColor, toItem.name, toPriceColor, toItem.price)
}
@@ -116,7 +116,7 @@ func getToList(fromName string) []*toCurrency {
for _, to := range toNames {
toList = append(toList, &toCurrency{
name: to.(string),
- price: -1,
+ price: 0,
})
}
From 218b6937b13e6da801e22913da1ece900cfcd3ee Mon Sep 17 00:00:00 2001
From: Chris Cummer
Date: Sun, 3 Jun 2018 20:54:07 -0700
Subject: [PATCH 17/19] Implement ASCII-only widget titles
Widget titles can now be specified in the config file via a 'title' key.
Example:
wtf:
mods:
todo:
title: Tada
which can include emoji. No need to force everyone to look at my emoji,
now they can define their own.
---
bamboohr/widget.go | 4 ++--
clocks/widget.go | 2 +-
cmdrunner/widget.go | 2 +-
cryptoexchanges/cryptolive/widget.go | 3 +--
gcal/widget.go | 2 +-
git/display.go | 2 +-
github/display.go | 2 +-
jira/widget.go | 6 +++---
newrelic/widget.go | 2 +-
opsgenie/widget.go | 4 ++--
power/widget.go | 2 +-
prettyweather/widget.go | 3 +--
security/widget.go | 2 +-
status/widget.go | 2 +-
system/widget.go | 2 +-
textfile/widget.go | 4 ++--
todo/widget.go | 2 +-
17 files changed, 22 insertions(+), 24 deletions(-)
diff --git a/bamboohr/widget.go b/bamboohr/widget.go
index a2b5f443..5876f567 100644
--- a/bamboohr/widget.go
+++ b/bamboohr/widget.go
@@ -16,7 +16,7 @@ type Widget struct {
func NewWidget() *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" ð― BambooHR ", "bamboohr", false),
+ TextWidget: wtf.NewTextWidget(" BambooHR ", "bamboohr", false),
}
return &widget
@@ -37,7 +37,7 @@ func (widget *Widget) Refresh() {
)
widget.UpdateRefreshedAt()
- widget.View.SetTitle(fmt.Sprintf(" ð― Away (%d) ", len(todayItems)))
+ widget.View.SetTitle(fmt.Sprintf("%s(%d)", widget.Name, len(todayItems)))
widget.View.SetText(fmt.Sprintf("%s", widget.contentFrom(todayItems)))
}
diff --git a/clocks/widget.go b/clocks/widget.go
index 3dff6cae..0e1a7fdc 100644
--- a/clocks/widget.go
+++ b/clocks/widget.go
@@ -18,7 +18,7 @@ type Widget struct {
func NewWidget() *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" ð World Clocks ", "clocks", false),
+ TextWidget: wtf.NewTextWidget(" World Clocks ", "clocks", false),
}
widget.clockColl = widget.buildClockCollection(Config.UMap("wtf.mods.clocks.locations"))
diff --git a/cmdrunner/widget.go b/cmdrunner/widget.go
index bb25dac6..3c80ae5a 100644
--- a/cmdrunner/widget.go
+++ b/cmdrunner/widget.go
@@ -22,7 +22,7 @@ type Widget struct {
func NewWidget() *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" ð Runner ", "cmdrunner", false),
+ TextWidget: wtf.NewTextWidget(" CmdRunner ", "cmdrunner", false),
args: wtf.ToStrs(Config.UList("wtf.mods.cmdrunner.args")),
cmd: Config.UString("wtf.mods.cmdrunner.cmd"),
diff --git a/cryptoexchanges/cryptolive/widget.go b/cryptoexchanges/cryptolive/widget.go
index c45cf0b1..e59eef96 100644
--- a/cryptoexchanges/cryptolive/widget.go
+++ b/cryptoexchanges/cryptolive/widget.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"net/http"
- "reflect"
"time"
"github.com/olebedev/config"
@@ -32,7 +31,7 @@ type Widget struct {
func NewWidget() *Widget {
started = false
widget := Widget{
- TextWidget: wtf.NewTextWidget(" $ CryptoLive ", "cryptolive", false),
+ TextWidget: wtf.NewTextWidget(" CryptoLive ", "cryptolive", false),
updateInterval: Config.UInt("wtf.mods.cryptolive.updateInterval", 10),
}
diff --git a/gcal/widget.go b/gcal/widget.go
index 6e2df90c..3b343627 100644
--- a/gcal/widget.go
+++ b/gcal/widget.go
@@ -20,7 +20,7 @@ type Widget struct {
func NewWidget() *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" ðŋ Calendar ", "gcal", false),
+ TextWidget: wtf.NewTextWidget(" Calendar ", "gcal", false),
}
return &widget
diff --git a/git/display.go b/git/display.go
index e809224f..8140615b 100644
--- a/git/display.go
+++ b/git/display.go
@@ -17,7 +17,7 @@ func (widget *Widget) display() {
}
title := fmt.Sprintf("[green]%s[white]\n", repoData.Repository)
- widget.View.SetTitle(fmt.Sprintf(" Git: %s ", title))
+ widget.View.SetTitle(fmt.Sprintf("%s- %s", widget.Name, title))
str := wtf.SigilStr(len(widget.Data), widget.Idx, widget.View) + "\n"
str = str + " [red]Branch[white]\n"
diff --git a/github/display.go b/github/display.go
index 1d35ef1b..e9346367 100644
--- a/github/display.go
+++ b/github/display.go
@@ -14,7 +14,7 @@ func (widget *Widget) display() {
return
}
- widget.View.SetTitle(fmt.Sprintf(" Github: %s ", widget.title(repo)))
+ widget.View.SetTitle(fmt.Sprintf("%s- %s", widget.Name, widget.title(repo)))
str := wtf.SigilStr(len(widget.GithubRepos), widget.Idx, widget.View) + "\n"
str = str + " [red]Stats[white]\n"
diff --git a/jira/widget.go b/jira/widget.go
index ebe85237..d9fe19f1 100644
--- a/jira/widget.go
+++ b/jira/widget.go
@@ -16,7 +16,7 @@ type Widget struct {
func NewWidget() *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget("JIRA", "jira", false),
+ TextWidget: wtf.NewTextWidget(" Jira ", "jira", false),
}
return &widget
@@ -35,13 +35,13 @@ func (widget *Widget) Refresh() {
if err != nil {
widget.View.SetWrap(true)
- widget.View.SetTitle(fmt.Sprintf(" %s ", widget.Name))
+ widget.View.SetTitle(fmt.Sprintf("%s", widget.Name))
fmt.Fprintf(widget.View, "%v", err)
} else {
widget.View.SetWrap(false)
widget.View.SetTitle(
fmt.Sprintf(
- " %s: [green]%s[white] ",
+ "%s- [green]%s[white]",
widget.Name,
Config.UString("wtf.mods.jira.project"),
),
diff --git a/newrelic/widget.go b/newrelic/widget.go
index 61a72a95..2831ea08 100644
--- a/newrelic/widget.go
+++ b/newrelic/widget.go
@@ -39,7 +39,7 @@ func (widget *Widget) Refresh() {
}
widget.UpdateRefreshedAt()
- widget.View.SetTitle(fmt.Sprintf(" New Relic: [green]%s[white] ", appName))
+ widget.View.SetTitle(fmt.Sprintf("%s- [green]%s[white]", widget.Name, appName))
widget.View.Clear()
if depErr != nil {
diff --git a/opsgenie/widget.go b/opsgenie/widget.go
index 1089c918..bf72c006 100644
--- a/opsgenie/widget.go
+++ b/opsgenie/widget.go
@@ -17,7 +17,7 @@ type Widget struct {
func NewWidget() *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" â° OpsGenie ", "opsgenie", false),
+ TextWidget: wtf.NewTextWidget(" OpsGenie ", "opsgenie", false),
}
return &widget
@@ -33,7 +33,7 @@ func (widget *Widget) Refresh() {
data, err := Fetch()
widget.UpdateRefreshedAt()
- widget.View.SetTitle(" â° On Call ")
+ widget.View.SetTitle(widget.Name)
if err != nil {
widget.View.SetWrap(true)
diff --git a/power/widget.go b/power/widget.go
index bd7a3652..dc98d6ae 100644
--- a/power/widget.go
+++ b/power/widget.go
@@ -18,7 +18,7 @@ type Widget struct {
func NewWidget() *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" âĄïļ Power ", "power", false),
+ TextWidget: wtf.NewTextWidget(" Power ", "power", false),
Battery: NewBattery(),
}
diff --git a/prettyweather/widget.go b/prettyweather/widget.go
index d4e237ea..6b838c69 100644
--- a/prettyweather/widget.go
+++ b/prettyweather/widget.go
@@ -22,7 +22,7 @@ type Widget struct {
func NewWidget() *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget("Pretty Weather", "prettyweather", false),
+ TextWidget: wtf.NewTextWidget(" Pretty Weather ", "prettyweather", false),
}
return &widget
@@ -35,7 +35,6 @@ func (widget *Widget) Refresh() {
widget.UpdateRefreshedAt()
widget.prettyWeather()
- widget.View.SetTitle(fmt.Sprintf(" %s ", widget.Name))
widget.View.SetText(fmt.Sprintf("%s", widget.result))
}
diff --git a/security/widget.go b/security/widget.go
index 2ec6ba3f..a6161fd9 100644
--- a/security/widget.go
+++ b/security/widget.go
@@ -17,7 +17,7 @@ type Widget struct {
func NewWidget() *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" ðĪš Security ", "security", false),
+ TextWidget: wtf.NewTextWidget(" Security ", "security", false),
}
return &widget
diff --git a/status/widget.go b/status/widget.go
index 83d2861e..6550fe5f 100644
--- a/status/widget.go
+++ b/status/widget.go
@@ -18,7 +18,7 @@ type Widget struct {
func NewWidget() *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" ð Status ", "status", false),
+ TextWidget: wtf.NewTextWidget(" Status ", "status", false),
CurrentIcon: 0,
}
diff --git a/system/widget.go b/system/widget.go
index 6da695f8..29f9efd0 100644
--- a/system/widget.go
+++ b/system/widget.go
@@ -21,7 +21,7 @@ type Widget struct {
func NewWidget(date, version string) *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" Build ", "system", false),
+ TextWidget: wtf.NewTextWidget(" System ", "system", false),
Date: date,
Version: version,
diff --git a/textfile/widget.go b/textfile/widget.go
index 12f2a7fa..d581dc4f 100644
--- a/textfile/widget.go
+++ b/textfile/widget.go
@@ -30,7 +30,7 @@ type Widget struct {
func NewWidget(app *tview.Application, pages *tview.Pages) *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" ð Text File ", "textfile", true),
+ TextWidget: wtf.NewTextWidget(" Text File ", "textfile", true),
app: app,
filePath: Config.UString("wtf.mods.textfile.filePath"),
@@ -53,7 +53,7 @@ func (widget *Widget) Refresh() {
}
widget.UpdateRefreshedAt()
- widget.View.SetTitle(fmt.Sprintf(" ð %s ", widget.filePath))
+ widget.View.SetTitle(fmt.Sprintf("%s %s", widget.Name, widget.filePath))
filePath, _ := wtf.ExpandHomeDir(widget.filePath)
diff --git a/todo/widget.go b/todo/widget.go
index df66bac4..f0b15503 100644
--- a/todo/widget.go
+++ b/todo/widget.go
@@ -44,7 +44,7 @@ type Widget struct {
func NewWidget(app *tview.Application, pages *tview.Pages) *Widget {
widget := Widget{
- TextWidget: wtf.NewTextWidget(" ð Todo ", "todo", true),
+ TextWidget: wtf.NewTextWidget(" Todo ", "todo", true),
app: app,
filePath: Config.UString("wtf.mods.todo.filename"),
From 64f8dbcf8d42bc4a32bf9314f99914bd824ba534 Mon Sep 17 00:00:00 2001
From: Chris Cummer
Date: Mon, 4 Jun 2018 04:20:17 -0700
Subject: [PATCH 18/19] Update Configuration documentation with an explanation
of the grid layout system
---
_site/content/posts/configuration.md | 27 +++++++++++++++++++++++++++
docs/posts/configuration/index.html | 26 ++++++++++++++++++++++++++
2 files changed, 53 insertions(+)
diff --git a/_site/content/posts/configuration.md b/_site/content/posts/configuration.md
index 183f0119..46e9e03a 100644
--- a/_site/content/posts/configuration.md
+++ b/_site/content/posts/configuration.md
@@ -51,3 +51,30 @@ wouldn't want to have laying about in the config files.
For modules that require them, the name of the required environment
variable(s) can be found in that module's "Required ENV Variables"
section of the documentation. See OpsGenie for an example.
+
+## Grid Layout
+
+WTF uses the `Grid` layout system from [tview](https://github.com/rivo/tview/blob/master/grid.go) to position widgets
+onscreen. It's not immediately obvious how this works, so here's an
+explanation:
+
+Think of your terminal screen as a matrix of letter positions, say `100` chrs wide and `58` chrs tall.
+
+Columns breaks up the width of the screen into chunks, each chunk a specified number of characters wide. use
+
+`[10, 10, 10, 10, 10, 10, 10, 10, 10, 10]`
+
+Ten columns that are ten characters wide
+
+Rows break up the height of the screen into chunks, each chunk a specified number of characters tall. If we wanted to have five rows:
+
+`[10, 10, 10, 10, 18]`
+
+The co-ordinate system starts at top-left and defines how wide and tall a widget is. If we wanted to put a 2-col, 2-row widget in the bottom of the screen, we'd position it at:
+
+```
+ top: 4 // top starts in the 4th row
+ left: 9 // left starts in the 9th column
+ height: 2 // span down rows 4 & 5 (18 characters in size, total)
+ width: 2 // span across cols 9 & 10 (20 characters in size, total)
+```
diff --git a/docs/posts/configuration/index.html b/docs/posts/configuration/index.html
index 02125fe1..8520f951 100644
--- a/docs/posts/configuration/index.html
+++ b/docs/posts/configuration/index.html
@@ -156,6 +156,32 @@ wouldn’t want to have laying about in the config files.
variable(s) can be found in that module’s “Required ENV Variables”
section of the documentation. See OpsGenie for an example.
+
Grid Layout
+
+
WTF uses the Grid layout system from tview to position widgets
+onscreen. It’s not immediately obvious how this works, so here’s an
+explanation:
+
+
Think of your terminal screen as a matrix of letter positions, say 100 chrs wide and 58 chrs tall.
+
+
Columns breaks up the width of the screen into chunks, each chunk a specified number of characters wide. use
+
+
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
+
+
Ten columns that are ten characters wide
+
+
Rows break up the height of the screen into chunks, each chunk a specified number of characters tall. If we wanted to have five rows:
+
+
[10, 10, 10, 10, 18]
+
+
The co-ordinate system starts at top-left and defines how wide and tall a widget is. If we wanted to put a 2-col, 2-row widget in the bottom of the screen, we’d position it at:
+
+
top: 4 // top starts in the 4th row
+ left: 9 // left starts in the 9th column
+ height: 2 // span down rows 4 & 5 (18 characters in size, total)
+ width: 2 // span across cols 9 & 10 (20 characters in size, total)
+