Reports give the same kind of number the same look everywhere: a count, a quota, a comparison and a share of a whole each have one form, so a reader learns them once and reads every report the same way.
Server estate · contoso.example · 48 servers · 3 sites ─────────────────────────── × 1 failing · ▲ 3 warning Servers healthy Backups current Oldest patch 44/48 ████████████████████ 46/48 ████████████████████ 41 days FS-02 Needs attention 3 of 48 servers · worst first ────────────── × 1 · ▲ 2 Sites by location ────────────── × SQL-03 97% Disk D: 97% full ╭───────────┬─────────┬─────────╮ ▲ FS-02 41 d Patches 41 days old │ Site │ Servers │ Healthy │ ▲ WEB-07 9 d Certificate expires in 9 days ├───────────┼─────────┼─────────┤ ✓ healthy · ▲ warning · × failing · 48 servers │ London │ 22 │ 21 │ │ Amsterdam │ 16 │ 15 │ │ New York │ 10 │ 8 │ ╰───────────┴─────────┴─────────╯ Backups ──────────────────────────────────────────────────────────────────────────────────────────────────── ╭──────────────┬───────────┬────────╮ │ Job │ Last run │ Size │ ├──────────────┼───────────┼────────┤ │ Nightly SQL │ 02:10 │ 84 GB │ │ Weekly files │ Sun 01:00 │ 312 GB │ ╰──────────────┴───────────┴────────╯
# Summarize a server estate in one dashboard frame: facts, meters, problems first, then the details.
Import-Module -Name ConsoleMonster
# All data is synthetic and inline; no services are contacted or changed.
# Data: fictional inputs kept in memory.
$sites = @(
[pscustomobject] @{ Site = 'London'; Servers = '22'; Healthy = '21' }
[pscustomobject] @{ Site = 'Amsterdam'; Servers = '16'; Healthy = '15' }
[pscustomobject] @{ Site = 'New York'; Servers = '10'; Healthy = '8' }
)
$backups = @(
[pscustomobject] @{ Job = 'Nightly SQL'; 'Last run' = '02:10'; Size = '84 GB' }
[pscustomobject] @{ Job = 'Weekly files'; 'Last run' = 'Sun 01:00'; Size = '312 GB' }
)
# Layout: the frame places each part for the terminal's width.
$frame = @{
Title = 'Server estate'
Fact = 'contoso.example', '48 servers', '3 sites'
State = @(
[pscustomobject] @{ State = 'Failing'; Count = 1; Word = 'failing' }
[pscustomobject] @{ State = 'Warning'; Count = 3; Word = 'warning' }
)
Meter = @(
[pscustomobject] @{ Label = 'Servers healthy'; Healthy = 44; Warning = 3; Failing = 1 }
[pscustomobject] @{ Label = 'Backups current'; Healthy = 46; Whole = 48 }
[pscustomobject] @{ Label = 'Oldest patch'; Value = '41 days'; Fact = 'FS-02'
State = 'Warning' }
[pscustomobject] @{ Label = 'Open alerts'; Value = 5 }
)
Attention = @(
[pscustomobject] @{ State = 'Failing'
Identity = 'SQL-03'
Reason = 'Disk D: 97% full'
Order = 1
Values = '97%' }
[pscustomobject] @{ State = 'Warning'
Identity = 'FS-02'
Reason = 'Patches 41 days old'
Order = 2
Values = '41 d' }
[pscustomobject] @{ State = 'Warning'
Identity = 'WEB-07'
Reason = 'Certificate expires in 9 days'
Order = 3
Values = '9 d' }
)
Noun = 'server'
OutOf = 48
Section = @(
[pscustomobject] @{ Title = 'Sites'; Facts = 'by location'
Content = { $sites | New-ConsoleTable } }
[pscustomobject] @{ Title = 'Backups'; Placement = 'Detail'
Content = { $backups | New-ConsoleTable } }
)
Legend = [ordered] @{ Healthy = 'healthy'; Warning = 'warning'; Failing = 'failing' }
FooterCount = '48 servers'
}
# Behaviour: print the frame once and return.
New-ConsoleDashboardFrame @frame | Show-ConsoleFull program
# Summarize a server estate in one dashboard frame: facts, meters, problems first, then the details.
Import-Module -Name ConsoleMonster
# All data is synthetic and inline; no services are contacted or changed.
# Data: fictional inputs kept in memory.
$sites = @(
[pscustomobject] @{ Site = 'London'; Servers = '22'; Healthy = '21' }
[pscustomobject] @{ Site = 'Amsterdam'; Servers = '16'; Healthy = '15' }
[pscustomobject] @{ Site = 'New York'; Servers = '10'; Healthy = '8' }
)
$backups = @(
[pscustomobject] @{ Job = 'Nightly SQL'; 'Last run' = '02:10'; Size = '84 GB' }
[pscustomobject] @{ Job = 'Weekly files'; 'Last run' = 'Sun 01:00'; Size = '312 GB' }
)
# Layout: the frame places each part for the terminal's width.
$frame = @{
Title = 'Server estate'
Fact = 'contoso.example', '48 servers', '3 sites'
State = @(
[pscustomobject] @{ State = 'Failing'; Count = 1; Word = 'failing' }
[pscustomobject] @{ State = 'Warning'; Count = 3; Word = 'warning' }
)
Meter = @(
[pscustomobject] @{ Label = 'Servers healthy'; Healthy = 44; Warning = 3; Failing = 1 }
[pscustomobject] @{ Label = 'Backups current'; Healthy = 46; Whole = 48 }
[pscustomobject] @{ Label = 'Oldest patch'; Value = '41 days'; Fact = 'FS-02'
State = 'Warning' }
[pscustomobject] @{ Label = 'Open alerts'; Value = 5 }
)
Attention = @(
[pscustomobject] @{ State = 'Failing'
Identity = 'SQL-03'
Reason = 'Disk D: 97% full'
Order = 1
Values = '97%' }
[pscustomobject] @{ State = 'Warning'
Identity = 'FS-02'
Reason = 'Patches 41 days old'
Order = 2
Values = '41 d' }
[pscustomobject] @{ State = 'Warning'
Identity = 'WEB-07'
Reason = 'Certificate expires in 9 days'
Order = 3
Values = '9 d' }
)
Noun = 'server'
OutOf = 48
Section = @(
[pscustomobject] @{ Title = 'Sites'; Facts = 'by location'
Content = { $sites | New-ConsoleTable } }
[pscustomobject] @{ Title = 'Backups'; Placement = 'Detail'
Content = { $backups | New-ConsoleTable } }
)
Legend = [ordered] @{ Healthy = 'healthy'; Warning = 'warning'; Failing = 'failing' }
FooterCount = '48 servers'
}
# Behaviour: print the frame once and return.
New-ConsoleDashboardFrame @frame | Show-Console// Summarize a server estate in one dashboard frame: facts, meters, problems first, then the details.
// All data is synthetic and inline; no services are contacted or changed.
using ConsoleMonster;
using ConsoleMonster.Frames;
using ConsoleMonster.Widgets;
// All values are synthetic. This example never connects to a host.
// Data: fictional inputs kept in memory.
string[][] sites =
{
new[] { "London", "22", "21" },
new[] { "Amsterdam", "16", "15" },
new[] { "New York", "10", "8" }
};
string[][] backups =
{
new[] { "Nightly SQL", "02:10", "84 GB" },
new[] { "Weekly files", "Sun 01:00", "312 GB" }
};
// Layout: the frame places each part for the terminal's width.
DashboardFrame frame = new DashboardFrame("Server estate");
frame.Header.Facts.Add("contoso.example");
frame.Header.Facts.Add("48 servers");
frame.Header.Facts.Add("3 sites");
frame.Header.States.Add(FrameState.Of(HealthState.Failing, 1, "failing"));
frame.Header.States.Add(FrameState.Of(HealthState.Warning, 3, "warning"));
frame.Meters.Add(Meter.Composition("Servers healthy", 44, 3, 1));
frame.Meters.Add(Meter.Ratio("Backups current", 46, 48));
frame.Meters.Add(Meter.Fact("Oldest patch", "41 days", "FS-02", HealthState.Warning));
frame.Meters.Add(Meter.Count("Open alerts", 5));
frame.Attention.Noun = "server";
frame.Attention.OutOf = 48;
(HealthState State, string Identity, string Reason, string Value)[] problems =
{
(HealthState.Failing, "SQL-03", "Disk D: 97% full", "97%"),
(HealthState.Warning, "FS-02", "Patches 41 days old", "41 d"),
(HealthState.Warning, "WEB-07", "Certificate expires in 9 days", "9 d")
};
for (int i = 0; i < problems.Length; i++)
{
AttentionItem item = new AttentionItem(problems[i].State, problems[i].Identity,
problems[i].Reason, i + 1.0);
item.Values.Add(problems[i].Value);
frame.Attention.Items.Add(item);
}
Table siteTable = new Table().AddColumn("Site").AddColumn("Servers").AddColumn("Healthy");
foreach (string[] site in sites) { siteTable.AddValues(site); }
FrameSection siteSection = new FrameSection("Sites", context => siteTable);
siteSection.Facts.Add("by location");
frame.Sections.Add(siteSection);
Table backupTable = new Table().AddColumn("Job").AddColumn("Last run").AddColumn("Size");
foreach (string[] backup in backups) { backupTable.AddValues(backup); }
frame.Sections.Add(new FrameSection(
"Backups",
context => backupTable) { Placement = FramePlacement.Detail });
frame.Legend.Add(HealthState.Healthy, "healthy");
frame.Legend.Add(HealthState.Warning, "warning");
frame.Legend.Add(HealthState.Failing, "failing");
frame.Footer.Counts.Add("48 servers");
frame.Footer.Hint = FrameHint.None;
// Behaviour: print the frame once and return.
Terminal.Write(frame);Full program
// Summarize a server estate in one dashboard frame: facts, meters, problems first, then the details.
// All data is synthetic and inline; no services are contacted or changed.
using ConsoleMonster;
using ConsoleMonster.Frames;
using ConsoleMonster.Widgets;
// All values are synthetic. This example never connects to a host.
// Data: fictional inputs kept in memory.
string[][] sites =
{
new[] { "London", "22", "21" },
new[] { "Amsterdam", "16", "15" },
new[] { "New York", "10", "8" }
};
string[][] backups =
{
new[] { "Nightly SQL", "02:10", "84 GB" },
new[] { "Weekly files", "Sun 01:00", "312 GB" }
};
// Layout: the frame places each part for the terminal's width.
DashboardFrame frame = new DashboardFrame("Server estate");
frame.Header.Facts.Add("contoso.example");
frame.Header.Facts.Add("48 servers");
frame.Header.Facts.Add("3 sites");
frame.Header.States.Add(FrameState.Of(HealthState.Failing, 1, "failing"));
frame.Header.States.Add(FrameState.Of(HealthState.Warning, 3, "warning"));
frame.Meters.Add(Meter.Composition("Servers healthy", 44, 3, 1));
frame.Meters.Add(Meter.Ratio("Backups current", 46, 48));
frame.Meters.Add(Meter.Fact("Oldest patch", "41 days", "FS-02", HealthState.Warning));
frame.Meters.Add(Meter.Count("Open alerts", 5));
frame.Attention.Noun = "server";
frame.Attention.OutOf = 48;
(HealthState State, string Identity, string Reason, string Value)[] problems =
{
(HealthState.Failing, "SQL-03", "Disk D: 97% full", "97%"),
(HealthState.Warning, "FS-02", "Patches 41 days old", "41 d"),
(HealthState.Warning, "WEB-07", "Certificate expires in 9 days", "9 d")
};
for (int i = 0; i < problems.Length; i++)
{
AttentionItem item = new AttentionItem(problems[i].State, problems[i].Identity,
problems[i].Reason, i + 1.0);
item.Values.Add(problems[i].Value);
frame.Attention.Items.Add(item);
}
Table siteTable = new Table().AddColumn("Site").AddColumn("Servers").AddColumn("Healthy");
foreach (string[] site in sites) { siteTable.AddValues(site); }
FrameSection siteSection = new FrameSection("Sites", context => siteTable);
siteSection.Facts.Add("by location");
frame.Sections.Add(siteSection);
Table backupTable = new Table().AddColumn("Job").AddColumn("Last run").AddColumn("Size");
foreach (string[] backup in backups) { backupTable.AddValues(backup); }
frame.Sections.Add(new FrameSection(
"Backups",
context => backupTable) { Placement = FramePlacement.Detail });
frame.Legend.Add(HealthState.Healthy, "healthy");
frame.Legend.Add(HealthState.Warning, "warning");
frame.Legend.Add(HealthState.Failing, "failing");
frame.Footer.Counts.Add("48 servers");
frame.Footer.Hint = FrameHint.None;
// Behaviour: print the frame once and return.
Terminal.Write(frame);// Summarize a server estate in one dashboard frame: facts, meters, problems first, then the details.
// All data is synthetic and inline; no services are contacted or changed.
using ConsoleMonster;
using ConsoleMonster.Frames;
using ConsoleMonster.Widgets;
// All values are synthetic. This example never connects to a host.
// Data: fictional inputs kept in memory.
string[][] sites =
{
new[] { "London", "22", "21" },
new[] { "Amsterdam", "16", "15" },
new[] { "New York", "10", "8" }
};
string[][] backups =
{
new[] { "Nightly SQL", "02:10", "84 GB" },
new[] { "Weekly files", "Sun 01:00", "312 GB" }
};
// Layout: the frame places each part for the terminal's width.
DashboardFrame frame = Ui.DashboardFrame("Server estate");
frame.Header.Facts.Add("contoso.example");
frame.Header.Facts.Add("48 servers");
frame.Header.Facts.Add("3 sites");
frame.Header.States.Add(FrameState.Of(HealthState.Failing, 1, "failing"));
frame.Header.States.Add(FrameState.Of(HealthState.Warning, 3, "warning"));
frame.Meters.Add(Meter.Composition("Servers healthy", 44, 3, 1));
frame.Meters.Add(Meter.Ratio("Backups current", 46, 48));
frame.Meters.Add(Meter.Fact("Oldest patch", "41 days", "FS-02", HealthState.Warning));
frame.Meters.Add(Meter.Count("Open alerts", 5));
frame.Attention.Noun("server");
frame.Attention.OutOf(48);
(HealthState State, string Identity, string Reason, string Value)[] problems =
{
(HealthState.Failing, "SQL-03", "Disk D: 97% full", "97%"),
(HealthState.Warning, "FS-02", "Patches 41 days old", "41 d"),
(HealthState.Warning, "WEB-07", "Certificate expires in 9 days", "9 d")
};
for (int i = 0; i < problems.Length; i++)
{
AttentionItem item = new AttentionItem(problems[i].State, problems[i].Identity,
problems[i].Reason, i + 1.0);
item.Values.Add(problems[i].Value);
frame.Attention.Items.Add(item);
}
Table siteTable = Ui.Table().AddColumn("Site").AddColumn("Servers").AddColumn("Healthy");
foreach (string[] site in sites) { siteTable.AddValues(site); }
FrameSection siteSection = new FrameSection("Sites", context => siteTable);
siteSection.Facts.Add("by location");
frame.Sections.Add(siteSection);
Table backupTable = Ui.Table().AddColumn("Job").AddColumn("Last run").AddColumn("Size");
foreach (string[] backup in backups) { backupTable.AddValues(backup); }
frame.Sections.Add(new FrameSection("Backups", context => backupTable)
.Placement(FramePlacement.Detail));
frame.Legend.Add(HealthState.Healthy, "healthy");
frame.Legend.Add(HealthState.Warning, "warning");
frame.Legend.Add(HealthState.Failing, "failing");
frame.Footer.Counts.Add("48 servers");
frame.Footer.Hint(FrameHint.None);
// Behaviour: print the frame once and return.
frame.Show();
Full program
// Summarize a server estate in one dashboard frame: facts, meters, problems first, then the details.
// All data is synthetic and inline; no services are contacted or changed.
using ConsoleMonster;
using ConsoleMonster.Frames;
using ConsoleMonster.Widgets;
// All values are synthetic. This example never connects to a host.
// Data: fictional inputs kept in memory.
string[][] sites =
{
new[] { "London", "22", "21" },
new[] { "Amsterdam", "16", "15" },
new[] { "New York", "10", "8" }
};
string[][] backups =
{
new[] { "Nightly SQL", "02:10", "84 GB" },
new[] { "Weekly files", "Sun 01:00", "312 GB" }
};
// Layout: the frame places each part for the terminal's width.
DashboardFrame frame = Ui.DashboardFrame("Server estate");
frame.Header.Facts.Add("contoso.example");
frame.Header.Facts.Add("48 servers");
frame.Header.Facts.Add("3 sites");
frame.Header.States.Add(FrameState.Of(HealthState.Failing, 1, "failing"));
frame.Header.States.Add(FrameState.Of(HealthState.Warning, 3, "warning"));
frame.Meters.Add(Meter.Composition("Servers healthy", 44, 3, 1));
frame.Meters.Add(Meter.Ratio("Backups current", 46, 48));
frame.Meters.Add(Meter.Fact("Oldest patch", "41 days", "FS-02", HealthState.Warning));
frame.Meters.Add(Meter.Count("Open alerts", 5));
frame.Attention.Noun("server");
frame.Attention.OutOf(48);
(HealthState State, string Identity, string Reason, string Value)[] problems =
{
(HealthState.Failing, "SQL-03", "Disk D: 97% full", "97%"),
(HealthState.Warning, "FS-02", "Patches 41 days old", "41 d"),
(HealthState.Warning, "WEB-07", "Certificate expires in 9 days", "9 d")
};
for (int i = 0; i < problems.Length; i++)
{
AttentionItem item = new AttentionItem(problems[i].State, problems[i].Identity,
problems[i].Reason, i + 1.0);
item.Values.Add(problems[i].Value);
frame.Attention.Items.Add(item);
}
Table siteTable = Ui.Table().AddColumn("Site").AddColumn("Servers").AddColumn("Healthy");
foreach (string[] site in sites) { siteTable.AddValues(site); }
FrameSection siteSection = new FrameSection("Sites", context => siteTable);
siteSection.Facts.Add("by location");
frame.Sections.Add(siteSection);
Table backupTable = Ui.Table().AddColumn("Job").AddColumn("Last run").AddColumn("Size");
foreach (string[] backup in backups) { backupTable.AddValues(backup); }
frame.Sections.Add(new FrameSection("Backups", context => backupTable)
.Placement(FramePlacement.Detail));
frame.Legend.Add(HealthState.Healthy, "healthy");
frame.Legend.Add(HealthState.Warning, "warning");
frame.Legend.Add(HealthState.Failing, "failing");
frame.Footer.Counts.Add("48 servers");
frame.Footer.Hint(FrameHint.None);
// Behaviour: print the frame once and return.
frame.Show();
Facts
Facts are bold counts with a label. A status fact uses the same colour for the count and its word; a neutral fact steps back in Muted. Facts are separated by middle dots, or | in ASCII. The summaries under the module's default views use the same widget.
Import-Module ConsoleMonster
$facts = @(
[pscustomobject]@{ Value = 3; Label = 'passed'; Role = 'Pass' }
[pscustomobject]@{ Value = 1; Label = 'warning'; Role = 'Warn' }
[pscustomobject]@{ Value = 5; Label = 'checks' }
)
$facts | New-ConsoleFacts | Show-Console
using ConsoleMonster;
using ConsoleMonster.Styling;
using ConsoleMonster.Widgets;
Terminal.Write(new ViewFacts {
Facts = {
new ViewFact(3, "passed", role: ThemeToken.Pass),
new ViewFact(1, "warning", role: ThemeToken.Warn),
new ViewFact(5, "checks")
}
});
using ConsoleMonster;
using ConsoleMonster.Styling;
Ui.Facts().Add(3, "passed", ThemeToken.Pass).Add(1, "warning", ThemeToken.Warn)
.Add(5, "checks").Show();
Bars
- Quota. A Bar chart with
-Maximum 100draws a Line bar on aSubtletrack with a percentage on the right. With any other maximum it shows value/whole. - Comparison. Without
-Maximum, bars compare their values as solid blocks without a track. - Composition. A composition bar keeps the healthy share neutral and colours only the problems. Adjacent identical fills are separated by one cell when there is room, and the legend fits its columns to their labels before wrapping.
Progress bars and score gauges use the same quota form.
Trends
New-ConsoleChart -Type Sparkline -Values 4,3,8 -Labels '00:00','12:00','23:00' adds an axis and the peak value under the trend. Without -Labels it stays a compact one-line trend. A chart inside a titled panel drops a caption that would repeat the title.
Height and alignment
Printed reports keep their natural height. In a live Dashboard, rows share the remaining screen space, and an explicit Fill row takes precedence over the last section. Table columns that hold only numbers align right, even when the values arrive as strings; an explicit alignment wins.
Next
- Charts from the pipeline: bar, line and sparkline charts from object properties.
- Dashboard frames: facts, meters and attention items in one summary.
- Markdown display and export: documents and exports built from the same widgets.