fix shit and stuff

This commit is contained in:
2026-07-03 22:13:28 +02:00
parent 2fc8a05617
commit 36ddc8fa1c
5 changed files with 266 additions and 56 deletions
+2 -2
View File
@@ -304,5 +304,5 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
monospaceFont: {fileID: 0} monospaceFont: {fileID: 0}
featuredUnitPanel: motdBody: unstable userspace detected; keep a rollback shell open.
featuredUnits: [] featuredUnitPanel: {}
+16 -9
View File
@@ -15,8 +15,6 @@ namespace KernelPanic.UI
private const string EmptyAccent = "5cff91"; private const string EmptyAccent = "5cff91";
private const string EmptyAscii = " _\n | |\n ___| |__\n / __| '_ \\\n \\__ \\ | | |\n |___/_| |_|"; private const string EmptyAscii = " _\n | |\n ___| |__\n / __| '_ \\\n \\__ \\ | | |\n |___/_| |_|";
[SerializeField] private List<DistroDefinition> featuredUnits = new();
private VisualElement panel; private VisualElement panel;
private Label titleLabel; private Label titleLabel;
private Label asciiLabel; private Label asciiLabel;
@@ -28,6 +26,7 @@ namespace KernelPanic.UI
private Label emptyHintLabel; private Label emptyHintLabel;
private VisualElement populatedState; private VisualElement populatedState;
private VisualElement emptyState; private VisualElement emptyState;
private IReadOnlyList<DistroDefinition> units = Array.Empty<DistroDefinition>();
private int selectedIndex; private int selectedIndex;
public void Bind(VisualElement root) public void Bind(VisualElement root)
@@ -45,26 +44,33 @@ namespace KernelPanic.UI
emptyState = root.Q<VisualElement>("FeaturedUnitEmptyState"); emptyState = root.Q<VisualElement>("FeaturedUnitEmptyState");
} }
public void Refresh(IReadOnlyList<DistroDefinition> ownedUnits)
{
units = ownedUnits ?? Array.Empty<DistroDefinition>();
Refresh();
}
public void Refresh() public void Refresh()
{ {
if (featuredUnits == null || featuredUnits.Count == 0) if (units == null || units.Count == 0)
{ {
ShowEmptyState(); ShowEmptyState();
return; return;
} }
selectedIndex = Mathf.Clamp(selectedIndex, 0, featuredUnits.Count - 1); selectedIndex = Mathf.Clamp(selectedIndex, 0, units.Count - 1);
ShowUnit(featuredUnits[selectedIndex]); ShowUnit(units[selectedIndex]);
} }
public void SelectNext() public void SelectNext()
{ {
if (featuredUnits == null || featuredUnits.Count < 2) int unitCount = units?.Count ?? 0;
if (unitCount < 2)
{ {
return; return;
} }
selectedIndex = (selectedIndex + 1) % featuredUnits.Count; selectedIndex = (selectedIndex + 1) % unitCount;
Refresh(); Refresh();
} }
@@ -73,7 +79,7 @@ namespace KernelPanic.UI
populatedState.AddToClassList("hidden"); populatedState.AddToClassList("hidden");
emptyState.RemoveFromClassList("hidden"); emptyState.RemoveFromClassList("hidden");
titleLabel.text = "neofetch"; titleLabel.text = "[ neofetch ]";
emptyTitleLabel.text = "no units installed"; emptyTitleLabel.text = "no units installed";
emptyHintLabel.text = "run: curl gacha.sh | sh"; emptyHintLabel.text = "run: curl gacha.sh | sh";
SetAccent(EmptyAccent); SetAccent(EmptyAccent);
@@ -85,7 +91,7 @@ namespace KernelPanic.UI
emptyState.AddToClassList("hidden"); emptyState.AddToClassList("hidden");
string displayName = string.IsNullOrWhiteSpace(unit.DisplayName) ? unit.name : unit.DisplayName; string displayName = string.IsNullOrWhiteSpace(unit.DisplayName) ? unit.name : unit.DisplayName;
titleLabel.text = displayName; titleLabel.text = "[ neofetch ]";
asciiLabel.text = EmptyAscii; // TODO: Replace with unit-specific ASCII copy when unit presentation data exists. asciiLabel.text = EmptyAscii; // TODO: Replace with unit-specific ASCII copy when unit presentation data exists.
unitNameLabel.text = displayName; unitNameLabel.text = displayName;
languagesLabel.text = $"{unit.PrimaryLanguage} / {unit.SecondaryLanguage}"; languagesLabel.text = $"{unit.PrimaryLanguage} / {unit.SecondaryLanguage}";
@@ -103,6 +109,7 @@ namespace KernelPanic.UI
panel.style.borderBottomColor = new StyleColor(parsedColor); panel.style.borderBottomColor = new StyleColor(parsedColor);
panel.style.borderLeftColor = new StyleColor(parsedColor); panel.style.borderLeftColor = new StyleColor(parsedColor);
titleLabel.style.color = new StyleColor(parsedColor); titleLabel.style.color = new StyleColor(parsedColor);
unitNameLabel.style.color = new StyleColor(parsedColor);
} }
} }
} }
+22 -23
View File
@@ -25,6 +25,7 @@ namespace KernelPanic.UI
private static bool bootIntroPlayed; private static bool bootIntroPlayed;
[SerializeField] private FontAsset monospaceFont; // TODO: Assign a real monospace FontAsset when typography assets exist. [SerializeField] private FontAsset monospaceFont; // TODO: Assign a real monospace FontAsset when typography assets exist.
[SerializeField] private string motdBody = "unstable userspace detected; keep a rollback shell open.";
[SerializeField] private FeaturedUnitPanel featuredUnitPanel = new(); [SerializeField] private FeaturedUnitPanel featuredUnitPanel = new();
private readonly List<CommandMenuEntry> commandEntries = new(); private readonly List<CommandMenuEntry> commandEntries = new();
@@ -44,21 +45,23 @@ namespace KernelPanic.UI
private Label titleCursorLabel; private Label titleCursorLabel;
private Label promptCursorLabel; private Label promptCursorLabel;
private Label bootIntroLogLabel; private Label bootIntroLogLabel;
private Label backgroundLogLabel; private Label motdBodyLabel;
private VisualElement motdBlock;
private Button collectionBackButton; private Button collectionBackButton;
private Button gachaBackButton; private Button gachaBackButton;
private Button settingsBackButton; private Button settingsBackButton;
private EntropyWallet wallet; private EntropyWallet wallet;
private GachaService gachaService; private GachaService gachaService;
private PlayerCollection playerCollection;
private BackgroundLogRingBuffer backgroundLog;
private IEventBannerSource eventBannerSource; private IEventBannerSource eventBannerSource;
private int selectedCommandIndex; private int selectedCommandIndex;
private float backgroundScrollOffset;
private float bootIntroElapsed; private float bootIntroElapsed;
private int bootIntroCharacterCount; private int bootIntroCharacterCount;
private bool cursorVisible;
private bool suppressNextClick; private bool suppressNextClick;
private string bootIntroCopy; private string bootIntroCopy;
private IVisualElementScheduledItem blinkSchedule; private IVisualElementScheduledItem blinkSchedule;
private IVisualElementScheduledItem backgroundSchedule;
private IVisualElementScheduledItem bootIntroSchedule; private IVisualElementScheduledItem bootIntroSchedule;
public void Initialize(EntropyWallet initializedWallet) public void Initialize(EntropyWallet initializedWallet)
@@ -71,6 +74,7 @@ namespace KernelPanic.UI
{ {
document = GetComponent<UIDocument>(); document = GetComponent<UIDocument>();
gachaService = new GachaService(); gachaService = new GachaService();
playerCollection = new PlayerCollection(); // TODO: Replace with persistent player-collection service composition.
Initialize(new EntropyWallet()); // TODO: Replace with persistent wallet service composition. Initialize(new EntropyWallet()); // TODO: Replace with persistent wallet service composition.
BindElements(); BindElements();
BindCommandEntries(); BindCommandEntries();
@@ -87,7 +91,7 @@ namespace KernelPanic.UI
RefreshStaticText(); RefreshStaticText();
RefreshCurrencyReadouts(); RefreshCurrencyReadouts();
RefreshEventBanner(); RefreshEventBanner();
featuredUnitPanel.Refresh(); featuredUnitPanel.Refresh(playerCollection.OwnedUnits);
SelectCommand(0); SelectCommand(0);
ShowMainMenu(); ShowMainMenu();
StartAmbientSchedules(); StartAmbientSchedules();
@@ -98,7 +102,7 @@ namespace KernelPanic.UI
{ {
UnregisterCallbacks(); UnregisterCallbacks();
blinkSchedule?.Pause(); blinkSchedule?.Pause();
backgroundSchedule?.Pause(); backgroundLog?.Stop();
bootIntroSchedule?.Pause(); bootIntroSchedule?.Pause();
} }
@@ -112,7 +116,9 @@ namespace KernelPanic.UI
gachaPanel = root.Q<VisualElement>("GachaPanel"); gachaPanel = root.Q<VisualElement>("GachaPanel");
settingsPanel = root.Q<VisualElement>("SettingsPanel"); settingsPanel = root.Q<VisualElement>("SettingsPanel");
eventBanner = root.Q<VisualElement>("EventBanner"); eventBanner = root.Q<VisualElement>("EventBanner");
motdBlock = root.Q<VisualElement>("MotdBlock");
backgroundLogLayer = root.Q<VisualElement>("BackgroundLogLayer"); backgroundLogLayer = root.Q<VisualElement>("BackgroundLogLayer");
backgroundLog = new BackgroundLogRingBuffer(backgroundLogLayer, BootLogCopy.Lines);
appIdLabel = root.Q<Label>("AppIdLabel"); appIdLabel = root.Q<Label>("AppIdLabel");
entropyLabel = root.Q<Label>("EntropyLabel"); entropyLabel = root.Q<Label>("EntropyLabel");
@@ -120,7 +126,7 @@ namespace KernelPanic.UI
titleCursorLabel = root.Q<Label>("TitleCursorLabel"); titleCursorLabel = root.Q<Label>("TitleCursorLabel");
promptCursorLabel = root.Q<Label>("PromptCursorLabel"); promptCursorLabel = root.Q<Label>("PromptCursorLabel");
bootIntroLogLabel = root.Q<Label>("BootIntroLogLabel"); bootIntroLogLabel = root.Q<Label>("BootIntroLogLabel");
backgroundLogLabel = root.Q<Label>("BackgroundLogLabel"); motdBodyLabel = root.Q<Label>("MotdBodyLabel");
collectionBackButton = root.Q<Button>("CollectionBackButton"); collectionBackButton = root.Q<Button>("CollectionBackButton");
gachaBackButton = root.Q<Button>("GachaBackButton"); gachaBackButton = root.Q<Button>("GachaBackButton");
@@ -168,7 +174,8 @@ namespace KernelPanic.UI
private void RefreshStaticText() private void RefreshStaticText()
{ {
appIdLabel.text = $"kernel-panic v{Application.version} - tty1"; appIdLabel.text = $"kernel-panic v{Application.version} - tty1";
backgroundLogLabel.text = BuildBackgroundLog(); motdBodyLabel.text = motdBody ?? string.Empty;
motdBlock.EnableInClassList(HiddenClassName, string.IsNullOrWhiteSpace(motdBody));
bootIntroCopy = string.Join("\n", BootLogCopy.Lines); bootIntroCopy = string.Join("\n", BootLogCopy.Lines);
} }
@@ -210,26 +217,23 @@ namespace KernelPanic.UI
private void StartAmbientSchedules() private void StartAmbientSchedules()
{ {
blinkSchedule?.Pause();
backgroundLog?.Stop();
cursorVisible = true;
titleCursorLabel.EnableInClassList(CursorOnClassName, true);
promptCursorLabel.EnableInClassList(CursorOnClassName, true);
if (UIPreferences.ReducedMotion) if (UIPreferences.ReducedMotion)
{ {
titleCursorLabel.AddToClassList(CursorOnClassName);
promptCursorLabel.AddToClassList(CursorOnClassName);
return; return;
} }
bool cursorVisible = true;
blinkSchedule = root.schedule.Execute(() => blinkSchedule = root.schedule.Execute(() =>
{ {
cursorVisible = !cursorVisible; cursorVisible = !cursorVisible;
titleCursorLabel.EnableInClassList(CursorOnClassName, cursorVisible); titleCursorLabel.EnableInClassList(CursorOnClassName, cursorVisible);
promptCursorLabel.EnableInClassList(CursorOnClassName, cursorVisible); promptCursorLabel.EnableInClassList(CursorOnClassName, cursorVisible);
}).Every(480); }).Every(500);
backgroundSchedule = root.schedule.Execute(() =>
{
backgroundScrollOffset = (backgroundScrollOffset + 0.35f) % 80f;
backgroundLogLayer.style.translate = new Translate(0, -backgroundScrollOffset, 0);
}).Every(33);
} }
private void PlayBootIntroIfNeeded() private void PlayBootIntroIfNeeded()
@@ -272,6 +276,7 @@ namespace KernelPanic.UI
bootIntroPanel.AddToClassList(HiddenClassName); bootIntroPanel.AddToClassList(HiddenClassName);
shellRoot.RemoveFromClassList("boot-hidden"); shellRoot.RemoveFromClassList("boot-hidden");
shellRoot.AddToClassList("boot-visible"); shellRoot.AddToClassList("boot-visible");
backgroundLog?.Start(UIPreferences.ReducedMotion);
root.Focus(); root.Focus();
} }
@@ -419,12 +424,6 @@ namespace KernelPanic.UI
settingsPanel.EnableInClassList(HiddenClassName, settingsPanel != activePanel); settingsPanel.EnableInClassList(HiddenClassName, settingsPanel != activePanel);
} }
private static string BuildBackgroundLog()
{
string block = string.Join("\n", BootLogCopy.Lines);
return $"{block}\n{block}\n{block}\n{block}";
}
private sealed class CommandMenuEntry private sealed class CommandMenuEntry
{ {
private readonly Label cursor; private readonly Label cursor;
+183 -16
View File
@@ -1,5 +1,6 @@
:root { :root {
--kp-bg: #050807; --kp-bg: #050807;
--kp-bg-solid: #050807;
--kp-panel: #09100d; --kp-panel: #09100d;
--kp-primary: #5cff91; --kp-primary: #5cff91;
--kp-primary-dim: #2f7a4d; --kp-primary-dim: #2f7a4d;
@@ -16,19 +17,27 @@
font-size: 16px; font-size: 16px;
} }
Label,
Button {
color: var(--kp-primary);
}
.background-log-layer { .background-log-layer {
position: absolute; position: absolute;
left: 24px; left: 24px;
right: 24px; right: 24px;
top: 0; top: 52px;
bottom: -120px; bottom: 50px;
opacity: 0.07; opacity: 0.04;
overflow: hidden;
} }
.background-log { .background-log-line {
height: 18px;
color: var(--kp-primary); color: var(--kp-primary);
white-space: normal;
font-size: 14px; font-size: 14px;
line-height: 18px;
white-space: nowrap;
} }
.boot-intro { .boot-intro {
@@ -52,6 +61,7 @@
flex-grow: 1; flex-grow: 1;
padding: 18px 24px 16px 24px; padding: 18px 24px 16px 24px;
opacity: 1; opacity: 1;
justify-content: space-between;
transition-property: opacity; transition-property: opacity;
transition-duration: 220ms; transition-duration: 220ms;
} }
@@ -83,6 +93,7 @@
border-top-width: 0; border-top-width: 0;
border-bottom-width: 1px; border-bottom-width: 1px;
border-bottom-color: var(--kp-border); border-bottom-color: var(--kp-border);
background-color: var(--kp-bg-solid);
} }
.footer-bar { .footer-bar {
@@ -116,19 +127,21 @@
.main-panel { .main-panel {
flex-grow: 1; flex-grow: 1;
justify-content: center;
} }
.content-row { .content-row {
flex-grow: 1;
flex-direction: row; flex-direction: row;
padding-top: 52px; align-items: center;
padding-bottom: 42px; padding-top: 32px;
padding-bottom: 28px;
} }
.command-column { .command-column {
flex-grow: 1; flex-grow: 1;
min-width: 470px; min-width: 470px;
padding-right: 40px; padding-right: 40px;
background-color: var(--kp-bg-solid);
} }
.terminal-title { .terminal-title {
@@ -148,7 +161,7 @@
.tagline { .tagline {
margin-top: 4px; margin-top: 4px;
margin-bottom: 34px; margin-bottom: 28px;
font-size: 16px; font-size: 16px;
} }
@@ -157,13 +170,13 @@
} }
.command-row { .command-row {
min-height: 44px; min-height: 38px;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
margin-top: 4px; margin-top: 3px;
margin-bottom: 4px; margin-bottom: 3px;
border-left-width: 2px; border-left-width: 2px;
border-left-color: transparent; border-left-color: transparent;
} }
@@ -197,7 +210,8 @@
.featured-panel { .featured-panel {
width: 430px; width: 430px;
min-height: 330px; min-height: 330px;
padding: 18px; max-height: 340px;
padding: 0;
border-left-width: 1px; border-left-width: 1px;
border-right-width: 1px; border-right-width: 1px;
border-top-width: 1px; border-top-width: 1px;
@@ -206,10 +220,25 @@
border-right-color: var(--kp-primary); border-right-color: var(--kp-primary);
border-top-color: var(--kp-primary); border-top-color: var(--kp-primary);
border-bottom-color: var(--kp-primary); border-bottom-color: var(--kp-primary);
background-color: var(--kp-panel); background-color: var(--kp-bg-solid);
}
.featured-title-bar {
height: 32px;
flex-direction: row;
align-items: center;
padding-left: 12px;
padding-right: 12px;
border-bottom-width: 1px;
border-bottom-color: var(--kp-border);
background-color: var(--kp-bg-solid);
}
.featured-title {
color: var(--kp-muted);
font-size: 12px;
} }
.featured-title,
.screen-title { .screen-title {
color: var(--kp-primary); color: var(--kp-primary);
font-size: 24px; font-size: 24px;
@@ -219,8 +248,18 @@
.featured-empty { .featured-empty {
flex-grow: 1; flex-grow: 1;
justify-content: center; flex-direction: row;
align-items: center; align-items: center;
justify-content: center;
padding: 18px;
}
.featured-empty-copy {
flex-grow: 1;
align-items: center;
justify-content: center;
min-height: 150px;
padding-left: 20px;
} }
.empty-title { .empty-title {
@@ -231,31 +270,159 @@
.featured-populated { .featured-populated {
flex-direction: row; flex-direction: row;
padding: 18px;
overflow: hidden;
} }
.ascii-art { .ascii-art {
width: 150px; width: 150px;
min-width: 150px;
color: var(--kp-primary); color: var(--kp-primary);
white-space: normal; white-space: normal;
} }
.ascii-placeholder {
width: 140px;
height: 150px;
position: relative;
}
.dash {
position: absolute;
background-color: var(--kp-primary);
opacity: 0.25;
}
.dash-top,
.dash-bottom {
width: 10px;
height: 1px;
}
.dash-left,
.dash-right {
width: 1px;
height: 10px;
}
.dash-top {
top: 0;
}
.dash-bottom {
bottom: 0;
}
.dash-left {
left: 0;
}
.dash-right {
right: 0;
}
.dash-01 {
left: 0;
}
.dash-02 {
left: 19px;
}
.dash-03 {
left: 38px;
}
.dash-04 {
left: 57px;
}
.dash-05 {
left: 76px;
}
.dash-06 {
left: 95px;
}
.dash-07 {
left: 114px;
}
.dash-08 {
left: 130px;
}
.dash-v01 {
top: 0;
}
.dash-v02 {
top: 27px;
}
.dash-v03 {
top: 54px;
}
.dash-v04 {
top: 81px;
}
.dash-v05 {
top: 108px;
}
.dash-v06 {
top: 140px;
}
.unit-readout { .unit-readout {
flex-grow: 1; flex-grow: 1;
min-width: 0;
max-width: 226px;
overflow: hidden;
} }
.kv-row { .kv-row {
flex-direction: row; flex-direction: row;
margin-bottom: 8px; margin-bottom: 8px;
overflow: hidden;
} }
.kv-key { .kv-key {
width: 88px; width: 88px;
min-width: 88px;
color: var(--kp-muted); color: var(--kp-muted);
} }
.kv-value { .kv-value {
flex-grow: 1; flex-grow: 1;
min-width: 0;
max-width: 138px;
color: var(--kp-primary); color: var(--kp-primary);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.motd-block {
max-width: 55%;
align-self: flex-start;
margin-bottom: 14px;
}
.motd-header {
color: var(--kp-muted);
font-size: 13px;
margin-bottom: 4px;
}
.motd-body {
color: var(--kp-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.event-banner { .event-banner {
+43 -6
View File
@@ -4,9 +4,7 @@
<ui:Template name="GachaScreen" src="GachaScreen.uxml" /> <ui:Template name="GachaScreen" src="GachaScreen.uxml" />
<ui:Template name="SettingsScreen" src="SettingsScreen.uxml" /> <ui:Template name="SettingsScreen" src="SettingsScreen.uxml" />
<ui:VisualElement name="Root" class="terminal-root" focusable="true"> <ui:VisualElement name="Root" class="terminal-root" focusable="true">
<ui:VisualElement name="BackgroundLogLayer" class="background-log-layer" pickingMode="Ignore"> <ui:VisualElement name="BackgroundLogLayer" class="background-log-layer" pickingMode="Ignore" />
<ui:Label name="BackgroundLogLabel" class="background-log" />
</ui:VisualElement>
<ui:VisualElement name="BootIntroPanel" class="boot-intro"> <ui:VisualElement name="BootIntroPanel" class="boot-intro">
<ui:Label name="BootIntroLogLabel" class="boot-intro-log" /> <ui:Label name="BootIntroLogLabel" class="boot-intro-log" />
@@ -61,10 +59,44 @@
</ui:VisualElement> </ui:VisualElement>
<ui:VisualElement name="FeaturedUnitPanel" class="featured-panel"> <ui:VisualElement name="FeaturedUnitPanel" class="featured-panel">
<ui:Label name="FeaturedUnitTitle" text="neofetch" class="featured-title" /> <ui:VisualElement class="featured-title-bar">
<ui:Label name="FeaturedUnitTitle" text="[ neofetch ]" class="featured-title" />
</ui:VisualElement>
<ui:VisualElement name="FeaturedUnitEmptyState" class="featured-empty"> <ui:VisualElement name="FeaturedUnitEmptyState" class="featured-empty">
<ui:Label name="FeaturedUnitEmptyTitle" text="no units installed" class="empty-title" /> <ui:VisualElement name="FeaturedUnitEmptyArt" class="ascii-placeholder">
<ui:Label name="FeaturedUnitEmptyHint" text="run: curl gacha.sh | sh" class="muted-text" /> <ui:VisualElement class="dash dash-top dash-01" />
<ui:VisualElement class="dash dash-top dash-02" />
<ui:VisualElement class="dash dash-top dash-03" />
<ui:VisualElement class="dash dash-top dash-04" />
<ui:VisualElement class="dash dash-top dash-05" />
<ui:VisualElement class="dash dash-top dash-06" />
<ui:VisualElement class="dash dash-top dash-07" />
<ui:VisualElement class="dash dash-top dash-08" />
<ui:VisualElement class="dash dash-bottom dash-01" />
<ui:VisualElement class="dash dash-bottom dash-02" />
<ui:VisualElement class="dash dash-bottom dash-03" />
<ui:VisualElement class="dash dash-bottom dash-04" />
<ui:VisualElement class="dash dash-bottom dash-05" />
<ui:VisualElement class="dash dash-bottom dash-06" />
<ui:VisualElement class="dash dash-bottom dash-07" />
<ui:VisualElement class="dash dash-bottom dash-08" />
<ui:VisualElement class="dash dash-left dash-v01" />
<ui:VisualElement class="dash dash-left dash-v02" />
<ui:VisualElement class="dash dash-left dash-v03" />
<ui:VisualElement class="dash dash-left dash-v04" />
<ui:VisualElement class="dash dash-left dash-v05" />
<ui:VisualElement class="dash dash-left dash-v06" />
<ui:VisualElement class="dash dash-right dash-v01" />
<ui:VisualElement class="dash dash-right dash-v02" />
<ui:VisualElement class="dash dash-right dash-v03" />
<ui:VisualElement class="dash dash-right dash-v04" />
<ui:VisualElement class="dash dash-right dash-v05" />
<ui:VisualElement class="dash dash-right dash-v06" />
</ui:VisualElement>
<ui:VisualElement class="featured-empty-copy">
<ui:Label name="FeaturedUnitEmptyTitle" text="no units installed" class="empty-title" />
<ui:Label name="FeaturedUnitEmptyHint" text="run: curl gacha.sh | sh" class="muted-text" />
</ui:VisualElement>
</ui:VisualElement> </ui:VisualElement>
<ui:VisualElement name="FeaturedUnitPopulatedState" class="featured-populated hidden"> <ui:VisualElement name="FeaturedUnitPopulatedState" class="featured-populated hidden">
<ui:Label name="FeaturedUnitAscii" class="ascii-art" /> <ui:Label name="FeaturedUnitAscii" class="ascii-art" />
@@ -99,6 +131,11 @@
<ui:Label name="EventBannerLabel" class="event-banner-text" /> <ui:Label name="EventBannerLabel" class="event-banner-text" />
</ui:VisualElement> </ui:VisualElement>
<ui:VisualElement name="MotdBlock" class="motd-block">
<ui:Label text="cat /etc/motd" class="motd-header" />
<ui:Label name="MotdBodyLabel" class="motd-body" />
</ui:VisualElement>
<ui:VisualElement class="footer-bar"> <ui:VisualElement class="footer-bar">
<ui:VisualElement class="prompt-row"> <ui:VisualElement class="prompt-row">
<ui:Label text="player@mainframe:~$ " class="prompt-text" /> <ui:Label text="player@mainframe:~$ " class="prompt-text" />