feat: Phase 1 - TUI Foundation with Textual

- Added Textual TUI framework with modern terminal UI
- Created main TUI app with navigation, message view, event log, state inspector
- Added comprehensive CSS styling for Discord-like appearance
- Implemented navigation pane, message area, event log, state tree, command bar
- Added keyboard shortcuts (Ctrl+Q quit, Ctrl+C command, Ctrl+N/P nav, etc.)
- Integrated TUI with existing REST/Gateway servers and state management
- Added CLI flag --tui/--no-tui to launch TUI mode
- Added Textual and textual-dev to optional dependencies

All 19 tests passing.
This commit is contained in:
k.eaven committed 2026-11-01 03:09:40 +00:00
1 parent 5669048f89
commit 2466acf837
11 files changed
+826 -1

No files matched your search

+474
View File
@@ -0,0 +1,474 @@
/* APIcord TUI Styles */
Screen {
background: $surface;
color: $text;
}
#navigation-pane {
width: 22;
background: $panel;
border-right: solid $primary;
padding: 1;
}
#navigation-pane .nav-title {
text-style: bold;
color: $accent;
margin-bottom: 1;
}
#navigation-pane .nav-item {
height: 3;
margin: 0 1;
padding: 0 1;
background: $surface;
border: solid $background;
}
#navigation-pane .nav-item:hover {
background: $primary 20%;
border: solid $primary;
}
#navigation-pane .nav-item:focus {
background: $primary 30%;
border: solid $accent;
}
#navigation-pane .nav-item .nav-icon {
width: 3;
text-align: center;
color: $text-muted;
}
#navigation-pane .nav-item .nav-label {
width: 1fr;
}
#navigation-pane .nav-item .nav-badge {
width: 5;
text-align: right;
color: $accent;
text-style: bold;
}
#main-content {
width: 1fr;
background: $surface;
layout: vertical;
}
#message-area {
height: 1fr;
border: solid $primary;
margin: 1;
padding: 1;
}
#message-list {
height: 1fr;
overflow-y: scroll;
}
.message-entry {
height: auto;
min-height: 3;
margin: 0 0 1 0;
padding: 1;
background: $panel;
border: solid $background;
}
.message-entry:hover {
border: solid $primary;
}
.message-header {
height: 1;
layout: horizontal;
}
.message-author {
color: $accent;
text-style: bold;
}
.message-timestamp {
color: $text-muted;
text-align: right;
width: 20;
}
.message-content {
margin-top: 1;
color: $text;
}
.message-embed {
margin-top: 1;
padding: 1;
background: $surface;
border: solid $primary;
}
.embed-title {
color: $accent;
text-style: bold;
}
.embed-description {
margin: 1 0;
color: $text;
}
.embed-fields {
layout: horizontal;
}
.embed-field {
width: 1fr;
margin: 0 1;
padding: 1;
background: $surface;
border: solid $background;
}
.embed-field.inline {
width: auto;
min-width: 20;
}
.embed-field-name {
color: $text-muted;
text-style: bold;
text-transform: uppercase;
font-size: 80%;
}
.embed-field-value {
color: $text;
margin-top: 1;
}
.embed-footer {
margin-top: 1;
color: $text-muted;
font-size: 80%;
text-align: right;
}
.message-components {
margin-top: 1;
layout: horizontal;
}
.component-button {
min-width: 16;
height: 3;
margin: 0 1;
border: solid $primary;
}
.component-button.primary {
background: $primary;
color: $surface;
}
.component-button.secondary {
background: $secondary;
color: $surface;
}
.component-button.success {
background: $success;
color: $surface;
}
.component-button.danger {
background: $error;
color: $surface;
}
.component-button.link {
background: transparent;
color: $primary;
text-style: underline;
}
.component-select {
min-width: 30;
height: 3;
margin: 0 1;
border: solid $primary;
background: $surface;
}
#inspector-pane {
width: 36;
background: $panel;
border-left: solid $primary;
padding: 1;
}
#inspector-pane .section-title {
text-style: bold underline;
color: $accent;
margin: 1 0;
}
#inspector-pane .state-tree {
height: 1fr;
overflow-y: scroll;
}
.tree-node {
height: 1;
padding: 0 1;
}
.tree-node.expanded > .node-label {
text-style: bold;
}
.tree-node .node-icon {
width: 2;
}
.tree-node .node-label {
width: 1fr;
}
.tree-node .node-value {
width: 12;
text-align: right;
color: $text-muted;
}
#event-log {
height: 1fr;
border: solid $primary;
margin: 1;
padding: 1;
overflow-y: scroll;
}
.event-entry {
height: 1;
padding: 0 1;
border-bottom: solid $background 50%;
}
.event-entry:hover {
background: $primary 10%;
}
.event-time {
width: 10;
color: $text-muted;
text-style: bold;
}
.event-type {
width: 20;
color: $accent;
text-style: bold;
}
.event-detail {
width: 1fr;
color: $text;
}
#command-bar {
height: 3;
background: $panel;
border-top: solid $primary;
padding: 0 1;
layout: horizontal;
}
#command-bar .prompt {
width: 3;
color: $accent;
text-style: bold;
content-align: center middle;
}
#command-input {
width: 1fr;
height: 3;
background: $surface;
border: solid $primary;
padding: 0 1;
}
#command-input:focus {
border: solid $accent;
}
#status-bar {
height: 1;
background: $primary;
color: $surface;
padding: 0 1;
layout: horizontal;
}
.status-item {
width: auto;
margin-right: 2;
}
#footer-hints {
height: 1;
background: $surface;
color: $text-muted;
padding: 0 1;
text-align: center;
}
.tabbed-content {
height: 1fr;
}
.tab-header {
height: 1;
layout: horizontal;
background: $panel;
border-bottom: solid $primary;
}
.tab-button {
min-width: 12;
height: 1;
padding: 0 1;
background: transparent;
border: none;
color: $text-muted;
}
.tab-button.active {
color: $accent;
text-style: bold;
border-bottom: solid $accent;
}
.tab-button:hover {
color: $text;
background: $surface;
}
.tab-panel {
height: 1fr;
display: none;
}
.tab-panel.active {
display: block;
}
/* Modal / Dialog */
.modal {
align: center middle;
width: 60;
height: 30;
background: $panel;
border: thick $accent;
padding: 1;
}
.modal-title {
text-style: bold;
color: $accent;
margin-bottom: 1;
text-align: center;
}
.modal-content {
height: 1fr;
overflow-y: scroll;
}
.modal-buttons {
height: 3;
layout: horizontal;
margin-top: 1;
}
.modal-button {
width: 1fr;
margin: 0 1;
}
/* Command Palette */
.command-palette {
align: center middle;
width: 80;
height: 40;
background: $panel;
border: thick $accent;
padding: 1;
}
.command-palette-input {
width: 1fr;
height: 3;
margin-bottom: 1;
}
.command-palette-results {
height: 1fr;
overflow-y: scroll;
}
.command-palette-item {
height: 3;
padding: 0 1;
layout: horizontal;
}
.command-palette-item:hover {
background: $primary 20%;
}
.command-palette-item .cmd-name {
width: 24;
color: $accent;
text-style: bold;
}
.command-palette-item .cmd-desc {
width: 1fr;
color: $text-muted;
}
.command-palette-item .cmd-shortcut {
width: 20;
text-align: right;
color: $text-muted;
font-size: 80%;
}
/* Connection status indicators */
.status-connected {
color: $success;
}
.status-disconnected {
color: $error;
}
.status-connecting {
color: $warning;
}
/* Scrollbar styling */
ScrollBar {
background: $surface;
color: $primary;
}
ScrollBar:hover {
color: $accent;
}