hyperdx/scripts/dev-portal/styles.css
Karl Power 464fb231be
feat: add "Open ClickHouse" button to portal (#2041)
## Summary

- Adds an "Open ClickHouse" to the dev portal that opens the ClickHouse web UI.



### Screenshots or video



<img width="334" height="168" alt="Screenshot 2026-04-02 at 12 29 59" src="https://github.com/user-attachments/assets/1482044a-f7ff-4b42-a05e-49fd6b4f50ca" />


### How to test locally or on Vercel



1. Run the dev portal.
2. Check the "Open ClickHouse" button appears for all instances of the stack, and opens the web UI when clicked.
2026-04-02 12:33:55 +00:00

737 lines
12 KiB
CSS

:root {
/* HyperDX dark mode palette — from _tokens.scss + mantineTheme.ts */
--bg: #101113; /* dark-9: color-bg-body */
--card-bg: #1a1b1e; /* dark-7: color-bg-muted */
--card-surface: #25262b; /* dark-6: color-bg-field */
--border: #2c2e33; /* dark-5: color-border */
--border-emphasis: #373a40; /* dark-4: color-border-emphasis */
--text: #c1c2c5; /* dark-0: color-text */
--text-muted: #909296; /* dark-2: color-text-secondary */
--accent: #25e2a5; /* green-4: color-bg-brand / color-text-brand */
--accent-hover: #a0fad5; /* green-2 */
--green: #25e2a5; /* green-4: color-text-success */
--red: #ff725c; /* chart-error */
--yellow: #efb118; /* chart-warning */
--orange: #db6d28;
--log-bg: #141517; /* dark-8 */
--hover: #25262b; /* dark-6: color-bg-hover */
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial,
sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
}
.layout {
display: flex;
height: 100vh;
}
.main-panel {
flex: 1;
overflow-y: auto;
padding: 24px;
min-width: 0;
}
.log-panel {
width: 0;
background: var(--bg);
border-left: 1px solid var(--border);
display: flex;
flex-direction: column;
transition: width 0.2s ease;
overflow: hidden;
}
.log-panel.open {
width: 50%;
min-width: 400px;
}
.log-panel-header {
padding: 12px 16px;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
}
.log-panel-header h3 {
font-size: 14px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.log-panel-header .slot-label {
font-size: 11px;
color: var(--text-muted);
font-weight: 400;
}
.log-panel-actions {
display: flex;
gap: 8px;
align-items: center;
}
.log-panel-actions button {
background: none;
border: 1px solid var(--border);
color: var(--text-muted);
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: all 0.15s;
}
.log-panel-actions button:hover {
color: var(--text);
border-color: var(--text-muted);
}
.log-panel-actions .close-btn {
font-size: 18px;
line-height: 1;
padding: 2px 6px;
border: none;
}
.log-content {
flex: 1;
overflow-y: auto;
padding: 12px 16px;
font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
font-size: 12px;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-all;
background: var(--bg);
color: #c9d1d9;
}
.log-content .log-line {
padding: 0 4px;
}
.log-streaming-badge {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 11px;
color: var(--green);
}
.log-streaming-badge .stream-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--green);
animation: pulse 1.5s infinite;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid var(--border);
}
.header h1 {
font-size: 24px;
font-weight: 600;
display: flex;
align-items: center;
gap: 10px;
}
.header .logo {
width: 28px;
height: 28px;
flex-shrink: 0;
}
.header .status {
font-size: 13px;
color: var(--text-muted);
display: flex;
align-items: center;
gap: 6px;
}
.header .status .dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--green);
animation: pulse 2s infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.empty-state {
text-align: center;
padding: 80px 20px;
color: var(--text-muted);
}
.empty-state h2 {
font-size: 20px;
margin-bottom: 12px;
color: var(--text);
}
.empty-state code {
background: var(--card-bg);
padding: 4px 10px;
border-radius: 6px;
border: 1px solid var(--border);
font-size: 14px;
}
.stacks {
display: grid;
gap: 16px;
grid-template-columns: 1fr;
}
.stack-card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 12px;
overflow: hidden;
}
.stack-header {
padding: 16px 20px;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
}
.env-badge {
font-size: 10px;
font-weight: 700;
padding: 2px 7px;
border-radius: 10px;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.env-badge.dev {
background: rgba(37, 226, 165, 0.15);
color: var(--accent);
}
.env-badge.e2e {
background: rgba(88, 166, 255, 0.15);
color: #58a6ff;
}
.env-badge.int {
background: rgba(239, 177, 24, 0.15);
color: var(--yellow);
}
.services-table tr.env-separator td {
padding: 8px 20px 4px;
border-bottom: none;
background: var(--card-surface);
}
.services-table tr.env-separator + tr td {
border-top: none;
}
.stack-header .branch {
font-size: 15px;
font-weight: 600;
font-family: 'SF Mono', 'Fira Code', monospace;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.stack-header .worktree {
font-size: 12px;
color: var(--text-muted);
margin-top: 2px;
}
.stack-header .stack-actions {
display: flex;
gap: 8px;
}
.stack-header .open-btn {
background: var(--accent);
color: var(--bg);
border: none;
padding: 6px 14px;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
text-decoration: none;
transition: opacity 0.15s;
}
.stack-header .open-btn:hover {
opacity: 0.85;
}
.stack-header .open-btn.open-btn-secondary {
background: transparent;
color: var(--accent);
border: 1px solid var(--accent);
}
.services-table {
width: 100%;
border-collapse: collapse;
}
.services-table th {
text-align: left;
padding: 8px 20px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-muted);
border-bottom: 1px solid var(--border);
}
.services-table td {
padding: 10px 20px;
font-size: 14px;
border-bottom: 1px solid rgba(48, 54, 61, 0.5);
}
.services-table tr:last-child td {
border-bottom: none;
}
.services-table tr.clickable {
cursor: pointer;
transition: background 0.1s;
}
.services-table tr.clickable:hover {
background: rgba(88, 166, 255, 0.05);
}
.services-table tr.active {
background: rgba(88, 166, 255, 0.1);
}
.service-name {
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
}
.service-name .type-badge {
font-size: 10px;
padding: 1px 5px;
border-radius: 4px;
font-weight: 600;
text-transform: uppercase;
}
.type-badge.docker {
background: rgba(37, 226, 165, 0.12);
color: var(--accent);
}
.type-badge.local {
background: rgba(239, 177, 24, 0.12);
color: var(--yellow);
}
.log-btn {
background: none;
border: 1px solid var(--border);
color: var(--text-muted);
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
cursor: pointer;
transition: all 0.15s;
}
.log-btn:hover {
color: var(--text);
border-color: var(--text-muted);
}
.status-indicator {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-dot.up {
background: var(--green);
}
.status-dot.down {
background: var(--red);
}
.port-link {
color: var(--accent);
text-decoration: none;
font-family: 'SF Mono', 'Fira Code', monospace;
font-size: 13px;
}
.port-link:hover {
text-decoration: underline;
}
.port-plain {
color: var(--text-muted);
font-family: 'SF Mono', 'Fira Code', monospace;
font-size: 13px;
}
.error-banner {
background: rgba(248, 81, 73, 0.1);
border: 1px solid var(--red);
color: var(--red);
padding: 12px 16px;
border-radius: 8px;
margin-bottom: 16px;
font-size: 14px;
display: none;
}
/* --- Tabs --- */
.tab-bar {
display: flex;
gap: 0;
margin-bottom: 20px;
border-bottom: 1px solid var(--border);
}
.tab {
background: none;
border: none;
border-bottom: 2px solid transparent;
color: var(--text-muted);
padding: 8px 16px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.15s;
}
.tab:hover {
color: var(--text);
}
.tab.active {
color: var(--accent);
border-bottom-color: var(--accent);
}
/* --- History --- */
.history-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.history-header h2 {
font-size: 18px;
font-weight: 600;
}
.clear-all-btn {
background: none;
border: 1px solid var(--red);
color: var(--red);
padding: 5px 12px;
border-radius: 6px;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.15s;
}
.clear-all-btn:hover {
background: rgba(248, 81, 73, 0.1);
}
.history-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.history-toggle-btn {
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
font-size: 18px;
line-height: 1;
padding: 2px 6px;
border-radius: 4px;
transition: all 0.15s;
user-select: none;
}
.history-toggle-btn:hover {
color: var(--text);
background: rgba(255, 255, 255, 0.05);
}
.history-card-body {
overflow: hidden;
transition: max-height 0.2s ease;
}
.history-card-body.collapsed {
max-height: 0 !important;
}
.history-entry {
border-top: 1px solid var(--border);
}
.history-entry:first-child {
border-top: none;
}
.history-entry-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 20px;
background: var(--card-surface);
border-bottom: 1px solid rgba(48, 54, 61, 0.5);
}
.history-entry-header .history-meta {
display: flex;
align-items: center;
gap: 10px;
}
.history-time {
font-size: 13px;
color: var(--text);
font-weight: 500;
}
.history-ts {
font-size: 11px;
color: var(--text-muted);
font-family: 'SF Mono', 'Fira Code', monospace;
}
.history-delete-btn {
background: none;
border: 1px solid var(--border);
color: var(--text-muted);
padding: 3px 10px;
border-radius: 4px;
font-size: 11px;
cursor: pointer;
transition: all 0.15s;
}
.history-delete-btn:hover {
color: var(--red);
border-color: var(--red);
}
.file-list {
padding: 4px 0;
}
.file-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 20px;
cursor: pointer;
transition: background 0.1s;
font-size: 13px;
}
.file-item:hover {
background: rgba(88, 166, 255, 0.05);
}
.file-item.active {
background: rgba(88, 166, 255, 0.1);
}
.file-name {
font-family: 'SF Mono', 'Fira Code', monospace;
color: var(--text);
}
.file-size {
font-size: 11px;
color: var(--text-muted);
font-family: 'SF Mono', 'Fira Code', monospace;
}
/* --- History search --- */
.history-search {
position: relative;
margin-bottom: 16px;
}
.history-search input {
width: 100%;
padding: 9px 14px 9px 36px;
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 8px;
color: var(--text);
font-size: 14px;
font-family: inherit;
outline: none;
transition: border-color 0.15s;
box-sizing: border-box;
}
.history-search input:focus {
border-color: var(--accent);
}
.history-search input::placeholder {
color: var(--text-muted);
}
.history-search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: var(--text-muted);
font-size: 14px;
pointer-events: none;
}
.search-match {
background: rgba(37, 226, 165, 0.2);
border-radius: 2px;
}
/* --- Confirm modal --- */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
opacity: 0;
transition: opacity 0.15s;
}
.modal-overlay.visible {
opacity: 1;
}
.modal-box {
background: var(--card-bg);
border: 1px solid var(--border-emphasis);
border-radius: 12px;
padding: 24px;
max-width: 380px;
width: 90%;
transform: scale(0.95);
transition: transform 0.15s;
}
.modal-overlay.visible .modal-box {
transform: scale(1);
}
.modal-box h3 {
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
}
.modal-box p {
font-size: 14px;
color: var(--text-muted);
margin-bottom: 20px;
line-height: 1.5;
}
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
.modal-actions button {
padding: 7px 16px;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.15s;
}
.modal-cancel {
background: none;
border: 1px solid var(--border);
color: var(--text);
}
.modal-cancel:hover {
border-color: var(--text-muted);
}
.modal-danger {
background: var(--red);
border: 1px solid var(--red);
color: #fff;
}
.modal-danger:hover {
opacity: 0.85;
}