Add Loki and Sentinel utility classes for web API endpoints

- Implemented LokiUtils class with GET and POST endpoints for managing scripts, jobs, and payloads.
- Added SentinelUtils class with GET and POST endpoints for managing events, rules, devices, and notifications.
- Both classes include error handling and JSON response formatting.
This commit is contained in:
infinition
2026-03-14 22:33:10 +01:00
parent eb20b168a6
commit aac77a3e76
525 changed files with 29400 additions and 13136 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
/* ===== STUDIO RUNTIME HOST ===== */
.studio-container.studio-runtime-host {
min-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 60px) - 12px);
height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 60px) - 12px);
}
.studio-container.studio-runtime-host #app {
height: 100%;
min-height: 100%;
display: grid;
grid-template-rows: auto minmax(0, 1fr) auto;
}
.studio-container.studio-runtime-host main {
min-height: 0;
}
.studio-container.studio-runtime-host #left,
.studio-container.studio-runtime-host #center,
.studio-container.studio-runtime-host #right {
min-height: 0;
height: 100%;
}
/* ===== Studio kebab menu (replaces inline styles) ===== */
.studio-container .kebab {
position: relative;
}
.studio-container .studio-kebab-menu {
position: absolute;
top: calc(100% + 6px);
right: 0;
min-width: 240px;
background: var(--panel);
border: 1px solid var(--c-border-strong);
border-radius: 12px;
padding: 6px;
box-shadow: 0 10px 32px rgba(0, 0, 0, .45);
display: none;
z-index: 2400;
}
.studio-container .studio-kebab-menu .item {
padding: .55rem .7rem;
border-radius: 8px;
font-size: 13px;
cursor: pointer;
color: var(--ink);
transition: background .15s ease;
}
.studio-container .studio-kebab-menu .item:hover {
background: color-mix(in oklab, var(--ink) 8%, transparent);
}
/* ===== Studio legend dots ===== */
.studio-container .legend-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
}
.studio-container .legend-dot.legend-ok {
background: var(--ok, #22c55e);
}
.studio-container .legend-dot.legend-bad {
background: var(--bad, #ef4444);
}
.studio-container .legend-dot.legend-req {
background: #7aa7ff;
}
/* ===== Studio create host button ===== */
.studio-container .studio-create-host-btn {
width: 100%;
margin-bottom: 10px;
}
/* ===== Studio action buttons row ===== */
.studio-container .studio-action-btns {
margin-top: .6rem;
}
/* ===== Studio mono input ===== */
.studio-container .mono-input {
font-family: ui-monospace, 'Cascadia Code', 'Fira Code', monospace;
}
/* ===== Flex-1 utility ===== */
.studio-container .flex-1 {
flex: 1;
}
/* ===== Studio link wizard endpoints ===== */
.studio-container .studio-link-endpoints {
margin-bottom: 6px;
}
/* ===== Studio preview row ===== */
.studio-container .studio-preview-row {
margin-top: 10px;
}
/* ===== Studio wizard buttons ===== */
.studio-container .studio-wizard-btns {
margin-top: 16px;
display: flex;
gap: 10px;
}
@media (max-width: 1100px) {
.studio-container.studio-runtime-host #left,
.studio-container.studio-runtime-host #right {
top: calc(var(--h-topbar, 56px) + var(--studio-header-h, 52px) + 8px);
}
}
.studio-container .studio-loading {
padding: 14px;
color: var(--muted);
text-align: center;
border: 1px dashed var(--c-border);
border-radius: 10px;
margin: 12px;
}
/* ===== Backup page responsive ===== */
@media (max-width: 900px) {
.page-backup .backup-layout {
grid-template-columns: 1fr;
min-height: auto;
}
.page-backup .backup-sidebar {
grid-template-columns: 1fr 1fr;
}
.page-backup .backup-sidehead {
grid-column: 1 / -1;
}
.page-backup.page-with-sidebar .backup-sidebar {
grid-template-columns: 1fr 1fr;
}
.page-backup.page-with-sidebar .backup-sidehead {
grid-column: 1 / -1;
}
}

993
web/css/pages/attacks.css Normal file
View File

@@ -0,0 +1,993 @@
/* ==========================================================================
ATTACKS — Full page: Attacks · Comments · Images · EPD Layout
Expert UX/UI — responsive, no unwanted scroll, clean layouts.
========================================================================== */
/* ── Sidebar scroll isolation ──────────────────────────────── */
.attacks-container .attacks-sidebar.page-sidebar {
overflow: hidden;
}
/* ── Main area: fill height so textarea stretches ─────────── */
.attacks-container .attacks-main.page-main {
max-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 24px);
overflow: hidden;
}
/* ── Tabs ─────────────────────────────────────────────────── */
.attacks-container .tabs-container {
display: flex;
gap: 3px;
padding: 0 0 8px;
border-bottom: 1px solid var(--_border);
}
.attacks-container .attacks-sidebar > .tabs-container {
margin: 10px 10px 0;
padding-bottom: 6px;
flex-shrink: 0;
}
/* Keep sidehead + tabs pinned at top of sidebar */
.attacks-container .attacks-sidebar > .sidehead {
flex-shrink: 0;
position: sticky;
top: 0;
z-index: 5;
background: var(--grad-card);
}
.attacks-container .attacks-sidebar > .sidebar-page {
flex: 1;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
padding: 8px 10px 10px;
}
.attacks-container .tab-btn {
flex: 1;
padding: 8px 4px;
border: 1px solid var(--_border);
border-bottom: none;
cursor: pointer;
font-size: 12px;
font-weight: 700;
letter-spacing: .02em;
border-radius: 8px 8px 0 0;
color: var(--_muted);
background: transparent;
transition: color .15s, background .15s;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.attacks-container .tab-btn:hover {
color: var(--_ink);
background: var(--_panel-lo);
}
.attacks-container .tab-btn.active {
color: var(--_ink);
background: color-mix(in oklab, var(--_acid2) 10%, var(--_panel-lo));
border-color: color-mix(in oklab, var(--_acid2) 25%, var(--_border));
}
/* ── Unified list (shared across sidebar tabs) ────────────── */
.attacks-container .unified-list {
list-style: none;
margin: 0;
padding: 0;
}
.attacks-container .unified-list .card {
display: flex;
align-items: center;
gap: 10px;
padding: 8px;
margin-bottom: 4px;
cursor: pointer;
border-radius: 10px;
background: var(--_panel-lo);
transition: background .15s;
border: 1px solid transparent;
}
.attacks-container .unified-list .card:hover {
background: var(--_panel-hi);
}
.attacks-container .unified-list .card.selected {
background: color-mix(in oklab, var(--_acid2) 12%, var(--_panel-hi));
border-color: color-mix(in oklab, var(--_acid2) 30%, var(--_border));
}
.attacks-container .unified-list .card img {
height: 40px;
width: 40px;
border-radius: 8px;
object-fit: cover;
background: #0b0e13;
border: 1px solid var(--_border);
flex: 0 0 auto;
}
.attacks-container .unified-list .card span {
flex: 1;
font-weight: 700;
font-size: 13px;
color: var(--_ink);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ── Enable dot ───────────────────────────────────────────── */
.attacks-container .enable-dot {
--size: 12px;
width: var(--size);
height: var(--size);
border-radius: 999px;
border: 1px solid var(--_border);
background: var(--ko);
box-shadow: 0 0 0 0 var(--ko-glow);
transition: .18s ease;
flex: 0 0 auto;
cursor: pointer;
}
.attacks-container .enable-dot.on {
background: var(--ok);
box-shadow: 0 0 0 3px var(--ok-glow);
border-color: color-mix(in oklab, var(--ok) 45%, var(--_border));
}
.attacks-container .enable-dot:focus-visible {
outline: none;
box-shadow: 0 0 0 3px color-mix(in oklab, var(--_acid2) 45%, transparent);
}
/* ── Page content panels ──────────────────────────────────── */
.attacks-container .page-content {
display: none;
flex-direction: column;
min-height: 0;
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 10px;
}
.attacks-container .page-content.active {
display: flex;
}
/* Suppress focus ring on main area (used for arrow key nav in EPD editor) */
.attacks-container .page-content:focus {
outline: none;
}
/* ── ATTACKS TAB: Editor ──────────────────────────────────── */
.attacks-container .editor-textarea-container {
display: flex;
flex-direction: column;
flex: 1;
gap: 10px;
min-height: 0;
}
.attacks-container .editor-header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
flex-wrap: wrap;
flex-shrink: 0;
}
.attacks-container .editor-header h2 {
font-size: 16px;
margin: 0;
}
.attacks-container .editor-buttons {
display: flex;
gap: 6px;
}
.attacks-container .editor-textarea {
flex: 1;
min-height: 200px;
resize: vertical;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 13px;
line-height: 1.5;
color: var(--_ink);
background: var(--_panel-lo);
border: 1px solid var(--_border);
border-radius: 10px;
padding: 12px;
transition: border-color .15s;
}
.attacks-container .editor-textarea:focus {
outline: none;
border-color: color-mix(in oklab, var(--_acid2) 28%, var(--_border));
box-shadow: 0 0 0 2px color-mix(in oklab, var(--_acid2) 12%, transparent);
background: var(--_panel-hi);
}
/* ── IMAGES TAB: Actions bar + grid ───────────────────────── */
.attacks-container .actions-bar {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
position: sticky;
top: 0;
z-index: 10;
background: var(--_panel);
padding: 8px 10px;
border-radius: 10px;
border: 1px solid var(--_border);
backdrop-filter: blur(10px);
}
.attacks-container .actions-bar button,
.attacks-container .chip,
.attacks-container .select,
.attacks-container .sort-toggle {
border-radius: 8px;
border: 1px solid var(--_border);
color: var(--_ink);
background: var(--_panel-lo);
padding: 7px 10px;
cursor: pointer;
transition: .15s;
font-weight: 700;
font-size: 12px;
white-space: nowrap;
}
.attacks-container .actions-bar button:hover,
.attacks-container .chip:hover,
.attacks-container .select:hover,
.attacks-container .sort-toggle:hover {
background: var(--_panel-hi);
}
.attacks-container .actions-bar button.danger {
background: color-mix(in oklab, var(--_acid) 10%, var(--_panel-lo));
}
.attacks-container .actions-bar button.danger:hover {
background: color-mix(in oklab, var(--_acid) 16%, var(--_panel-hi));
}
.attacks-container .chip { border-radius: 999px; }
.attacks-container .select { appearance: none; }
.attacks-container .sort-toggle { min-width: 36px; text-align: center; }
.attacks-container .field {
position: relative;
min-width: 140px;
flex: 1 1 140px;
max-width: 240px;
}
.attacks-container .input {
width: 100%;
padding: 7px 10px 7px 32px;
color: var(--_ink);
background: var(--_panel-lo);
border: 1px solid var(--_border);
border-radius: 8px;
outline: none;
font-size: 12px;
transition: .15s;
}
.attacks-container .input:focus {
border-color: color-mix(in oklab, var(--_acid2) 28%, var(--_border));
box-shadow: 0 0 0 2px color-mix(in oklab, var(--_acid2) 12%, transparent);
background: var(--_panel-hi);
}
.attacks-container .field .icon {
position: absolute;
left: 10px;
top: 7px;
opacity: .6;
pointer-events: none;
font-size: 12px;
}
.attacks-container .range-wrap {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
}
.attacks-container .range { accent-color: var(--_acid); }
.attacks-container .image-container {
display: grid;
gap: 8px;
grid-template-columns: repeat(auto-fill, minmax(var(--tile-min), 1fr));
padding-bottom: 80px;
}
.attacks-container .image-item {
position: relative;
border-radius: 10px;
overflow: hidden;
cursor: pointer;
aspect-ratio: 1/1;
transition: .15s;
background: var(--_panel-lo);
border: 1px solid var(--_border);
}
.attacks-container .image-item:hover {
transform: translateY(-2px);
box-shadow: var(--_shadow);
}
.attacks-container .image-item img {
width: 100%;
height: 100%;
display: block;
object-fit: contain;
background: #0b0e13;
image-rendering: pixelated;
}
.attacks-container .image-info {
position: absolute;
inset: auto 0 0 0;
padding: 4px 6px;
text-align: center;
font-size: 11px;
color: var(--_ink);
background: linear-gradient(180deg, transparent, rgba(0, 0, 0, .7));
}
.attacks-container .select-ring {
position: absolute;
inset: 0;
pointer-events: none;
border: 3px solid transparent;
border-radius: 10px;
transition: .15s;
}
.attacks-container .image-item.selectable:hover .select-ring {
border-color: color-mix(in oklab, var(--_acid2) 35%, transparent);
}
.attacks-container .image-item.selected .select-ring {
border-color: var(--_acid2);
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--_acid2) 30%, transparent);
}
.attacks-container .tick-overlay {
position: absolute;
top: 6px;
right: 6px;
width: 22px;
height: 22px;
border-radius: 50%;
background: color-mix(in oklab, var(--_acid) 80%, white);
color: #001;
font-weight: 900;
font-size: 12px;
display: none;
align-items: center;
justify-content: center;
box-shadow: var(--_shadow);
}
.attacks-container .image-item.selected .tick-overlay {
display: flex;
}
.attacks-container .skeleton {
border-radius: 10px;
aspect-ratio: 1/1;
background: linear-gradient(90deg, rgba(255, 255, 255, .03) 25%, rgba(255, 255, 255, .07) 37%, rgba(255, 255, 255, .03) 63%);
background-size: 400% 100%;
animation: atk-shimmer 1.1s infinite;
border: 1px solid var(--_border);
}
@keyframes atk-shimmer {
0% { background-position: 100% 0; }
100% { background-position: 0 0; }
}
/* ── Mode visibility toggles ─────────────────────────────── */
.attacks-container .edit-only { display: none; }
.attacks-container .status-only { display: none; }
.attacks-container .static-only { display: none; }
.attacks-container .web-only { display: none; }
.attacks-container .icons-only { display: none; }
.attacks-container .edit-mode .edit-only { display: inline-flex; }
.attacks-container .status-mode .status-only { display: inline-block; }
.attacks-container .static-mode .static-only { display: inline-block; }
.attacks-container .web-mode .web-only { display: inline-block; }
.attacks-container .icons-mode .icons-only { display: inline-block; }
/* ── COMMENTS TAB ─────────────────────────────────────────── */
.attacks-container .buttons-container {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
margin-bottom: 8px;
flex-wrap: wrap;
position: sticky;
top: 0;
z-index: 10;
background: var(--_panel);
padding: 6px 8px;
border-radius: 10px;
flex-shrink: 0;
}
.attacks-container .buttons-container h2 {
margin: 0;
margin-right: auto;
font-size: 16px;
}
.attacks-container .comments-container {
display: flex;
flex: 1 1 auto;
min-height: 0;
}
.attacks-container .comments-editor {
flex: 1 1 auto;
min-width: 0;
min-height: 0;
overflow: auto;
white-space: pre;
word-wrap: normal;
background: var(--_panel-lo);
color: var(--_ink);
border: 1px solid var(--_border);
border-radius: 10px;
padding: 14px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 13px;
}
.attacks-container .comments-editor:focus {
outline: none;
border-color: color-mix(in oklab, var(--_acid2) 28%, var(--_border));
box-shadow: 0 0 0 2px color-mix(in oklab, var(--_acid2) 12%, transparent);
background: var(--_panel-hi);
}
.attacks-container .comments-editor.placeholder { color: var(--_muted); }
.attacks-container .comment-line { display: block; width: 100%; }
.attacks-container .comment-line:nth-child(odd) { color: var(--_ink); }
.attacks-container .comment-line:nth-child(even) { color: var(--_acid); }
/* ── Modals (shared) ──────────────────────────────────────── */
.attacks-container .modal-action {
display: none;
position: fixed;
inset: 0;
z-index: 1000;
padding: 10px;
background: rgba(0, 0, 0, .6);
justify-content: center;
align-items: center;
}
.attacks-container .modal-content {
position: relative;
width: 100%;
max-width: 520px;
max-height: 90vh;
overflow-y: auto;
background: var(--_panel-hi);
padding: 20px;
border-radius: 14px;
border: 1px solid var(--_border);
box-shadow: var(--_shadow);
}
.attacks-container .modal-header h3 { margin: 0 0 10px; color: var(--_ink); }
.attacks-container .modal-body { margin-bottom: 16px; }
.attacks-container .modal-footer { display: flex; justify-content: flex-end; gap: 8px; }
.attacks-container .close {
position: absolute;
right: 10px;
top: 10px;
font-size: 20px;
cursor: pointer;
color: var(--_muted);
background: none;
border: none;
}
.attacks-container .form-group { margin-bottom: 14px; }
.attacks-container .form-group label {
display: block;
margin-bottom: 5px;
color: var(--_muted);
font-weight: 700;
font-size: 12px;
}
.attacks-container .form-group input[type="text"],
.attacks-container .form-group input[type="number"],
.attacks-container .form-group input[type="file"] {
width: 100%;
padding: 8px 10px;
color: var(--_ink);
background: var(--_panel-lo);
border: 1px solid var(--_border);
border-radius: 8px;
outline: none;
font-size: 13px;
transition: .15s;
}
.attacks-container .form-group input:focus {
border-color: color-mix(in oklab, var(--_acid2) 28%, var(--_border));
box-shadow: 0 0 0 2px color-mix(in oklab, var(--_acid2) 12%, transparent);
background: var(--_panel-hi);
}
/* ── Sidebar action buttons / hero buttons ────────────────── */
.attacks-container .action-btn-container {
padding: 2px;
gap: 2px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
.attacks-container .hero-btn {
border-radius: 12px;
background: var(--grid), var(--grad-hero);
position: sticky;
bottom: 0;
border: 1px solid var(--c-border);
box-shadow: var(--shadow);
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 4px;
text-align: center;
padding: 6px;
}
/* ── Responsive: mobile main area ─────────────────────────── */
@media (max-width: 900px) {
.attacks-container .attacks-main.page-main {
max-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 12px);
}
}
/* ── Responsive: base (all tabs) ──────────────────────────── */
@media (max-width: 480px) {
.attacks-container .tabs-container { gap: 2px; }
.attacks-container .tab-btn {
font-size: 11px;
padding: 6px 3px;
}
.attacks-container .actions-bar {
gap: 6px;
padding: 6px;
}
.attacks-container .actions-bar button,
.attacks-container .chip,
.attacks-container .select {
padding: 6px 8px;
font-size: 11px;
}
}
/* ==========================================================================
EPD LAYOUT EDITOR
========================================================================== */
/* ── Toolbar — two compact rows ───────────────────────────── */
.epd-editor-toolbar {
display: flex;
flex-direction: column;
gap: 5px;
margin-bottom: 10px;
position: sticky;
top: 0;
z-index: 10;
background: var(--_panel);
padding: 8px 10px;
border-radius: 10px;
border: 1px solid var(--_border);
backdrop-filter: blur(10px);
}
.epd-toolbar-row {
display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
.epd-editor-toolbar .select,
.epd-editor-toolbar .btn {
border-radius: 8px;
border: 1px solid var(--_border);
color: var(--_ink);
background: var(--_panel-lo);
padding: 5px 9px;
cursor: pointer;
transition: .15s;
font-weight: 700;
font-size: 12px;
white-space: nowrap;
}
.epd-editor-toolbar .select { appearance: none; }
.epd-editor-toolbar .btn:hover,
.epd-editor-toolbar .select:hover {
background: var(--_panel-hi);
}
.epd-editor-toolbar .btn.active {
background: color-mix(in oklab, var(--_acid2) 14%, var(--_panel-lo));
border-color: color-mix(in oklab, var(--_acid2) 25%, var(--_border));
}
.epd-editor-toolbar .btn.danger {
background: color-mix(in oklab, var(--_acid) 10%, var(--_panel-lo));
}
.epd-editor-toolbar .btn.danger:hover {
background: color-mix(in oklab, var(--_acid) 16%, var(--_panel-hi));
}
/* ── Zoom ─────────────────────────────────────────────────── */
.epd-zoom-wrap {
display: flex;
align-items: center;
gap: 5px;
font-size: 12px;
font-weight: 600;
}
.epd-zoom-range {
width: 70px;
accent-color: var(--_acid);
}
.epd-zoom-label {
min-width: 32px;
text-align: right;
opacity: .65;
font-size: 11px;
}
/* ── Content row: canvas + live preview ───────────────────── */
.epd-content-row {
display: flex;
gap: 12px;
align-items: flex-start;
min-height: 0;
flex: 1;
}
/* ── Canvas wrapper — scrolls internally, never causes page scroll */
.epd-canvas-wrapper {
flex: 1 1 0;
min-width: 0;
max-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 140px);
overflow: auto;
border-radius: 10px;
border: 1px solid var(--_border);
padding: 12px;
/* Checkerboard transparency */
background-image:
linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
linear-gradient(-45deg, transparent 75%, #e0e0e0 75%);
background-size: 14px 14px;
background-position: 0 0, 0 7px, 7px -7px, -7px 0;
background-color: #f0f0f0;
}
.epd-canvas-wrapper.mode-bn {
background-image:
linear-gradient(45deg, #1a1a1a 25%, transparent 25%),
linear-gradient(-45deg, #1a1a1a 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #1a1a1a 75%),
linear-gradient(-45deg, transparent 75%, #1a1a1a 75%);
background-color: #111;
}
/* Inverted mode — handled at SVG level (bg fill + image filter) */
.epd-canvas-wrapper svg {
display: block;
cursor: crosshair;
}
.epd-canvas-wrapper svg g[data-key] { cursor: move; }
.epd-canvas-wrapper svg g[data-key] rect { transition: stroke-width .1s; }
.epd-canvas-wrapper svg g[data-key]:hover rect { stroke-width: 1.2; }
/* ── Live EPD preview ─────────────────────────────────────── */
.epd-live-panel {
flex: 0 0 auto;
width: 180px;
background: var(--_panel-lo);
border: 1px solid var(--_border);
border-radius: 10px;
padding: 10px;
text-align: center;
position: sticky;
top: 0;
}
.epd-live-panel h4 {
margin: 0 0 6px;
font-size: 12px;
font-weight: 700;
color: var(--_ink);
opacity: .7;
}
.epd-live-img {
width: 100%;
border-radius: 4px;
border: 1px solid var(--_border);
background: #0b0e13;
image-rendering: pixelated;
transition: opacity .3s;
}
/* ── Sidebar: properties panel ────────────────────────────── */
.epd-props-panel {
padding: 0 0 8px;
border-bottom: 1px solid var(--_border);
margin-bottom: 8px;
}
.epd-props-panel h4 {
margin: 0 0 6px;
font-size: 14px;
color: var(--_acid2);
}
.epd-hint {
opacity: .55;
font-size: 12px;
margin: 4px 0;
}
.epd-prop-row {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 3px;
}
.epd-prop-row label {
font-weight: 700;
font-size: 12px;
min-width: 20px;
color: var(--_muted);
}
.epd-prop-input {
width: 60px;
padding: 4px 6px;
font-size: 12px;
color: var(--_ink);
background: var(--_panel-lo);
border: 1px solid var(--_border);
border-radius: 6px;
outline: none;
transition: .15s;
}
.epd-prop-input:focus {
border-color: color-mix(in oklab, var(--_acid2) 28%, var(--_border));
box-shadow: 0 0 0 2px color-mix(in oklab, var(--_acid2) 12%, transparent);
}
.epd-delete-btn {
margin-top: 6px;
width: 100%;
}
/* ── Sidebar: element list ────────────────────────────────── */
.epd-elements-list {
flex: 1;
min-height: 0;
}
.epd-elements-list h4 {
margin: 8px 0 4px;
font-size: 13px;
}
.epd-elements-list .unified-list {
max-height: none;
overflow-y: auto;
}
.epd-element-item {
display: flex;
align-items: center;
gap: 6px;
padding: 5px 8px;
margin-bottom: 2px;
cursor: pointer;
border-radius: 8px;
background: transparent;
border: 1px solid transparent;
transition: background .12s;
font-size: 12px;
}
.epd-element-item:hover {
background: var(--_panel-lo);
}
.epd-element-item.selected {
background: color-mix(in oklab, var(--_acid2) 12%, var(--_panel-hi));
border-color: color-mix(in oklab, var(--_acid2) 25%, var(--_border));
}
.epd-type-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex: 0 0 auto;
}
.epd-list-icon {
width: 16px;
height: 16px;
object-fit: contain;
image-rendering: pixelated;
border-radius: 2px;
flex: 0 0 auto;
background: #0b0e13;
}
.epd-line-dash {
font-weight: 900;
opacity: .4;
min-width: 14px;
font-size: 11px;
}
.epd-coords {
font-size: 10px;
opacity: .4;
font-family: monospace;
margin-left: auto;
}
.epd-list-divider {
padding: 4px 8px;
font-size: 10px;
font-weight: 800;
text-transform: uppercase;
opacity: .4;
letter-spacing: .06em;
list-style: none;
}
/* ── Sidebar: font sizes ──────────────────────────────────── */
.epd-fonts-section {
border-top: 1px solid var(--_border);
padding-top: 6px;
margin-top: 6px;
}
.epd-fonts-section h4 {
margin: 0 0 4px;
font-size: 13px;
}
.epd-fonts-section .epd-prop-row label {
min-width: 80px;
font-size: 11px;
}
.epd-meta-info { opacity: .4; font-size: 10px; }
/* ── Add element modal ────────────────────────────────────── */
.epd-add-modal {
display: none;
position: fixed;
inset: 0;
z-index: 1000;
padding: 10px;
background: rgba(0, 0, 0, .6);
justify-content: center;
align-items: center;
}
.epd-add-modal .modal-content {
width: 100%;
max-width: 360px;
background: var(--_panel-hi);
padding: 18px;
border-radius: 12px;
border: 1px solid var(--_border);
box-shadow: var(--_shadow);
}
.epd-add-modal .form-group { margin-bottom: 10px; }
.epd-add-modal .form-group label {
display: block;
margin-bottom: 4px;
color: var(--_muted);
font-weight: 700;
font-size: 12px;
}
.epd-add-modal .modal-footer {
display: flex;
justify-content: flex-end;
gap: 6px;
margin-top: 14px;
}
/* ── Responsive: EPD ──────────────────────────────────────── */
@media (max-width: 900px) {
.epd-content-row {
flex-direction: column;
}
.epd-live-panel {
width: 100%;
max-width: 100%;
position: static;
}
.epd-canvas-wrapper {
max-height: 65vh;
}
}
@media (max-width: 480px) {
.epd-editor-toolbar {
gap: 3px;
padding: 6px;
}
.epd-toolbar-row { gap: 4px; }
.epd-editor-toolbar .select,
.epd-editor-toolbar .btn {
padding: 4px 7px;
font-size: 11px;
}
.epd-zoom-range { width: 50px; }
.epd-canvas-wrapper { padding: 6px; max-height: 55vh; }
.epd-live-panel { padding: 6px; }
}

274
web/css/pages/backup.css Normal file
View File

@@ -0,0 +1,274 @@
/* ===== BACKUP PAGE (NEW SPA LAYOUT) ===== */
.page-backup {
padding: 10px;
}
.page-backup {
padding: 10px;
}
.page-backup .backup-sidebar,
.page-backup .backup-main {
border: 1px solid var(--c-border);
border-radius: 12px;
background: var(--grad-card);
box-shadow: var(--shadow);
}
.page-backup .backup-sidebar {
padding: 12px;
display: grid;
align-content: start;
gap: 10px;
}
.page-backup .backup-sidehead {
border-bottom: 1px dashed var(--c-border);
padding-bottom: 8px;
}
.page-backup .backup-side-title {
margin: 0;
color: var(--acid);
font-size: 14px;
letter-spacing: .04em;
text-transform: uppercase;
}
.page-backup .backup-nav-item {
width: 100%;
border: 1px solid var(--c-border);
border-radius: 12px;
background: color-mix(in oklab, var(--panel) 88%, transparent);
color: var(--ink);
display: flex;
align-items: center;
gap: 10px;
padding: 10px;
cursor: pointer;
transition: .18s;
text-align: left;
}
.page-backup .backup-nav-item:hover {
transform: translateY(-1px);
box-shadow: var(--shadow);
}
.page-backup .backup-nav-item.active {
border-color: color-mix(in oklab, var(--acid) 45%, var(--c-border));
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--acid-2) 30%, transparent);
}
.page-backup .backup-nav-icon {
width: 42px;
height: 42px;
object-fit: contain;
border-radius: 8px;
background: rgba(0, 0, 0, .2);
}
.page-backup .backup-nav-label {
font-weight: 700;
letter-spacing: .01em;
}
.page-backup .backup-main {
padding: 14px;
overflow: auto;
}
.page-backup .backup-title {
margin: 0 0 12px 0;
}
.page-backup .backup-form {
margin-bottom: 14px;
}
.page-backup .backup-label {
display: block;
margin-bottom: 8px;
color: var(--muted);
}
.page-backup .backup-form-row {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.page-backup .backup-input {
flex: 1;
min-width: 220px;
border: 1px solid var(--c-border);
border-radius: 10px;
background: var(--c-panel);
color: var(--ink);
padding: 10px 12px;
}
.page-backup .backup-subtitle {
margin: 10px 0;
color: var(--muted);
font-size: 13px;
text-transform: uppercase;
letter-spacing: .03em;
}
.page-backup .backup-table-wrap {
overflow: auto;
border: 1px solid var(--c-border);
border-radius: 12px;
}
.page-backup .backup-table {
width: 100%;
border-collapse: collapse;
}
.page-backup .backup-table th,
.page-backup .backup-table td {
padding: 10px;
border-bottom: 1px dashed var(--c-border);
text-align: left;
vertical-align: top;
}
.page-backup .backup-row-actions {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.page-backup .backup-default-pill {
margin-left: 8px;
}
.page-backup .backup-empty {
padding: 22px;
text-align: center;
color: var(--muted);
}
.page-backup .backup-update-message {
background: color-mix(in oklab, var(--ok) 18%, transparent);
border: 1px solid color-mix(in oklab, var(--ok) 40%, var(--c-border));
border-radius: 999px;
padding: 10px 14px;
display: inline-block;
margin-bottom: 12px;
}
.page-backup .backup-version-lines {
display: grid;
gap: 4px;
}
.page-backup .backup-update-available {
color: var(--acid);
font-weight: 700;
}
.page-backup .backup-update-ok {
color: var(--ok);
font-weight: 700;
}
.page-backup .backup-update-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.page-backup .backup-modal-overlay {
position: fixed;
inset: 0;
z-index: 1200;
background: rgba(0, 0, 0, .6);
display: none;
align-items: center;
justify-content: center;
padding: 10px;
}
.page-backup .backup-modal {
width: min(480px, 95vw);
background: var(--grad-card);
border: 1px solid var(--c-border);
border-radius: 14px;
padding: 12px;
box-shadow: var(--shadow-hover);
}
.page-backup .backup-modal-head {
display: flex;
align-items: center;
gap: 8px;
justify-content: space-between;
}
.page-backup .backup-modal-title {
margin: 0;
}
.page-backup .backup-modal-help {
color: var(--muted);
margin: 8px 0 10px 0;
}
.page-backup .backup-keep {
display: flex;
gap: 8px;
align-items: center;
padding: 4px 0;
}
.page-backup .backup-modal-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 12px;
padding-top: 10px;
border-top: 1px dashed var(--c-border);
}
.page-backup .backup-loading-overlay {
position: fixed;
inset: 0;
z-index: 1300;
background: rgba(0, 0, 0, .6);
display: none;
align-items: center;
justify-content: center;
}
.page-backup .backup-spinner {
width: 52px;
height: 52px;
border: 4px solid transparent;
border-top-color: var(--accent-2);
border-right-color: var(--accent-2);
border-radius: 50%;
animation: bak-spin .9s linear infinite;
}
/* Integrated layout provided by shared.css page-with-sidebar */
.page-backup.page-with-sidebar {
--page-sidebar-w: 280px;
min-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 60px) - 24px);
}
.page-backup.page-with-sidebar .backup-sidebar {
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
}
.page-backup.page-with-sidebar .backup-main {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
}

518
web/css/pages/bifrost.css Normal file
View File

@@ -0,0 +1,518 @@
/* ============================================================
Bifrost (Pwnagotchi Mode) — SPA page styles
============================================================ */
.bifrost-page {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
gap: 12px;
padding: 15px;
}
/* ── Header bar ─────────────────────────────────────────── */
.bifrost-header {
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
flex-wrap: wrap;
gap: 10px;
}
.bifrost-title {
margin: 0;
font-size: 1.3rem;
font-weight: 800;
color: var(--ink);
display: flex;
align-items: center;
gap: 8px;
}
.bifrost-title-icon { font-size: 1.5rem; }
.bifrost-controls {
display: flex;
gap: 8px;
align-items: center;
}
.bifrost-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 14px;
border-radius: 8px;
border: 1px solid var(--c-border);
background: var(--c-panel);
font-size: 0.8rem;
font-weight: 700;
color: var(--ink);
cursor: pointer;
transition: 0.2s;
}
.bifrost-btn.active {
border-color: var(--acid);
background: rgba(0, 255, 154, 0.08);
color: var(--acid);
box-shadow: 0 0 12px rgba(0, 255, 154, 0.15);
}
.bifrost-btn .dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--muted-off, #555);
transition: 0.2s;
}
.bifrost-btn.active .dot {
background: var(--acid);
box-shadow: 0 0 6px var(--acid);
animation: bifrost-pulse 2s infinite;
}
@keyframes bifrost-pulse {
0%, 100% { opacity: 0.7; box-shadow: 0 0 4px var(--acid); }
50% { opacity: 1; box-shadow: 0 0 12px var(--acid); }
}
/* ── Stats bar ──────────────────────────────────────────── */
.bifrost-stats {
display: flex;
gap: 10px;
flex-shrink: 0;
flex-wrap: wrap;
}
.bifrost-stat {
flex: 1 1 100px;
padding: 10px 14px;
background: var(--grad-card);
border: 1px solid var(--c-border);
border-radius: 10px;
text-align: center;
min-width: 80px;
}
.bifrost-stat-val {
font-size: 1.4rem;
font-weight: 800;
font-family: 'Fira Code', monospace;
color: var(--ink);
line-height: 1.2;
}
.bifrost-stat-lbl {
font-size: 0.65rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-top: 4px;
}
/* ── Main grid ──────────────────────────────────────────── */
.bifrost-grid {
display: grid;
grid-template-columns: 1fr 340px;
gap: 12px;
flex: 1;
min-height: 0;
}
/* ── Panels ─────────────────────────────────────────────── */
.bifrost-panel {
background: var(--grad-card);
border: 1px solid var(--c-border);
border-radius: 12px;
display: flex;
flex-direction: column;
overflow: hidden;
min-height: 0;
}
.bifrost-panel-head {
padding: 10px 14px;
font-size: 0.72rem;
font-weight: 700;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 1px;
border-bottom: 1px solid var(--c-border);
background: rgba(0, 0, 0, 0.15);
flex-shrink: 0;
display: flex;
justify-content: space-between;
align-items: center;
}
/* ── Face display ──────────────────────────────────────── */
.bifrost-live {
padding: 0;
}
.bifrost-face-wrap {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 30px 20px 16px;
gap: 8px;
flex-shrink: 0;
}
.bifrost-face {
font-family: 'Fira Code', 'Courier New', monospace;
font-size: 2.8rem;
font-weight: 700;
color: var(--acid);
text-shadow:
0 0 10px rgba(0, 255, 154, 0.4),
0 0 30px rgba(0, 255, 154, 0.15);
text-align: center;
white-space: nowrap;
line-height: 1.2;
user-select: none;
transition: color 0.4s, text-shadow 0.4s;
}
/* Mood-specific face colors */
.bifrost-face.mood-excited {
color: #00ff9a;
text-shadow: 0 0 15px rgba(0, 255, 154, 0.6), 0 0 40px rgba(0, 255, 154, 0.25);
}
.bifrost-face.mood-happy { color: #00ff9a; }
.bifrost-face.mood-grateful { color: #00dcff; text-shadow: 0 0 10px rgba(0, 220, 255, 0.4); }
.bifrost-face.mood-bored { color: #ffd166; text-shadow: 0 0 10px rgba(255, 209, 102, 0.3); }
.bifrost-face.mood-sad { color: #7ba5c9; text-shadow: 0 0 10px rgba(123, 165, 201, 0.3); }
.bifrost-face.mood-angry { color: #ff3b3b; text-shadow: 0 0 10px rgba(255, 59, 59, 0.4); }
.bifrost-face.mood-lonely { color: #b48cff; text-shadow: 0 0 10px rgba(180, 140, 255, 0.3); }
.bifrost-face.mood-sleeping { color: var(--muted); text-shadow: none; }
/* Mood badge */
.bifrost-mood {
font-size: 0.72rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 1px;
padding: 3px 12px;
border-radius: 20px;
}
.mood-badge-excited { background: rgba(0,255,154,0.15); color: #00ff9a; }
.mood-badge-happy { background: rgba(0,255,154,0.12); color: #00ff9a; }
.mood-badge-grateful { background: rgba(0,220,255,0.15); color: #00dcff; }
.mood-badge-bored { background: rgba(255,209,102,0.15); color: #ffd166; }
.mood-badge-sad { background: rgba(123,165,201,0.15); color: #7ba5c9; }
.mood-badge-angry { background: rgba(255,59,59,0.15); color: #ff3b3b; }
.mood-badge-lonely { background: rgba(180,140,255,0.15); color: #b48cff; }
.mood-badge-sleeping { background: rgba(255,255,255,0.06); color: var(--muted); }
.mood-badge-starting { background: rgba(255,255,255,0.06); color: var(--muted); }
.mood-badge-ready { background: rgba(0,255,154,0.08); color: var(--acid); }
.bifrost-voice {
font-size: 0.78rem;
color: var(--muted);
font-style: italic;
text-align: center;
max-width: 300px;
line-height: 1.4;
min-height: 1.4em;
}
/* ── Info chips ────────────────────────────────────────── */
.bifrost-info-row {
display: flex;
gap: 6px;
flex-wrap: wrap;
justify-content: center;
padding: 8px 14px;
border-bottom: 1px solid var(--c-border);
flex-shrink: 0;
}
.bifrost-info-chip {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
background: rgba(0,0,0,0.2);
border-radius: 6px;
font-size: 0.7rem;
font-family: 'Fira Code', monospace;
}
.bifrost-info-chip.pwnd {
background: rgba(0,255,154,0.1);
border: 1px solid rgba(0,255,154,0.2);
}
.bifrost-info-label {
color: var(--muted);
font-weight: 600;
}
.bifrost-info-value {
color: var(--ink);
font-weight: 700;
}
/* ── Activity feed ─────────────────────────────────────── */
.bifrost-activity {
flex: 1;
overflow-y: auto;
padding: 8px 12px;
display: flex;
flex-direction: column;
gap: 2px;
}
.bifrost-activity-item {
display: flex;
align-items: baseline;
gap: 6px;
padding: 3px 0;
font-size: 0.73rem;
border-bottom: 1px solid rgba(255,255,255,0.03);
}
.bifrost-act-time {
color: var(--muted);
font-family: 'Fira Code', monospace;
font-size: 0.65rem;
flex-shrink: 0;
min-width: 28px;
}
.bifrost-act-icon {
flex-shrink: 0;
font-size: 0.75rem;
}
.bifrost-act-title {
color: var(--ink);
font-weight: 600;
}
.bifrost-act-detail {
color: var(--muted);
font-size: 0.68rem;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bifrost-empty {
color: var(--muted);
text-align: center;
padding: 30px 10px;
font-size: 0.8rem;
}
/* ── Sidebar tabs ──────────────────────────────────────── */
.bifrost-side-tabs {
display: flex;
gap: 2px;
padding: 6px;
background: rgba(0, 0, 0, 0.15);
border-bottom: 1px solid var(--c-border);
flex-shrink: 0;
}
.bifrost-side-tab {
flex: 1;
padding: 5px 8px;
border: none;
border-radius: 6px;
background: transparent;
color: var(--muted);
font-size: 0.7rem;
font-weight: 700;
cursor: pointer;
transition: 0.15s;
}
.bifrost-side-tab.active {
background: var(--c-panel);
color: var(--acid);
}
.bifrost-sidebar {
flex: 1;
overflow-y: auto;
padding: 8px;
display: flex;
flex-direction: column;
gap: 4px;
}
/* ── Network rows ──────────────────────────────────────── */
.bifrost-net-row {
padding: 6px 10px;
border-radius: 6px;
border: 1px solid var(--c-border);
background: color-mix(in oklab, var(--c-panel) 60%, transparent);
}
.bifrost-net-main {
display: flex;
align-items: center;
gap: 8px;
}
.bifrost-net-signal {
font-size: 0.65rem;
color: var(--acid);
letter-spacing: -1px;
flex-shrink: 0;
min-width: 32px;
}
.bifrost-net-essid {
font-size: 0.78rem;
font-weight: 700;
color: var(--ink);
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bifrost-net-enc {
font-size: 0.58rem;
font-weight: 800;
text-transform: uppercase;
padding: 1px 5px;
border-radius: 3px;
letter-spacing: 0.5px;
flex-shrink: 0;
background: rgba(0,220,255,0.12);
color: #00dcff;
}
.bifrost-net-meta {
font-size: 0.62rem;
color: var(--muted);
margin-top: 2px;
font-family: 'Fira Code', monospace;
}
/* ── Plugin rows ───────────────────────────────────────── */
.bifrost-plugin-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
padding: 6px 10px;
border-radius: 6px;
border: 1px solid var(--c-border);
background: color-mix(in oklab, var(--c-panel) 60%, transparent);
}
.bifrost-plugin-info {
flex: 1;
min-width: 0;
}
.bifrost-plugin-name {
font-size: 0.78rem;
font-weight: 700;
color: var(--ink);
display: block;
}
.bifrost-plugin-desc {
font-size: 0.65rem;
color: var(--muted);
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ── Epoch table ───────────────────────────────────────── */
.bifrost-epoch-table {
display: flex;
flex-direction: column;
gap: 1px;
font-size: 0.72rem;
font-family: 'Fira Code', monospace;
}
.bifrost-epoch-header,
.bifrost-epoch-row {
display: grid;
grid-template-columns: 40px 35px 35px 35px 1fr 50px;
gap: 4px;
padding: 4px 8px;
align-items: center;
}
.bifrost-epoch-header {
font-weight: 800;
color: var(--muted);
text-transform: uppercase;
font-size: 0.6rem;
border-bottom: 1px solid var(--c-border);
position: sticky;
top: 0;
background: var(--c-panel);
z-index: 1;
}
.bifrost-epoch-row {
border-bottom: 1px solid rgba(255,255,255,0.03);
color: var(--ink);
}
.bifrost-epoch-row:hover {
background: rgba(255,255,255,0.03);
}
/* ── Responsive ────────────────────────────────────────── */
@media (max-width: 900px) {
.bifrost-grid {
grid-template-columns: 1fr;
}
.bifrost-stats {
flex-wrap: wrap;
}
.bifrost-stat {
flex: 1 1 70px;
min-width: 60px;
padding: 8px 8px;
}
.bifrost-stat-val { font-size: 1.1rem; }
.bifrost-page {
padding: 10px;
gap: 8px;
}
.bifrost-header {
flex-direction: column;
align-items: flex-start;
}
.bifrost-face { font-size: 2rem; }
.bifrost-face-wrap { padding: 20px 12px 12px; }
}

33
web/css/pages/bjorn.css Normal file
View File

@@ -0,0 +1,33 @@
/* ==========================================================================
BJORN
========================================================================== */
.bjorn-container .image-container {
display: flex;
justify-content: center;
align-items: center;
height: calc(100vh - 70px);
}
.bjorn-container .image-container img {
max-height: 100%;
max-width: 100%;
height: -webkit-fill-available;
cursor: pointer;
transition: transform 0.2s ease-in-out;
}
.bjorn-container .image-container img:active {
transform: scale(1.05);
}
.bjorn-container .image-container.fullscreen img {
height: 100vh;
width: auto;
}
@media (max-width:768px) {
.bjorn-container .image-container {
height: calc(100vh - 60px);
}
}

1814
web/css/pages/compat.css Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,370 @@
/* ===== CREDENTIALS ===== */
.credentials-container {
display: flex;
flex-direction: column;
gap: 12px;
scroll-padding-top: 56px;
}
.credentials-container .stats-bar {
display: flex;
gap: 12px;
flex-wrap: wrap;
padding: 12px;
background: color-mix(in oklab, var(--_panel) 88%, transparent);
border: 1px solid var(--_border);
border-radius: 12px;
box-shadow: var(--_shadow);
backdrop-filter: blur(16px);
}
.credentials-container .stat-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border: 1px solid var(--_border);
border-radius: 10px;
background: color-mix(in oklab, var(--_panel) 70%, transparent);
}
.credentials-container .stat-icon {
font-size: 1.1rem;
opacity: .9;
}
.credentials-container .stat-value {
font-weight: 800;
background: linear-gradient(135deg, var(--_acid), var(--_acid2));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.credentials-container .stat-label {
color: var(--_muted);
font-size: .8rem;
}
.credentials-container .global-search-container {
position: relative;
}
.credentials-container .global-search-input {
width: 100%;
padding: 10px 14px;
border-radius: 12px;
border: 1px solid var(--_border);
background: color-mix(in oklab, var(--_panel) 90%, transparent);
color: var(--_ink);
}
.credentials-container .global-search-input:focus {
outline: none;
border-color: color-mix(in oklab, var(--_acid2) 40%, var(--_border));
box-shadow: 0 0 0 3px color-mix(in oklab, var(--_acid2) 18%, transparent);
}
.credentials-container .clear-global-button {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: 1px solid var(--_border);
color: #ef4444;
border-radius: 8px;
padding: 2px 6px;
display: none;
}
.credentials-container .clear-global-button.show {
display: block;
}
.credentials-container .tabs-container {
position: sticky;
top: 0;
z-index: 20;
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
min-height: 44px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
background: color-mix(in oklab, var(--_panel) 92%, transparent);
border: 1px solid var(--_border);
border-radius: 12px;
box-shadow: var(--_shadow);
}
.credentials-container .tabs-container::-webkit-scrollbar {
height: 0;
}
.credentials-container .tab {
padding: 10px 18px;
border-radius: 10px;
cursor: pointer;
color: var(--_muted);
font-weight: 700;
font-size: .9rem;
border: 1px solid transparent;
white-space: nowrap;
flex: 0 0 auto;
}
.credentials-container .tab:hover {
background: rgba(255, 255, 255, .05);
color: var(--_ink);
border-color: var(--_border);
}
.credentials-container .tab.active {
color: var(--_ink);
background: linear-gradient(135deg, color-mix(in oklab, var(--_acid2) 18%, transparent), color-mix(in oklab, var(--_acid) 14%, transparent));
border-color: color-mix(in oklab, var(--_acid2) 28%, var(--_border));
}
.credentials-container .tab-badge {
margin-left: 8px;
padding: 2px 6px;
border-radius: 999px;
background: rgba(255, 255, 255, .1);
border: 1px solid var(--_border);
font-size: .75rem;
}
.credentials-container .services-grid {
display: flex;
flex-direction: column;
gap: 12px;
}
.credentials-container .service-card {
background: color-mix(in oklab, var(--_panel) 88%, transparent);
border: 1px solid var(--_border);
border-radius: 16px;
overflow: hidden;
box-shadow: var(--_shadow);
}
.credentials-container .service-header {
display: flex;
align-items: center;
gap: 8px;
padding: 12px;
cursor: pointer;
user-select: none;
border-bottom: 1px solid color-mix(in oklab, var(--_border) 65%, transparent);
}
.credentials-container .service-header:hover {
background: rgba(255, 255, 255, .04);
}
.credentials-container .service-title {
flex: 1;
font-weight: 800;
letter-spacing: .2px;
font-size: .95rem;
text-transform: uppercase;
background: linear-gradient(135deg, var(--_acid), var(--_acid2));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.credentials-container .service-count {
font-weight: 800;
font-size: .8rem;
padding: 4px 8px;
border-radius: 10px;
background: rgba(255, 255, 255, .08);
color: var(--_ink);
border: 1px solid var(--_border);
}
.credentials-container .service-card[data-credentials]:not([data-credentials="0"]) .service-count {
background: linear-gradient(135deg, #2e2e2e, #4CAF50);
box-shadow: inset 0 0 0 1px rgba(76, 175, 80, .35);
}
.credentials-container .search-container {
position: relative;
}
.credentials-container .search-input {
padding: 6px 24px 6px 8px;
border: none;
border-radius: 10px;
background: rgba(255, 255, 255, .06);
color: var(--_ink);
font-size: .82rem;
}
.credentials-container .search-input:focus {
outline: none;
background: rgba(255, 255, 255, .1);
}
.credentials-container .clear-button {
position: absolute;
right: 4px;
top: 50%;
transform: translateY(-50%);
border: none;
background: none;
color: #ef4444;
cursor: pointer;
display: none;
}
.credentials-container .clear-button.show {
display: block;
}
.credentials-container .download-button {
border: 1px solid var(--_border);
background: rgba(255, 255, 255, .04);
color: var(--_muted);
border-radius: 8px;
padding: 4px 8px;
cursor: pointer;
}
.credentials-container .download-button:hover {
color: #e99f00;
filter: brightness(1.06);
}
.credentials-container .collapse-indicator {
color: var(--_muted);
}
.credentials-container .service-card.collapsed .service-content {
max-height: 0;
overflow: hidden;
}
.credentials-container .service-content {
padding: 8px 12px;
}
.credentials-container .credential-item {
border: 1px solid var(--_border);
border-radius: 10px;
margin-bottom: 6px;
padding: 8px;
background: rgba(255, 255, 255, .02);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 8px;
}
.credentials-container .credential-field {
display: flex;
align-items: center;
gap: 6px;
}
.credentials-container .field-label {
font-size: .78rem;
color: var(--_muted);
}
.credentials-container .field-value {
flex: 1;
padding: 2px 6px;
border-radius: 8px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border: 1px solid transparent;
}
.credentials-container .field-value:hover {
background: rgba(255, 255, 255, .06);
border-color: var(--_border);
}
.credentials-container .bubble-blue {
background: linear-gradient(135deg, #1d2a32, #00c4d6);
color: #fff;
}
.credentials-container .bubble-green {
background: linear-gradient(135deg, #1e2a24, #00b894);
color: #fff;
}
.credentials-container .bubble-orange {
background: linear-gradient(135deg, #3b2f1a, #e7951a);
color: #fff;
}
.credentials-container .copied-feedback {
position: fixed;
left: 50%;
bottom: 20px;
transform: translateX(-50%);
padding: 8px 12px;
background: #4CAF50;
color: #fff;
border-radius: 10px;
box-shadow: var(--_shadow);
opacity: 0;
transition: opacity .25s;
z-index: 9999;
}
.credentials-container .copied-feedback.show {
opacity: 1;
}
/* ── Mobile responsive ── */
@media (max-width: 768px) {
.credentials-container .stats-bar {
flex-wrap: wrap;
gap: 8px;
}
.credentials-container .tabs-container {
flex-wrap: wrap;
gap: 4px;
}
.credentials-container .tab {
font-size: .75rem;
padding: .35rem .6rem;
}
.credentials-container .services-grid {
grid-template-columns: 1fr;
}
.credentials-container .service-header {
flex-wrap: wrap;
gap: 6px;
}
.credentials-container .search-container {
width: 100%;
order: 10;
}
.credentials-container .credential-field {
flex-direction: column;
gap: 2px;
}
.credentials-container .field-value {
word-break: break-all;
}
}

660
web/css/pages/dashboard.css Normal file
View File

@@ -0,0 +1,660 @@
/* ===== MODERN DASHBOARD ===== */
.dashboard-container.modern-dash {
--pad: 12px;
--gap: 12px;
--card-bg: color-mix(in oklab, var(--_panel) 40%, transparent);
--card-border: color-mix(in oklab, var(--_border) 80%, transparent);
--card-radius: 16px;
--anim-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
--accent: var(--_acid, #00ff9a);
--glass: blur(12px);
display: flex;
flex-direction: column;
gap: var(--gap);
padding: 8px;
max-width: 1400px;
margin: 0 auto;
}
@media (min-width: 768px) {
.dashboard-container.modern-dash {
padding: 16px;
--gap: 16px;
}
}
/* Animations */
@keyframes slip-up {
0% {
opacity: 0;
transform: translateY(12px) scale(0.98);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.modern-dash .anim-enter {
animation: slip-up 0.5s var(--anim-ease) both;
}
.modern-dash .anim-enter:nth-child(1) {
animation-delay: 0.0s;
}
.modern-dash .anim-enter:nth-child(2) {
animation-delay: 0.1s;
}
.modern-dash .anim-enter:nth-child(3) {
animation-delay: 0.15s;
}
/* Top Bar */
.modern-dash .top-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4px 6px;
font-size: 11px;
}
.modern-dash .liveops {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
padding: 6px 12px;
border-radius: 20px;
background: var(--card-bg);
border: 1px solid var(--card-border);
backdrop-filter: var(--glass);
-webkit-backdrop-filter: var(--glass);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
font-weight: 700;
color: var(--_ink);
}
.modern-dash .liveops:hover {
background: color-mix(in oklab, var(--_panel) 70%, transparent);
border-color: var(--accent);
transform: translateY(-1px);
}
.modern-dash .liveops-time {
font-weight: 500;
color: var(--_muted);
}
.modern-dash .pulse-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--ok);
box-shadow: 0 0 8px var(--ok);
animation: pulse-dot-anim 1.5s infinite;
}
@keyframes pulse-dot-anim {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: .5;
transform: scale(1.3);
}
}
.modern-dash .sys-badges {
display: flex;
gap: 6px;
}
.modern-dash .badge {
padding: 6px 10px;
border-radius: 12px;
background: var(--card-bg);
border: 1px solid var(--card-border);
font-family: ui-monospace, monospace;
font-size: 11px;
font-weight: 700;
color: var(--_ink);
backdrop-filter: var(--glass);
-webkit-backdrop-filter: var(--glass);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
justify-content: center;
}
.modern-dash .mode-badge {
color: var(--accent);
border-color: color-mix(in oklab, var(--accent) 30%, transparent);
}
/* Main layout grid */
.modern-dash .dash-main {
display: grid;
grid-template-columns: 1fr;
gap: var(--gap);
}
@media(min-width: 900px) {
.modern-dash .dash-main {
grid-template-columns: 340px 1fr;
}
}
@media(min-width: 1200px) {
.modern-dash .dash-main {
grid-template-columns: 380px 1fr;
}
}
.modern-dash .dash-col-left,
.modern-dash .dash-col-right {
display: flex;
flex-direction: column;
gap: var(--gap);
}
/* Card Base */
.modern-dash .dash-card {
background: var(--card-bg);
border: 1px solid var(--card-border);
border-radius: var(--card-radius);
padding: var(--pad);
position: relative;
overflow: hidden;
backdrop-filter: var(--glass);
-webkit-backdrop-filter: var(--glass);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.modern-dash .dash-card:hover {
border-color: color-mix(in oklab, var(--_border) 100%, transparent);
}
/* Hero Card */
.modern-dash .hero-card {
display: flex;
align-items: stretch;
padding: 16px;
gap: 16px;
background: linear-gradient(135deg, color-mix(in oklab, var(--_panel) 60%, transparent), color-mix(in oklab, var(--_panel) 20%, transparent));
}
.modern-dash .hero-left {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
}
.modern-dash .hero-divider {
width: 1px;
background: var(--card-border);
margin: 4px 0;
}
.modern-dash .hero-right {
flex: 1 1 auto;
display: flex;
flex-direction: column;
justify-content: center;
min-width: 0;
}
/* Battery Widget */
.modern-dash .battery-wrap {
position: relative;
width: 90px;
height: 90px;
display: grid;
place-items: center;
}
@media(min-width: 400px) {
.modern-dash .battery-wrap {
width: 110px;
height: 110px;
}
}
.modern-dash .battery-ring {
position: absolute;
left: 50%;
top: 50%;
width: 100%;
height: 100%;
transform: translate(-50%, -50%) rotate(-90deg);
display: block;
}
.modern-dash .batt-bg {
fill: none;
stroke: color-mix(in oklab, var(--_ink) 10%, transparent);
stroke-width: 13;
opacity: .35;
}
.modern-dash .batt-fg {
fill: none;
stroke: url(#batt-grad);
stroke-width: 13;
stroke-linecap: round;
filter: url(#batt-glow);
stroke-dasharray: 100;
stroke-dashoffset: 100;
transition: stroke-dashoffset 0.9s cubic-bezier(0.4, 0, 0.2, 1);
}
.modern-dash .batt-scan {
fill: none;
stroke: var(--_acid2);
stroke-width: 13;
stroke-linecap: round;
stroke-dasharray: 8 280;
opacity: .14;
transform-origin: 50% 50%;
animation: batt-spin 2.2s linear infinite;
}
@keyframes batt-spin {
to {
transform: rotate(360deg);
}
}
.modern-dash .batt-center {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
}
.modern-dash .bjorn-portrait {
width: 32px;
height: 32px;
position: relative;
margin-bottom: 2px;
}
@media(min-width: 400px) {
.modern-dash .bjorn-portrait {
width: 40px;
height: 40px;
}
}
.modern-dash .bjorn-portrait img {
width: 100%;
height: 100%;
object-fit: contain;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
}
.modern-dash .bjorn-lvl {
position: absolute;
right: -8px;
bottom: -4px;
font-size: 8px;
font-weight: 800;
padding: 1px 4px;
border-radius: 6px;
background: var(--_panel);
color: var(--accent);
border: 1px solid var(--card-border);
}
.modern-dash .batt-val {
font-size: 18px;
font-weight: 800;
line-height: 1;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
display: flex;
align-items: baseline;
}
.modern-dash .batt-state {
display: flex;
align-items: center;
gap: 4px;
font-size: 9px;
margin-top: 2px;
color: var(--_muted);
}
.modern-dash .hide-mobile {
display: none;
}
@media(min-width: 1200px) {
.modern-dash .hide-mobile {
display: inline;
}
}
.modern-dash .batt-indicator svg {
width: 12px;
height: 12px;
stroke: currentColor;
fill: none;
stroke-width: 2;
margin-top: 2px;
}
/* System Bars Widget */
.modern-dash .sys-bars {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.modern-dash .sys-row .sys-lbl {
display: flex;
justify-content: space-between;
font-size: 10px;
font-weight: 700;
color: var(--_muted);
margin-bottom: 4px;
}
.modern-dash .sys-vals {
color: var(--_ink);
font-weight: 600;
font-family: ui-monospace, monospace;
}
.modern-dash .bar {
width: 100%;
height: 6px;
border-radius: 4px;
background: color-mix(in oklab, var(--_ink) 10%, transparent);
overflow: hidden;
}
.modern-dash .bar i {
display: block;
height: 100%;
width: 0%;
border-radius: 4px;
background: linear-gradient(90deg, var(--accent), var(--_acid2));
transition: width 0.5s ease;
}
.modern-dash .bar i.warm {
background: linear-gradient(90deg, #ffd166, #ffbe55);
}
.modern-dash .bar i.hot {
background: linear-gradient(90deg, #ff4d6d, #ff6b6b);
}
/* Connectivity Card */
.modern-dash .net-card {
padding: var(--pad);
}
.modern-dash .net-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.modern-dash .net-title {
font-size: 14px;
font-weight: 800;
color: var(--_ink);
}
.modern-dash .net-badge-wrap {
font-size: 11px;
font-weight: 600;
color: var(--_muted);
display: flex;
align-items: center;
gap: 6px;
}
.modern-dash .net-badge {
padding: 2px 8px;
border-radius: 10px;
font-size: 10px;
font-weight: 800;
background: color-mix(in oklab, var(--danger, #ff4d6d) 20%, transparent);
color: var(--danger, #ff4d6d);
border: 1px solid color-mix(in oklab, var(--danger, #ff4d6d) 40%, transparent);
}
.modern-dash .net-badge.net-on {
background: color-mix(in oklab, var(--ok) 20%, transparent);
color: var(--ok);
border-color: color-mix(in oklab, var(--ok) 40%, transparent);
}
.modern-dash .conn-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
}
.modern-dash .conn-box {
background: var(--card-bg);
border: 1px solid var(--card-border);
border-radius: 12px;
padding: 10px 4px;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
position: relative;
transition: all 0.2s;
}
.modern-dash .conn-box:hover {
background: color-mix(in oklab, var(--_panel) 80%, transparent);
transform: translateY(-2px);
}
.modern-dash .conn-icon svg {
width: 22px;
height: 22px;
stroke: var(--_muted);
fill: none;
stroke-width: 1.5;
}
.modern-dash .conn-box.on .conn-icon svg {
stroke: var(--accent);
filter: drop-shadow(0 0 6px rgba(0, 255, 154, 0.4));
}
.modern-dash .conn-lbl {
font-size: 11px;
font-weight: 600;
color: var(--_ink);
}
.modern-dash .state-pill {
font-size: 9px;
font-weight: 800;
padding: 2px 6px;
border-radius: 6px;
background: color-mix(in oklab, var(--_ink) 10%, transparent);
color: var(--_muted);
text-transform: uppercase;
}
.modern-dash .conn-box.on .state-pill {
background: color-mix(in oklab, var(--ok) 20%, transparent);
color: var(--ok);
}
.modern-dash .conn-det-wrap {
display: none;
}
/* KPI Grid */
.modern-dash .kpi-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--gap);
}
@media(min-width: 480px) {
.modern-dash .kpi-grid {
grid-template-columns: repeat(4, 1fr);
}
}
@media(min-width: 1024px) {
.modern-dash .kpi-grid {
grid-template-columns: repeat(4, 1fr);
}
}
.modern-dash .kpi-box {
background: var(--card-bg);
border: 1px solid var(--card-border);
border-radius: var(--card-radius);
padding: 12px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
backdrop-filter: var(--glass);
-webkit-backdrop-filter: var(--glass);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
transition: transform 0.2s, background 0.2s;
text-align: center;
}
.modern-dash .kpi-box:hover {
transform: translateY(-2px);
background: color-mix(in oklab, var(--_panel) 60%, transparent);
border-color: color-mix(in oklab, var(--_border) 100%, transparent);
}
.modern-dash .kpi-ico {
margin-bottom: 6px;
}
.modern-dash .kpi-ico svg {
width: 24px;
height: 24px;
stroke: var(--_muted);
fill: none;
stroke-width: 1.5;
opacity: 0.6;
}
.modern-dash .kpi-val {
font-size: 20px;
font-weight: 800;
color: var(--_ink);
line-height: 1;
margin-bottom: 4px;
display: flex;
align-items: baseline;
justify-content: center;
gap: 4px;
}
.modern-dash .kpi-val.multi-val {
font-size: 16px;
}
.modern-dash .kpi-val .dim {
opacity: 0.6;
font-size: 0.85em;
}
.modern-dash .kpi-lbl {
font-size: 10px;
text-transform: uppercase;
font-weight: 700;
color: var(--_muted);
letter-spacing: 0.5px;
}
.modern-dash .kpi-extra {
margin-top: 4px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.modern-dash .delta {
font-size: 9px;
font-weight: 700;
padding: 2px 6px;
border-radius: 6px;
background: color-mix(in oklab, var(--_ink) 10%, transparent);
color: var(--_muted);
}
.modern-dash .delta.good {
background: color-mix(in oklab, var(--ok) 20%, transparent);
color: var(--ok);
border: 1px solid color-mix(in oklab, var(--ok) 40%, transparent);
}
.modern-dash .delta.bad {
background: color-mix(in oklab, var(--danger, #ff4d6d) 20%, transparent);
color: var(--danger, #ff4d6d);
border: 1px solid color-mix(in oklab, var(--danger, #ff4d6d) 40%, transparent);
}
/* Footer info */
.modern-dash .footer-card {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
padding: 16px;
}
.modern-dash .footer-col {
display: flex;
flex-direction: column;
gap: 4px;
}
.modern-dash .f-title {
font-size: 11px;
font-weight: 800;
color: var(--_ink);
margin-bottom: 2px;
text-transform: uppercase;
letter-spacing: 1px;
}
.modern-dash .f-title.mt {
margin-top: 8px;
}
.modern-dash .f-val {
font-size: 11px;
color: var(--_muted);
font-family: ui-monospace, monospace;
}
.modern-dash .f-val.gps-state {
font-weight: 800;
color: var(--accent);
}

369
web/css/pages/database.css Normal file
View File

@@ -0,0 +1,369 @@
/* ==========================================================================
DATABASE
========================================================================== */
.db-container {
--db-row-hover: rgba(0, 255, 154, .06);
--db-row-selected: rgba(0, 255, 154, .12);
--db-cell-edited: rgba(24, 240, 255, .18);
}
.db-container.page-with-sidebar {
height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 24px);
min-height: 0;
}
.db-container .db-toolbar,
.db-container .db-actions {
display: flex;
gap: 10px;
margin-bottom: 12px;
align-items: center;
flex-wrap: wrap;
flex-shrink: 0;
}
.db-container .db-header {
position: sticky;
top: 0;
z-index: 20;
background: var(--grad-topbar);
border: 1px solid var(--c-border);
border-radius: 12px;
padding: 12px;
box-shadow: var(--shadow);
margin-bottom: 12px;
}
.db-container .sticky-actions {
position: sticky;
bottom: 0;
z-index: 15;
display: flex;
gap: 8px;
justify-content: flex-end;
padding: 8px;
background: linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, .4));
border-top: 1px solid var(--c-border);
border-radius: 12px;
backdrop-filter: blur(4px);
}
.db-container .db-tree {
display: grid;
gap: 6px;
}
.db-container .tree-head {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 8px;
position: sticky;
top: 0;
z-index: 3;
background: var(--grad-card);
padding: 4px 0;
}
.db-container .db-sidebar-filter {
position: sticky;
top: 30px;
z-index: 2;
background: var(--c-panel);
border: 1px solid var(--c-border-strong);
border-radius: 10px;
padding: 6px 10px;
width: 100%;
color: var(--ink);
font: inherit;
margin-bottom: 8px;
}
.db-container .tree-search {
display: flex;
gap: 6px;
align-items: center;
background: var(--c-panel);
border: 1px solid var(--c-border-strong);
border-radius: 10px;
padding: 6px 8px;
}
.db-container .tree-search input {
all: unset;
flex: 1;
color: var(--ink);
}
.db-container .tree-group {
margin-top: 10px;
}
.db-container .db-tree-item {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 8px 10px;
border: 1px solid var(--c-border);
border-radius: 10px;
background: var(--c-panel-2);
cursor: pointer;
transition: .18s;
color: var(--ink);
text-align: left;
}
.db-container .db-tree-item:hover {
box-shadow: 0 0 0 1px var(--c-border-hi) inset, 0 8px 22px var(--glow-weak);
transform: translateX(2px);
}
.db-container .db-tree-item.active {
background: linear-gradient(180deg, #0b151c, #091219);
outline: 2px solid color-mix(in oklab, var(--acid) 55%, transparent);
}
.db-container .db-tree-item-name {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.db-container .db-title {
display: flex;
align-items: center;
gap: 10px;
font-weight: 700;
color: var(--acid);
letter-spacing: .08em;
}
.db-container .db-controls {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
.db-container .db-search-input {
background: var(--c-panel);
border: 1px solid var(--c-border-strong);
border-radius: 10px;
padding: 0 12px;
min-width: 240px;
flex: 1;
color: var(--ink);
height: 36px;
font: inherit;
}
.db-container .db-search-input:focus {
outline: none;
border-color: color-mix(in oklab, var(--acid) 55%, var(--c-border-strong));
box-shadow: 0 0 0 3px color-mix(in oklab, var(--acid) 20%, transparent);
}
.db-container .db-opts {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.db-container .hint {
color: var(--muted);
font-size: 12px;
}
.db-container .sep {
width: 1px;
height: 24px;
background: var(--c-border);
margin: 0 4px;
opacity: .6;
}
.db-container .db-wrap {
display: flex;
flex-direction: column;
gap: 12px;
min-height: 0;
flex: 1;
}
.db-container .db-table-wrap {
position: relative;
overflow: auto;
border: 1px solid var(--c-border);
border-radius: 12px;
background: var(--grad-card);
box-shadow: var(--shadow);
flex: 1;
min-height: 0;
max-width: 100%;
}
.db-container table.db {
width: max-content;
min-width: 100%;
border-collapse: separate;
border-spacing: 0;
}
.db-container .db-table-wrap table.db thead th {
position: sticky;
top: 0;
z-index: 5;
background: var(--c-panel);
border-bottom: 1px solid var(--c-border-strong);
text-align: left;
padding: 10px;
font-weight: 700;
color: var(--acid);
user-select: none;
-webkit-user-select: none;
cursor: pointer;
white-space: nowrap;
}
.db-container .db tbody td {
padding: 8px 10px;
border-bottom: 1px dashed var(--c-border-muted);
vertical-align: middle;
background: var(--grad-card);
}
.db-container .db tbody tr:hover {
background: var(--db-row-hover);
}
.db-container .db tbody tr.selected {
background: var(--db-row-selected);
outline: 1px solid var(--c-border-hi);
}
.db-container .cell {
display: block;
min-width: 80px;
max-width: 520px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.db-container .cell[contenteditable="true"] {
outline: 0;
border-radius: 6px;
transition: .12s;
padding: 2px 6px;
}
.db-container .cell[contenteditable="true"]:focus {
background: var(--db-cell-focus);
box-shadow: 0 0 0 1px var(--c-border-hi) inset;
}
.db-container .cell.edited {
background: var(--db-cell-edited);
}
.db-container .pk {
color: var(--muted);
font-size: 12px;
}
.db-container .cols-drawer {
display: none;
}
.db-container .cols-drawer.open {
display: block;
}
.db-container .db-page {
display: grid;
grid-template-columns: 1fr;
}
.db-container .sticky-col-cell {
position: sticky;
z-index: 3;
background: var(--grad-card);
box-shadow: 1px 0 0 0 var(--c-border-strong), -1px 0 0 0 var(--c-border);
}
.db-container .sticky-col-head {
position: sticky;
z-index: 3;
background: var(--grad-card);
box-shadow: 1px 0 0 0 var(--c-border-strong), -1px 0 0 0 var(--c-border);
}
.db-container .sticky-check,
.db-container .sticky-col-head.sticky-check {
z-index: 4;
}
.db-container th.is-sticky .sticky-dot::after {
content: "\25CF";
margin-left: 6px;
font-size: 10px;
color: var(--acid);
opacity: .9;
}
@keyframes db-blinkChange {
from {
box-shadow: 0 0 0 0 var(--acid-22);
}
to {
box-shadow: 0 0 0 6px transparent;
}
}
.db-container .value-changed {
animation: db-blinkChange .66s ease;
}
@media (max-width:1100px) {
.db-container .db-controls {
gap: 6px;
}
.db-container .db-search-input {
min-width: 160px;
}
.db-container .cell {
max-width: 60vw;
}
}
@media (max-width:768px) {
.db-container .db-search-input {
min-width: 0;
width: 100%;
}
.db-container .db-toolbar,
.db-container .db-actions {
gap: 4px;
}
.db-container .db-actions .btn {
padding: 4px 8px;
font-size: 11px;
}
.db-container .cell {
min-width: 60px;
max-width: 45vw;
}
.db-container .db-controls {
flex-direction: column;
align-items: stretch;
}
}

3244
web/css/pages/files.css Normal file

File diff suppressed because it is too large Load Diff

403
web/css/pages/loki.css Normal file
View File

@@ -0,0 +1,403 @@
/* ============================================================
Loki (HID Attack Mode) — SPA page styles
============================================================ */
.loki-page {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
gap: 12px;
padding: 15px;
}
/* ── Header bar ─────────────────────────────────────────── */
.loki-header {
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
flex-wrap: wrap;
gap: 10px;
}
.loki-title {
margin: 0;
font-size: 1.3rem;
font-weight: 800;
color: var(--ink);
display: flex;
align-items: center;
gap: 8px;
}
.loki-title-icon { font-size: 1.5rem; }
.loki-controls {
display: flex;
gap: 8px;
align-items: center;
}
/* ── Status bar ─────────────────────────────────────────── */
.loki-status-bar {
display: flex;
flex-wrap: wrap;
gap: 12px;
padding: 10px 14px;
border-radius: 8px;
background: var(--surface);
border: 1px solid var(--line);
font-size: 0.85rem;
color: var(--muted);
}
.loki-status-item {
display: flex;
align-items: center;
gap: 4px;
}
.loki-status-item .label { font-weight: 600; }
.loki-status-item .value { color: var(--ink); }
.loki-status-item .dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
}
.loki-status-item .dot.on { background: var(--ok, #10b981); }
.loki-status-item .dot.off { background: var(--muted, #888); }
/* ── Grid: Editor + Library ──────────────────────────────── */
.loki-grid {
display: grid;
grid-template-columns: 1fr 280px;
gap: 12px;
flex: 1;
min-height: 0;
}
@media (max-width: 768px) {
.loki-grid {
grid-template-columns: 1fr;
grid-template-rows: 1fr auto;
}
}
/* ── Editor panel ────────────────────────────────────────── */
.loki-editor-panel {
display: flex;
flex-direction: column;
gap: 8px;
min-height: 0;
}
.loki-editor {
flex: 1;
min-height: 200px;
width: 100%;
font-family: 'Courier New', 'Fira Code', monospace;
font-size: 0.85rem;
line-height: 1.5;
padding: 12px;
border: 1px solid var(--line);
border-radius: 8px;
background: var(--surface);
color: var(--ink);
resize: vertical;
tab-size: 2;
white-space: pre;
overflow: auto;
}
.loki-editor:focus {
outline: 2px solid var(--accent, #3b82f6);
outline-offset: -1px;
}
.loki-editor-toolbar {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.loki-editor-toolbar select {
padding: 6px 10px;
border: 1px solid var(--line);
border-radius: 6px;
background: var(--surface);
color: var(--ink);
font-size: 0.8rem;
}
/* ── Library panel ───────────────────────────────────────── */
.loki-library {
display: flex;
flex-direction: column;
gap: 8px;
min-height: 0;
max-height: 100%;
overflow-y: auto;
}
.loki-library-section {
border: 1px solid var(--line);
border-radius: 8px;
background: var(--surface);
overflow: hidden;
}
.loki-library-heading {
padding: 8px 12px;
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--muted);
border-bottom: 1px solid var(--line);
cursor: pointer;
user-select: none;
display: flex;
justify-content: space-between;
align-items: center;
}
.loki-library-heading::after {
content: '▾';
font-size: 0.7rem;
}
.loki-library-list {
list-style: none;
margin: 0;
padding: 0;
max-height: 200px;
overflow-y: auto;
}
.loki-library-item {
padding: 6px 12px;
cursor: pointer;
font-size: 0.8rem;
color: var(--ink);
border-bottom: 1px solid var(--line-faint, var(--line));
transition: background 0.15s;
display: flex;
justify-content: space-between;
align-items: center;
}
.loki-library-item:last-child { border-bottom: none; }
.loki-library-item:hover {
background: var(--hover, rgba(0,0,0,0.04));
}
.loki-library-item.active {
background: var(--accent-bg, rgba(59, 130, 246, 0.1));
font-weight: 600;
}
.loki-library-item .name { flex: 1; }
.loki-library-item .badge {
font-size: 0.65rem;
padding: 1px 5px;
border-radius: 4px;
background: var(--muted);
color: var(--surface, #fff);
}
/* ── Jobs panel ──────────────────────────────────────────── */
.loki-jobs {
flex-shrink: 0;
}
.loki-jobs-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.loki-jobs-header h3 {
margin: 0;
font-size: 0.95rem;
font-weight: 700;
color: var(--ink);
}
.loki-jobs-table {
width: 100%;
border-collapse: collapse;
font-size: 0.8rem;
}
.loki-jobs-table th,
.loki-jobs-table td {
padding: 6px 10px;
text-align: left;
border-bottom: 1px solid var(--line);
}
.loki-jobs-table th {
font-weight: 700;
color: var(--muted);
text-transform: uppercase;
font-size: 0.7rem;
letter-spacing: 0.04em;
}
.loki-jobs-empty {
text-align: center;
padding: 16px;
color: var(--muted);
font-size: 0.85rem;
}
/* ── Job status badges ───────────────────────────────────── */
.loki-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: 10px;
font-size: 0.7rem;
font-weight: 600;
white-space: nowrap;
}
.loki-badge.running { background: #fef3c7; color: #92400e; }
.loki-badge.succeeded { background: #d1fae5; color: #065f46; }
.loki-badge.failed { background: #fee2e2; color: #991b1b; }
.loki-badge.cancelled { background: #e5e7eb; color: #374151; }
.loki-badge.pending { background: #e0e7ff; color: #3730a3; }
/* ── Buttons ─────────────────────────────────────────────── */
.loki-btn {
padding: 6px 14px;
border: 1px solid var(--line);
border-radius: 6px;
background: var(--surface);
color: var(--ink);
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
transition: all 0.15s;
display: inline-flex;
align-items: center;
gap: 4px;
}
.loki-btn:hover { background: var(--hover, rgba(0,0,0,0.04)); }
.loki-btn.primary {
background: var(--accent, #3b82f6);
color: #fff;
border-color: var(--accent, #3b82f6);
}
.loki-btn.primary:hover { filter: brightness(1.1); }
.loki-btn.danger {
background: var(--danger, #ef4444);
color: #fff;
border-color: var(--danger, #ef4444);
}
.loki-btn.danger:hover { filter: brightness(1.1); }
.loki-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* ── Toggle switch ───────────────────────────────────────── */
.loki-toggle {
position: relative;
width: 44px;
height: 24px;
appearance: none;
background: var(--muted, #ccc);
border-radius: 12px;
cursor: pointer;
transition: background 0.3s;
border: none;
}
.loki-toggle:checked { background: var(--ok, #10b981); }
.loki-toggle::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
border-radius: 50%;
background: #fff;
transition: transform 0.3s;
}
.loki-toggle:checked::after { transform: translateX(20px); }
/* ── Disabled overlay ────────────────────────────────────── */
.loki-disabled-overlay {
position: relative;
}
.loki-disabled-overlay::after {
content: '';
position: absolute;
inset: 0;
background: var(--surface, #fff);
opacity: 0.6;
pointer-events: all;
border-radius: 8px;
z-index: 2;
}
/* ── Quick type ──────────────────────────────────────────── */
.loki-quick-row {
display: flex;
gap: 8px;
align-items: center;
}
.loki-quick-input {
flex: 1;
padding: 6px 10px;
border: 1px solid var(--line);
border-radius: 6px;
font-size: 0.8rem;
background: var(--surface);
color: var(--ink);
}
/* ── Install banner ─────────────────────────────────────── */
.loki-install-banner {
padding: 16px 20px;
background: var(--warn-bg, #fff3cd);
border: 1px solid var(--warn-border, #ffc107);
border-radius: 8px;
text-align: center;
margin-bottom: 4px;
}
.loki-install-banner p {
margin: 0 0 12px;
font-size: 0.85rem;
color: var(--ink);
}

633
web/css/pages/loot.css Normal file
View File

@@ -0,0 +1,633 @@
/* ==========================================================================
LOOT
========================================================================== */
.loot-container {
position: relative;
z-index: 2;
padding: 16px;
margin-top: 5px;
min-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 24px);
max-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 24px);
display: flex;
flex-direction: column;
gap: 16px;
animation: loot-fadeInUp .6s ease-out;
overflow: hidden;
}
@keyframes loot-fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.loot-container .stats-bar {
display: flex;
gap: 12px;
flex-wrap: wrap;
padding: 12px;
background: color-mix(in oklab, var(--_panel) 88%, transparent);
border: 1px solid var(--_border);
border-radius: 12px;
box-shadow: var(--_shadow);
backdrop-filter: blur(16px);
}
.loot-container .stat-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background: color-mix(in oklab, var(--_panel) 65%, transparent);
border: 1px solid var(--_border);
border-radius: 10px;
transition: .2s;
}
.loot-container .stat-item:hover {
background: color-mix(in oklab, var(--_panel) 78%, transparent);
transform: translateY(-2px);
}
.loot-container .stat-icon {
font-size: 1.2rem;
opacity: .95;
}
.loot-container .stat-value {
font-size: 1.05rem;
font-weight: 800;
background: linear-gradient(135deg, var(--_acid), var(--_acid2));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.loot-container .stat-label {
color: var(--_muted);
font-size: .75rem;
margin-left: 4px;
}
.loot-container .controls-bar {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
.loot-container .search-container {
min-width: 200px;
position: relative;
}
.loot-container .search-input {
width: 100%;
padding: 12px 16px 12px 44px;
background: color-mix(in oklab, var(--_panel) 90%, transparent);
border: 1px solid var(--_border);
border-radius: 12px;
color: var(--_ink);
font-size: .95rem;
backdrop-filter: blur(10px);
transition: .2s;
}
.loot-container .search-input:focus {
outline: none;
border-color: color-mix(in oklab, var(--_acid2) 40%, var(--_border));
box-shadow: 0 0 0 3px color-mix(in oklab, var(--_acid2) 18%, transparent);
background: color-mix(in oklab, var(--_panel) 96%, transparent);
}
.loot-container .search-icon {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
color: var(--_muted);
pointer-events: none;
}
.loot-container .clear-search {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
color: var(--_muted);
cursor: pointer;
font-size: 1rem;
display: none;
}
.loot-container .search-input:not(:placeholder-shown)~.clear-search {
display: block;
}
.loot-container .view-controls {
display: flex;
gap: 8px;
align-items: center;
}
.loot-container .view-btn,
.loot-container .sort-btn {
padding: 10px;
background: color-mix(in oklab, var(--_panel) 90%, transparent);
border: 1px solid var(--_border);
border-radius: 10px;
color: var(--_muted);
cursor: pointer;
transition: .2s;
backdrop-filter: blur(10px);
font-size: 1.1rem;
}
.loot-container .view-btn:hover,
.loot-container .sort-btn:hover {
background: color-mix(in oklab, var(--_panel) 96%, transparent);
color: var(--_ink);
transform: translateY(-2px);
}
.loot-container .view-btn.active {
background: linear-gradient(135deg, color-mix(in oklab, var(--_acid) 20%, transparent), color-mix(in oklab, var(--_acid2) 12%, transparent));
color: var(--_ink);
border-color: color-mix(in oklab, var(--_acid2) 35%, var(--_border));
}
.loot-container .sort-dropdown {
position: relative;
}
.loot-container .sort-menu {
position: absolute;
top: calc(100% + 8px);
right: 0;
background: color-mix(in oklab, var(--_panel) 98%, transparent);
border: 1px solid var(--_border);
border-radius: 12px;
padding: 8px;
min-width: 150px;
backdrop-filter: blur(20px);
box-shadow: var(--_shadow);
opacity: 0;
pointer-events: none;
transform: translateY(-10px);
transition: .2s;
z-index: 10;
}
.loot-container .sort-dropdown.active .sort-menu {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
}
.loot-container .sort-option {
padding: 10px 12px;
border-radius: 8px;
cursor: pointer;
transition: .2s;
font-size: .9rem;
color: var(--_ink);
}
.loot-container .sort-option:hover {
background: rgba(255, 255, 255, .05);
}
.loot-container .sort-option.active {
color: var(--_ink);
background: color-mix(in oklab, var(--_acid2) 14%, transparent);
}
.loot-container .tabs-container {
display: flex;
gap: 8px;
padding: 4px;
background: color-mix(in oklab, var(--_panel) 88%, transparent);
border-radius: 12px;
border: 1px solid var(--_border);
backdrop-filter: blur(10px);
overflow-x: auto;
scrollbar-width: none;
}
.loot-container .tabs-container::-webkit-scrollbar {
display: none;
}
.loot-container .tab {
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
transition: .2s;
white-space: nowrap;
font-size: .9rem;
font-weight: 700;
position: relative;
color: var(--_muted);
border: 1px solid transparent;
}
.loot-container .tab:hover {
background: rgba(255, 255, 255, .05);
color: var(--_ink);
}
.loot-container .tab.active {
background: linear-gradient(135deg, color-mix(in oklab, var(--_acid) 16%, transparent), color-mix(in oklab, var(--_acid2) 10%, transparent));
color: var(--_ink);
border-color: color-mix(in oklab, var(--_acid2) 28%, var(--_border));
}
.loot-container .tab.active::after {
content: '';
position: absolute;
bottom: 0;
left: 10%;
right: 10%;
height: 2px;
background: linear-gradient(90deg, var(--_acid), var(--_acid2));
border-radius: 2px;
}
.loot-container .tab-badge {
display: inline-block;
padding: 2px 6px;
margin-left: 6px;
background: rgba(255, 255, 255, .08);
border: 1px solid var(--_border);
border-radius: 10px;
font-size: .75rem;
font-weight: 700;
color: var(--_ink);
}
.loot-container .explorer {
background: color-mix(in oklab, var(--_panel) 88%, transparent);
border-radius: 20px;
border: 1px solid var(--_border);
backdrop-filter: blur(20px);
box-shadow: var(--_shadow);
overflow: hidden;
flex: 1;
display: flex;
flex-direction: column;
animation: loot-slideIn .6s ease-out;
}
@keyframes loot-slideIn {
from {
opacity: 0;
transform: translateX(-16px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.loot-container .explorer-content {
padding: 20px;
overflow-y: auto;
flex: 1;
min-height: 0;
}
.loot-container .tree-view {
display: none;
}
.loot-container .tree-view.active {
display: block;
}
.loot-container .list-view {
display: none;
}
.loot-container .list-view.active {
display: grid;
gap: 8px;
}
.loot-container .loot-tree-node {
margin-bottom: 4px;
animation: loot-itemSlide .3s ease-out backwards;
margin-left: calc(var(--loot-level, 0) * 8px);
}
@keyframes loot-itemSlide {
from {
opacity: 0;
transform: translateX(-10px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.loot-container .loot-tree-row {
width: 100%;
display: grid;
grid-template-columns: 18px 30px minmax(0, 1fr) auto;
align-items: center;
gap: 10px;
padding: 10px 12px;
cursor: pointer;
border-radius: 10px;
transition: .2s;
border: 1px solid transparent;
position: relative;
overflow: hidden;
color: var(--_ink);
background: color-mix(in oklab, var(--_panel) 86%, transparent);
text-align: left;
}
.loot-container .loot-tree-row::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .05), transparent);
transform: translateX(-100%);
transition: transform .6s;
}
.loot-container .loot-tree-row:hover::before {
transform: translateX(100%);
}
.loot-container .loot-tree-row:hover {
border-color: color-mix(in oklab, var(--_acid2) 26%, var(--_border));
background: color-mix(in oklab, var(--_panel) 94%, transparent);
}
.loot-container .loot-tree-icon {
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
font-size: 1.1rem;
flex-shrink: 0;
background: color-mix(in oklab, var(--_acid) 12%, transparent);
color: var(--_ink);
}
.loot-container .folder-icon {
background: color-mix(in oklab, var(--_acid) 10%, transparent);
color: var(--_ink);
}
.loot-container .loot-tree-name {
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.loot-container .loot-tree-chevron {
width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
color: var(--_muted);
transition: transform .3s cubic-bezier(.4, 0, .2, 1);
font-size: .75rem;
}
.loot-container .loot-tree-meta {
justify-self: end;
padding: 2px 8px;
border-radius: 999px;
border: 1px solid var(--_border);
font-size: .72rem;
color: var(--_muted);
background: color-mix(in oklab, var(--_panel) 76%, transparent);
}
.loot-container .loot-tree-node.expanded .loot-tree-chevron {
transform: rotate(90deg);
}
.loot-container .loot-tree-children {
display: none;
overflow: hidden;
margin-left: 10px;
padding-left: 14px;
border-left: 1px dashed color-mix(in oklab, var(--_border) 82%, transparent);
}
.loot-container .loot-tree-node.expanded .loot-tree-children {
display: block;
}
.loot-container .file-item {
display: flex;
align-items: center;
padding: 10px 12px;
border-radius: 10px;
cursor: pointer;
transition: .2s;
margin-bottom: 4px;
}
.loot-container .file-item:hover {
background: rgba(255, 255, 255, .04);
transform: translateX(4px);
}
.loot-container .file-item:active {
transform: translateX(2px) scale(.98);
}
.loot-container .file-item.is-tree-file {
margin-left: calc(var(--loot-level, 0) * 8px);
margin-top: 2px;
margin-bottom: 2px;
padding: 8px 10px;
border: 1px solid transparent;
background: color-mix(in oklab, var(--_panel) 82%, transparent);
}
.loot-container .file-item.is-tree-file:hover {
transform: none;
border-color: color-mix(in oklab, var(--_acid2) 22%, var(--_border));
}
.loot-container .file-icon {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
margin-right: 12px;
font-size: .9rem;
flex-shrink: 0;
color: var(--_ink);
background: color-mix(in oklab, var(--_panel) 75%, transparent);
}
.loot-container .file-icon.ssh {
background: color-mix(in oklab, var(--_acid) 12%, transparent);
}
.loot-container .file-icon.sql {
background: color-mix(in oklab, var(--_acid2) 12%, transparent);
}
.loot-container .file-icon.smb {
background: color-mix(in oklab, var(--_acid2) 16%, transparent);
}
.loot-container .file-icon.other {
background: color-mix(in oklab, var(--_panel) 75%, transparent);
}
.loot-container .file-name {
flex: 1;
font-size: .9rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--_ink);
}
.loot-container .file-type {
padding: 3px 8px;
border-radius: 6px;
font-size: .7rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: .05em;
margin-left: 8px;
border: 1px solid var(--_border);
color: var(--_ink);
background: color-mix(in oklab, var(--_panel) 80%, transparent);
}
.loot-container .file-type.ssh {
background: color-mix(in oklab, var(--_acid) 12%, transparent);
}
.loot-container .file-type.sql {
background: color-mix(in oklab, var(--_acid2) 12%, transparent);
}
.loot-container .file-type.smb {
background: color-mix(in oklab, var(--_acid2) 16%, transparent);
}
.loot-container .no-results {
text-align: center;
color: var(--_muted);
padding: 40px;
font-size: .95rem;
}
.loot-container .no-results-icon {
font-size: 3rem;
margin-bottom: 16px;
opacity: .5;
}
.loot-container .loading {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.loot-container .loading-spinner {
width: 40px;
height: 40px;
border: 3px solid var(--_border);
border-top-color: var(--_acid2);
border-radius: 50%;
animation: loot-spin 1s linear infinite;
}
@keyframes loot-spin {
to {
transform: rotate(360deg);
}
}
@media (max-width:768px) {
.loot-container {
padding: 12px;
gap: 12px;
}
.loot-container .controls-bar {
flex-direction: column;
align-items: stretch;
}
.loot-container .search-container {
width: 100%;
}
.loot-container .view-controls {
justify-content: center;
}
.loot-container .tabs-container {
padding: 2px;
}
.loot-container .tab {
padding: 8px 14px;
font-size: .85rem;
}
.loot-container .explorer-content {
padding: 12px;
max-height: calc(100vh - 320px);
}
.loot-container .loot-tree-row {
grid-template-columns: 16px 26px minmax(0, 1fr);
gap: 8px;
padding: 9px 10px;
}
.loot-container .loot-tree-meta {
display: none;
}
.loot-container .loot-tree-children {
margin-left: 8px;
padding-left: 10px;
}
.loot-container .stat-item {
padding: 6px 10px;
}
.loot-container .stat-value {
font-size: .95rem;
}
}
@media (hover:none) {
.loot-container .loot-tree-row:active {
background: rgba(255, 255, 255, .06);
}
}

538
web/css/pages/netkb.css Normal file
View File

@@ -0,0 +1,538 @@
/* ===== NETKB ===== */
.netkb-container {
display: grid;
gap: 16px;
min-width: 0;
max-width: 100%;
overflow: hidden;
}
.netkb-container .hidden {
display: none !important;
}
.netkb-container .netkb-toolbar-wrap {
position: sticky;
top: 0;
z-index: 500;
backdrop-filter: saturate(1.1) blur(6px);
}
.netkb-container .netkb-toolbar {
position: relative;
display: flex;
gap: 12px;
align-items: center;
justify-content: flex-end;
margin-bottom: 12px;
border: 1px solid var(--c-border-strong);
padding: 8px 10px;
box-shadow: var(--shadow);
background: var(--panel);
border-radius: 16px;
}
/* .segmented styles now inherited from global.css */
.netkb-container .kb-switch {
display: inline-flex;
align-items: center;
gap: 10px;
font-weight: 700;
color: var(--muted);
background: var(--panel);
border: 1px solid var(--c-border-strong);
border-radius: 999px;
padding: 6px 10px;
}
.netkb-container .kb-switch input {
display: none;
}
.netkb-container .kb-switch .track {
width: 44px;
height: 24px;
border-radius: 999px;
background: var(--c-panel-2);
position: relative;
border: 1px solid var(--c-border);
}
.netkb-container .kb-switch .thumb {
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--ink);
box-shadow: 0 2px 8px rgba(0, 0, 0, .4);
transition: left .18s ease, background .18s ease;
}
.netkb-container .kb-switch input:checked~.track .thumb {
left: 22px;
background: var(--acid);
}
.netkb-container .kb-switch[data-on="true"] {
color: var(--ink);
}
.netkb-container .icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 12px;
background: var(--c-panel);
border: 1px solid var(--c-border-strong);
box-shadow: var(--shadow);
cursor: pointer;
transition: transform .12s ease, box-shadow .12s ease;
}
.netkb-container .icon-btn:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-hover);
}
.netkb-container .icon-btn svg {
width: 20px;
height: 20px;
fill: var(--ink);
}
.netkb-container .search-pop {
position: absolute;
right: 8px;
top: 54px;
display: none;
min-width: 260px;
background: var(--c-panel);
border: 1px solid var(--c-border-strong);
border-radius: 12px;
padding: 10px;
box-shadow: var(--shadow-hover);
}
.netkb-container .search-pop.show {
display: block;
}
.netkb-container .search-input-wrap {
position: relative;
display: flex;
align-items: center;
}
.netkb-container .search-pop input {
width: 100%;
padding: 10px 32px 10px 12px;
border-radius: 10px;
border: 1px solid var(--c-border);
background: var(--c-panel-2);
color: var(--ink);
font-weight: 700;
outline: none;
}
.netkb-container .search-clear {
position: absolute;
right: 6px;
top: 50%;
transform: translateY(-50%);
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
color: var(--muted);
font-size: 14px;
cursor: pointer;
border-radius: 50%;
transition: background .15s;
}
.netkb-container .search-clear:hover {
background: var(--c-border-strong);
color: var(--ink);
}
.netkb-container .search-hint {
margin-top: 6px;
font-size: .85rem;
color: var(--muted);
}
.netkb-container .card-container {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: stretch;
justify-content: center;
min-width: 0;
max-width: 100%;
}
.netkb-container .card {
background: var(--grad-card);
color: var(--ink);
border: 1px solid var(--c-border-strong);
border-radius: 18px;
box-shadow: var(--shadow);
width: min(380px, 100%);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
overflow: hidden;
min-width: 0;
transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease, background .15s ease;
}
.netkb-container .card:hover {
box-shadow: var(--shadow-hover);
border-color: var(--c-border-hi);
transform: translateY(-1px);
}
.netkb-container .card.alive .card-title {
color: var(--ok);
}
.netkb-container .card.not-alive {
background: var(--kb-offline-bg);
border-color: var(--kb-offline-brd);
color: color-mix(in oklab, var(--muted) 90%, var(--ink) 10%);
box-shadow: 0 0 0 1px var(--kb-offline-brd), 0 0 0 2px color-mix(in oklab, var(--kb-offline-ring) 26%, transparent), var(--shadow);
}
.netkb-container .card.not-alive .card-title {
color: color-mix(in oklab, var(--muted) 85%, var(--ink) 15%);
}
.netkb-container .card-content {
display: flex;
flex-direction: column;
gap: 6px;
flex: 1;
}
.netkb-container .card-title {
font-size: 1.1rem;
font-weight: 800;
margin: 0;
}
.netkb-container .card-section {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.netkb-container .card.list {
width: 100%;
max-width: none;
flex-direction: row;
align-items: center;
}
.netkb-container .card.list .card-title {
font-size: 1rem;
}
.netkb-container .chip {
display: inline-block;
padding: .32rem .7rem;
border-radius: 999px;
border: 1px solid var(--c-border-strong);
background: var(--kb-chip);
color: var(--ink);
font-weight: 700;
font-size: .92rem;
}
.netkb-container .chip.host {
background: var(--kb-hostname-bg);
}
.netkb-container .chip.ip {
background: var(--kb-ip-bg);
}
.netkb-container .chip.mac {
background: var(--kb-mac-bg);
color: var(--muted);
}
.netkb-container .chip.vendor {
background: var(--kb-vendor-bg);
}
.netkb-container .chip.essid {
background: var(--kb-essid-bg);
}
.netkb-container .chip.port {
background: var(--kb-ports-bg);
border-color: var(--c-border-hi);
}
.netkb-container .port-bubbles {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.netkb-container .status-container {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
min-width: 0;
max-width: 100%;
}
.netkb-container .badge {
background: var(--c-panel-2);
color: var(--ink);
border: 1px solid var(--c-border);
border-radius: 14px;
padding: 8px 10px;
min-width: 0;
flex: 1 1 120px;
max-width: 100%;
text-align: center;
box-shadow: var(--shadow);
transition: transform .12s ease, box-shadow .12s ease, opacity .12s ease;
position: relative;
}
.netkb-container .badge .badge-header {
font-weight: 800;
opacity: .95;
}
.netkb-container .badge .badge-status {
font-weight: 900;
}
.netkb-container .badge .badge-timestamp {
font-size: .85em;
opacity: .9;
}
.netkb-container .badge.clickable {
cursor: pointer;
}
.netkb-container .badge:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-hover);
}
.netkb-container .badge.success {
background: linear-gradient(180deg, color-mix(in oklab, var(--ok) 12%, transparent), transparent);
}
.netkb-container .badge.failed {
background: linear-gradient(180deg, color-mix(in oklab, var(--danger) 18%, transparent), transparent);
}
.netkb-container .badge.pending {
background: linear-gradient(180deg, color-mix(in oklab, var(--muted) 12%, transparent), transparent);
}
.netkb-container .badge.expired {
background: linear-gradient(180deg, color-mix(in oklab, var(--warning) 18%, transparent), transparent);
}
.netkb-container .badge.cancelled {
background: linear-gradient(180deg, color-mix(in oklab, var(--c-panel) 18%, transparent), transparent);
}
.netkb-container .badge.running {
background: linear-gradient(180deg, color-mix(in oklab, #18f0ff 14%, transparent), transparent);
overflow: hidden;
animation: kb-badgePulse 1.6s ease-in-out infinite;
}
.netkb-container .badge.running::after {
content: "";
position: absolute;
inset: 0;
background: var(--kb-badge-shimmer);
animation: kb-shimmer 1.8s linear infinite;
}
.netkb-container .badge.running::before {
content: "";
position: absolute;
inset: -20%;
background: linear-gradient(130deg, transparent 40%, rgba(255, 255, 255, .06) 50%, transparent 60%);
animation: kb-sheen 2.2s ease-in-out infinite;
}
@keyframes kb-shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
@keyframes kb-sheen {
0% {
transform: translateX(-30%);
}
100% {
transform: translateX(30%);
}
}
@keyframes kb-badgePulse {
0%,
100% {
box-shadow: 0 0 0 0 rgba(24, 240, 255, .12);
}
50% {
box-shadow: 0 0 0 8px rgba(24, 240, 255, .04);
}
}
.netkb-container .table-wrap {
border: 1px solid var(--c-border-strong);
border-radius: 14px;
overflow: auto;
background: var(--panel);
box-shadow: var(--shadow);
}
.netkb-container .table-inner {
min-width: max-content;
}
.netkb-container table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
}
.netkb-container thead th {
position: sticky;
top: 0;
z-index: 2;
background: var(--c-panel);
color: var(--ink);
border-bottom: 1px solid var(--c-border-strong);
padding: 10px;
text-align: left;
white-space: nowrap;
cursor: pointer;
}
.netkb-container tbody td {
border-bottom: 1px solid var(--c-border);
padding: 10px;
white-space: nowrap;
text-align: center;
}
.netkb-container th:first-child,
.netkb-container td:first-child {
position: sticky;
left: 0;
background: var(--panel);
z-index: 3;
}
.netkb-container .filter-icon {
width: 16px;
height: 16px;
margin-left: 6px;
vertical-align: middle;
}
.netkb-container mark.hl {
background: color-mix(in oklab, var(--acid) 25%, transparent);
color: var(--ink);
padding: 0 .15em;
border-radius: 4px;
}
.netkb-container .segmented button:focus-visible,
.netkb-container .icon-btn:focus-visible,
.netkb-container .kb-switch:has(input:focus-visible) {
outline: 2px solid var(--acid);
outline-offset: 2px;
box-shadow: 0 0 0 3px color-mix(in oklab, var(--acid) 25%, transparent);
}
@media (max-width:720px) {
.netkb-container {
padding: 0 4px;
}
.netkb-container .card {
width: 100%;
}
.netkb-container .segmented button[data-view="grid"] {
display: none;
}
.netkb-container .netkb-toolbar-wrap {
position: relative;
top: auto;
}
.netkb-container .netkb-toolbar {
flex-wrap: wrap;
justify-content: center;
gap: 8px;
}
.netkb-container .table-wrap {
display: block;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
max-width: 100%;
width: 100%;
box-sizing: border-box;
}
.netkb-container .table-inner>table {
min-width: 760px;
width: max-content;
}
.netkb-container thead th,
.netkb-container tbody td {
min-width: 80px;
font-size: .85rem;
padding: 8px 6px;
white-space: nowrap;
}
.netkb-container .chip {
font-size: .8rem;
padding: .25rem .5rem;
}
.netkb-container .badge {
min-width: 120px;
padding: 6px 8px;
}
}

605
web/css/pages/network.css Normal file
View File

@@ -0,0 +1,605 @@
/* ===== NETWORK ===== */
.network-container {
padding: 12px;
position: relative;
z-index: 2;
display: flex;
flex-direction: column;
min-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 16px);
overflow: hidden;
}
.network-container.is-table-view .ocean-container {
display: none;
}
.network-container .nv-toolbar-wrap {
position: sticky;
top: 0;
margin: 0 0 10px 0;
z-index: 500;
backdrop-filter: saturate(1.1) blur(6px);
}
.network-container .nv-toolbar {
display: flex;
gap: 12px;
align-items: center;
justify-content: space-between;
background: var(--panel);
border: 1px solid var(--c-border-strong);
border-radius: 16px;
padding: 8px 10px;
box-shadow: var(--shadow);
}
.network-container .nv-search {
display: flex;
align-items: center;
gap: 8px;
background: var(--c-panel);
border: 1px solid var(--c-border-strong);
border-radius: 12px;
padding: 6px 10px;
min-width: 240px;
box-shadow: var(--shadow);
}
.network-container .nv-search-icon {
font-size: 16px;
flex-shrink: 0;
opacity: .9;
}
.network-container .nv-search input {
border: none;
outline: none;
background: transparent;
color: var(--ink);
font-weight: 700;
width: 100%;
min-width: 0;
}
.network-container .nv-search-clear {
flex-shrink: 0;
width: 22px;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
color: var(--muted);
font-size: 13px;
cursor: pointer;
border-radius: 50%;
transition: background .15s;
}
.network-container .nv-search-clear:hover {
background: var(--c-border-strong);
color: var(--ink);
}
.network-container .segmented {
display: inline-flex;
background: var(--panel);
border: 1px solid var(--c-border-strong);
border-radius: 999px;
padding: 4px;
box-shadow: var(--shadow);
}
.network-container .segmented button {
appearance: none;
border: 0;
background: transparent;
color: var(--muted);
font-weight: 700;
padding: 8px 14px;
border-radius: 999px;
cursor: pointer;
transition: background .15s ease, color .15s ease, transform .1s ease;
}
.network-container .segmented button[aria-pressed="true"] {
background: var(--grad-card);
color: var(--ink);
box-shadow: inset 0 0 0 1px var(--c-border-hi), 0 6px 24px var(--glow-weak);
transform: translateY(-1px);
}
.network-container .nv-switch {
display: inline-flex;
align-items: center;
gap: 10px;
font-weight: 700;
color: var(--muted);
background: var(--panel);
border: 1px solid var(--c-border-strong);
border-radius: 999px;
padding: 6px 10px;
box-shadow: var(--shadow);
}
.network-container .nv-switch input {
display: none;
}
.network-container .nv-switch .track {
width: 44px;
height: 24px;
border-radius: 999px;
background: var(--c-panel-2);
position: relative;
border: 1px solid var(--c-border);
}
.network-container .nv-switch .thumb {
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--ink);
box-shadow: 0 2px 8px rgba(0, 0, 0, .4);
transition: left .18s ease, background .18s ease;
}
.network-container .nv-switch input:checked~.track .thumb {
left: 22px;
background: var(--acid);
}
.network-container .nv-switch[data-on="true"] {
color: var(--ink);
}
.network-container .table-wrap {
position: relative;
border: 1px solid var(--c-border-strong);
border-radius: 14px;
overflow: auto;
-webkit-overflow-scrolling: touch;
background: var(--c-panel, #0b1218);
box-shadow: var(--shadow);
flex: 1;
min-height: 0;
}
.network-container table.network-table {
width: 100%;
min-width: 100%;
table-layout: auto;
border-collapse: separate;
border-spacing: 0;
}
.network-container thead th {
position: sticky;
top: 0;
z-index: 3;
background: var(--c-panel, #0b1218);
color: var(--ink);
border-bottom: 1px solid var(--c-border-strong);
padding: 10px;
text-align: left;
cursor: pointer;
box-shadow: inset 0 -1px 0 var(--c-border);
}
.network-container tbody tr {
background: color-mix(in oklab, var(--c-panel, #0b1218) 94%, var(--acid) 6%);
transition: .25s ease;
}
.network-container tbody tr:hover {
background: color-mix(in oklab, var(--c-panel, #0b1218) 84%, var(--acid) 16%);
}
.network-container td {
padding: 10px;
color: var(--ink, #fff);
background: color-mix(in oklab, var(--c-panel, #0b1218) 97%, var(--acid) 3%);
vertical-align: top;
white-space: normal;
border-bottom: 1px solid color-mix(in oklab, var(--c-border) 65%, transparent);
}
.network-container th.hosts-header {
left: 0;
position: sticky;
z-index: 4;
}
.network-container td.hosts-cell {
position: sticky;
left: 0;
z-index: 2;
background: color-mix(in oklab, var(--c-panel, #0b1218) 91%, var(--acid) 9%);
}
.network-container thead th.sort-asc::after {
content: '\2191';
margin-left: 8px;
color: #00b894;
}
.network-container thead th.sort-desc::after {
content: '\2193';
margin-left: 8px;
color: #00b894;
}
.network-container .hosts-content {
display: flex;
align-items: center;
gap: .55rem;
flex-wrap: wrap;
min-width: 320px;
}
.network-container .bubble {
padding: .5rem 1rem;
border-radius: 6px;
font-size: .9rem;
display: inline-flex;
align-items: center;
gap: .5rem;
transition: .2s;
box-shadow: 0 2px 4px rgba(0, 0, 0, .1);
}
.network-container .bubble.bubble-empty {
background: color-mix(in oklab, var(--muted) 18%, transparent);
color: var(--muted);
}
.network-container .bubble.essid {
background: linear-gradient(135deg, #272727, #2560a1);
color: #fff;
padding: 5px 10px;
border-radius: 5px;
font-size: .9em;
font-weight: bold;
white-space: nowrap;
display: inline-block;
}
.network-container .bubble.ip-address {
background: linear-gradient(135deg, #272727, #00cec9);
color: #fff;
font-weight: 600;
cursor: pointer;
}
.network-container .bubble.hostname {
background: linear-gradient(135deg, #5b5c5a, #e7951a);
color: #fff;
cursor: pointer;
}
.network-container .bubble.mac-address {
background: linear-gradient(135deg, #404041, #636e72);
color: #b2bec3;
font-family: monospace;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.network-container .bubble.vendor {
background: linear-gradient(135deg, #5b5c5a, #0a4952);
color: #fff;
font-weight: 600;
cursor: pointer;
white-space: nowrap;
}
.network-container .ports-container {
display: flex;
flex-wrap: wrap;
gap: .45rem;
align-items: center;
min-width: 220px;
}
.network-container .port-bubble {
background: linear-gradient(135deg, #1f2c33, #00b894);
color: #eafff8;
padding: .4rem .8rem;
border-radius: 20px;
font-size: .85rem;
border: 1px solid color-mix(in oklab, #00b894 40%, transparent);
max-width: fit-content;
transition: .2s;
}
.network-container .port-bubble.is-empty {
background: color-mix(in oklab, var(--panel) 90%, transparent);
color: var(--muted);
border-style: dashed;
}
.network-container .port-bubble:hover {
transform: scale(1.08);
box-shadow: 0 2px 8px rgba(9, 132, 227, .3);
}
/* Ports cell — match hosts-cell vertical alignment */
.network-container td.ports-cell {
vertical-align: top;
}
/* Sticky pin button */
.network-container .nv-pin-btn {
display: none; /* shown on mobile only */
appearance: none;
border: 1px solid var(--c-border);
background: var(--c-panel, #0b1218);
color: var(--ink);
border-radius: 6px;
width: 28px;
height: 24px;
font-size: 13px;
cursor: pointer;
align-items: center;
justify-content: center;
padding: 0;
line-height: 1;
transition: .15s;
}
.network-container .nv-pin-btn.active {
background: color-mix(in oklab, var(--acid) 20%, var(--c-panel));
border-color: var(--acid);
box-shadow: 0 0 6px color-mix(in oklab, var(--acid) 30%, transparent);
}
/* Dynamic sticky columns */
.network-container .nv-sticky-col {
position: sticky !important;
z-index: 2;
background: color-mix(in oklab, var(--c-panel, #0b1218) 97%, var(--acid) 3%);
box-shadow: 2px 0 4px rgba(0, 0, 0, .15);
}
.network-container thead .nv-sticky-col {
z-index: 5;
background: var(--c-panel, #0b1218);
}
.network-container .segmented button:focus-visible,
.network-container .nv-search input:focus-visible,
.network-container .nv-switch:has(input:focus-visible) {
outline: 2px solid var(--acid);
outline-offset: 2px;
box-shadow: 0 0 0 3px color-mix(in oklab, var(--acid) 25%, transparent);
}
/* Ocean / Map */
.network-container .ocean-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 0;
pointer-events: none;
background: radial-gradient(ellipse at center, #0a4b7a 0%, #01162e 60%, #00050a 100%);
}
.network-container .ocean-surface {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
opacity: 0.3;
background-image: repeating-radial-gradient(circle at 50% 50%, transparent 0, transparent 20px, rgba(255, 255, 255, 0.02) 25px, transparent 40px);
animation: nv-oceanDrift 60s linear infinite alternate;
}
.network-container .ocean-caustics {
position: absolute;
top: -100%;
left: -100%;
width: 300%;
height: 300%;
opacity: 0.3;
mix-blend-mode: overlay;
animation: nv-causticFlow 30s linear infinite;
}
@keyframes nv-oceanDrift {
0% {
transform: translate(0, 0) rotate(0deg);
}
100% {
transform: translate(-40px, 20px) rotate(1deg);
}
}
@keyframes nv-causticFlow {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(-100px, -50px);
}
}
.network-container #visualization-container {
display: none;
position: relative;
min-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 100px);
height: 100%;
flex: 1;
border-radius: 14px;
overflow: hidden;
border: 1px solid var(--c-border-strong);
box-shadow: var(--shadow);
background: transparent;
}
.network-container .link {
stroke: rgba(255, 255, 255, 0.15);
stroke-width: 1px;
}
.network-container .node {
cursor: pointer;
transition: opacity 0.5s;
}
.network-container .foam-ring {
fill: rgba(240, 248, 255, 0.3);
mix-blend-mode: screen;
animation: nv-foamPulse 4s ease-in-out infinite alternate;
}
.network-container .foam-ring:nth-child(2) {
animation-delay: -1s;
opacity: 0.3;
}
@keyframes nv-foamPulse {
0% {
transform: scale(0.9) rotate(0deg);
opacity: 0.4;
}
100% {
transform: scale(1.1) rotate(10deg);
opacity: 0.1;
}
}
.network-container .sonar-wave {
fill: none;
stroke: #ffb703;
stroke-width: 2px;
animation: nv-sonar 4s infinite ease-out;
opacity: 0;
pointer-events: none;
}
@keyframes nv-sonar {
0% {
r: 40px;
opacity: 0.6;
stroke-width: 3px;
}
100% {
r: 300px;
opacity: 0;
stroke-width: 1px;
}
}
.network-container .label-group {
transition: transform 0.1s;
}
.network-container .label-bg {
fill: rgba(0, 20, 40, 0.8);
rx: 4;
stroke: rgba(255, 255, 255, 0.1);
stroke-width: 0.5px;
}
.network-container .label-text {
font-size: 10px;
fill: #fff;
font-family: monospace;
text-shadow: 0 1px 2px #000;
pointer-events: none;
}
.network-container .d3-tooltip {
position: absolute;
pointer-events: none;
opacity: 0;
background: rgba(2, 16, 31, 0.95);
border: 1px solid #219ebc;
padding: 12px;
border-radius: 8px;
font-size: 0.85rem;
color: #fff;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.5);
transform: translate(-50%, -110%);
transition: opacity 0.2s;
white-space: nowrap;
z-index: 1000;
}
@media (max-width: 900px) {
.network-container .nv-toolbar {
flex-wrap: wrap;
justify-content: flex-start;
gap: 8px;
}
.network-container .nv-search {
min-width: 0;
flex: 1 1 220px;
}
.network-container .segmented {
order: 3;
}
.network-container table.network-table {
min-width: 100%;
}
.network-container .hosts-content {
min-width: unset;
}
.network-container th.hosts-header,
.network-container td.hosts-cell {
position: static;
}
.network-container .nv-pin-btn {
display: inline-flex;
}
}
@media (max-width: 720px) {
.network-container {
padding: 8px;
}
.network-container .nv-toolbar {
padding: 8px;
}
.network-container table.network-table {
min-width: 100%;
font-size: 0.9rem;
}
.network-container td,
.network-container th {
padding: 6px;
}
.network-container .bubble {
font-size: .82rem;
padding: .35rem .65rem;
}
.network-container .port-bubble {
font-size: .8rem;
padding: .34rem .62rem;
}
}

794
web/css/pages/scheduler.css Normal file
View File

@@ -0,0 +1,794 @@
/* ==========================================================================
SCHEDULER
========================================================================== */
.scheduler-container .toolbar-top {
position: sticky;
top: calc(var(--h-topbar, 0px) + 5px);
z-index: 60;
}
.scheduler-container .controls {
position: sticky;
top: 1px;
z-index: 50;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: .5rem;
padding: .6rem .8rem;
background: var(--panel);
border: 1px solid var(--c-border-strong);
border-radius: 14px;
margin: .6rem .6rem 0 .6rem;
box-shadow: var(--shadow);
backdrop-filter: saturate(1.05) blur(6px);
}
.scheduler-container .pill {
background: var(--panel);
border: 1px solid var(--c-border-strong);
color: var(--ink);
border-radius: 999px;
padding: .45rem .8rem;
cursor: pointer;
user-select: none;
-webkit-user-select: none;
font-weight: 700;
transition: transform .15s ease, box-shadow .2s ease, background .2s ease, color .2s ease;
box-shadow: var(--shadow);
}
.scheduler-container .pill:hover {
transform: translateY(-1px);
box-shadow: 0 10px 26px rgba(0, 0, 0, .35);
}
.scheduler-container .pill.active {
background: var(--grad-card, linear-gradient(135deg, color-mix(in oklab, var(--panel) 92%, transparent), color-mix(in oklab, var(--c-panel) 88%, transparent)));
box-shadow: inset 0 0 0 1px var(--c-border-strong), 0 6px 24px var(--glow-weak);
}
.scheduler-container .controls input[type="text"] {
flex: 1 1 260px;
min-width: 200px;
background: var(--c-panel);
border: 1px solid var(--c-border-strong);
color: var(--ink);
border-radius: 10px;
padding: .5rem .7rem;
box-shadow: var(--shadow);
font-weight: 700;
outline: none;
}
.scheduler-container .controls input[type="text"]:focus-visible,
.scheduler-container .pill:focus-visible {
outline: 2px solid var(--acid);
outline-offset: 2px;
box-shadow: 0 0 0 3px color-mix(in oklab, var(--acid) 25%, transparent);
}
.scheduler-container .stats {
flex-basis: 100%;
margin-left: 0;
text-align: center;
color: var(--muted);
}
/* Board */
.scheduler-container .boardWrap {
height: calc(100vh - (var(--h-topbar, 0px) + 5px) - 56px - 52px);
overflow: auto;
}
.scheduler-container .board {
display: flex;
gap: 14px;
padding: 14px;
min-width: 960px;
}
.scheduler-container .lane {
background: var(--panel);
border: 1px solid var(--c-border-strong);
border-radius: 16px;
width: 340px;
display: flex;
flex-direction: column;
box-shadow: var(--shadow);
min-height: 0;
}
.scheduler-container .laneHeader {
display: flex;
align-items: center;
gap: .6rem;
padding: .6rem .75rem;
border-bottom: 1px solid var(--c-border-strong);
border-top-left-radius: 16px;
border-top-right-radius: 16px;
background: linear-gradient(180deg, color-mix(in oklab, var(--panel) 96%, transparent), color-mix(in oklab, var(--panel) 88%, transparent));
position: sticky;
top: 0;
z-index: 5;
}
.scheduler-container .laneHeader .dot {
width: 10px;
height: 10px;
border-radius: 999px;
box-shadow: 0 0 0 1px rgba(255, 255, 255, .08) inset;
}
.scheduler-container .laneHeader .count {
margin-left: auto;
color: var(--muted);
font-size: .9rem;
}
.scheduler-container .laneBody {
padding: .6rem;
display: flex;
flex-direction: column;
gap: .6rem;
overflow: auto;
min-height: 0;
}
/* Status dot colors */
.scheduler-container .status-upcoming .laneHeader .dot {
background: var(--c-upcoming);
animation: sched-dotPulse 1.6s ease-in-out infinite;
}
.scheduler-container .status-pending .laneHeader .dot {
background: var(--c-pending);
}
.scheduler-container .status-running .laneHeader .dot {
background: var(--c-running);
animation: sched-dotPulse 1.6s ease-in-out infinite;
}
.scheduler-container .status-success .laneHeader .dot {
background: var(--c-success);
}
.scheduler-container .status-failed .laneHeader .dot {
background: var(--c-failed);
}
.scheduler-container .status-expired .laneHeader .dot {
background: var(--c-expired);
}
.scheduler-container .status-cancelled .laneHeader .dot {
background: var(--c-cancel);
}
@keyframes sched-dotPulse {
0%,
100% {
box-shadow: 0 0 0 0 rgba(74, 168, 255, 0);
}
50% {
box-shadow: 0 0 12px 3px rgba(74, 168, 255, .65);
}
}
/* Cards */
.scheduler-container .card {
position: relative;
border: 1px solid var(--c-border-strong);
border-radius: 12px;
padding: .7rem .75rem;
box-shadow: var(--shadow);
display: flex;
flex-direction: column;
gap: .45rem;
overflow: hidden;
transition: transform .15s ease, box-shadow .25s ease, filter .2s ease, background .25s ease;
will-change: transform, box-shadow, filter;
background: var(--c-panel);
}
.scheduler-container .card:hover {
transform: translateY(-1px);
box-shadow: 0 16px 36px rgba(0, 0, 0, .4);
}
.scheduler-container .card .infoBtn {
position: absolute;
top: 6px;
right: 6px;
z-index: 3;
width: 22px;
height: 22px;
line-height: 20px;
font-weight: 800;
text-align: center;
border-radius: 999px;
border: 1px solid var(--c-border-strong);
background: var(--panel);
color: var(--c-upcoming);
cursor: pointer;
user-select: none;
-webkit-user-select: none;
}
.scheduler-container .card .infoBtn:hover {
filter: brightness(1.1);
}
/* Card status backgrounds */
.scheduler-container .card.status-upcoming {
background: color-mix(in oklab, var(--c-upcoming) 12%, var(--c-panel));
animation: sched-breathe 2.6s ease-in-out infinite, sched-halo 2.6s ease-in-out infinite;
}
.scheduler-container .card.status-pending {
background: color-mix(in oklab, var(--c-pending) 10%, var(--c-panel));
animation: sched-breathe 2.6s ease-in-out infinite, sched-haloGray 2.8s ease-in-out infinite;
}
.scheduler-container .card.status-running {
background: color-mix(in oklab, var(--c-running) 12%, var(--c-panel));
animation: sched-pulse 1.8s ease-in-out infinite, sched-haloBlue 2s ease-in-out infinite;
}
.scheduler-container .card.status-success {
background: color-mix(in oklab, var(--c-success) 10%, var(--c-panel));
}
.scheduler-container .card.status-failed {
background: color-mix(in oklab, var(--c-failed) 10%, var(--c-panel));
}
.scheduler-container .card.status-expired {
background: color-mix(in oklab, var(--c-expired) 10%, var(--c-panel));
}
.scheduler-container .card.status-cancelled {
background: color-mix(in oklab, var(--c-cancel) 10%, var(--c-panel));
}
.scheduler-container .badge {
margin-left: auto;
border-radius: 999px;
padding: .15rem .6rem;
font-size: .75rem;
font-weight: 800;
color: #0a0d10;
}
.scheduler-container .card.status-upcoming .badge {
background: var(--c-upcoming);
}
.scheduler-container .card.status-pending .badge {
background: var(--c-pending);
}
.scheduler-container .card.status-running .badge {
background: var(--c-running);
}
.scheduler-container .card.status-success .badge {
background: var(--c-success);
}
.scheduler-container .card.status-failed .badge {
background: var(--c-failed);
}
.scheduler-container .card.status-expired .badge {
background: var(--c-expired);
}
.scheduler-container .card.status-cancelled .badge {
background: var(--c-cancel);
}
/* Collapsed */
.scheduler-container .card.collapsed .kv,
.scheduler-container .card.collapsed .tags,
.scheduler-container .card.collapsed .timer,
.scheduler-container .card.collapsed .meta,
.scheduler-container .card.collapsed .btns,
.scheduler-container .card.collapsed .notice {
display: none !important;
}
.scheduler-container .card.collapsed {
gap: .25rem;
padding: .4rem .5rem;
}
.scheduler-container .card.collapsed .actionIcon {
width: 80px;
height: 80px;
}
.scheduler-container .cardHeader {
display: flex;
align-items: center;
gap: .6rem;
}
.scheduler-container .actionName {
font-weight: 800;
letter-spacing: .2px;
}
.scheduler-container .actionIconWrap {
display: flex;
align-items: center;
justify-content: center;
margin-right: 8px;
}
.scheduler-container .actionIcon {
width: 80px;
height: 80px;
object-fit: contain;
border-radius: 6px;
background: var(--panel);
border: 1px solid var(--c-border);
}
.scheduler-container .card.status-running .actionIcon {
animation: sched-pulseIcon 1.2s ease-in-out infinite;
}
.scheduler-container .card.status-pending .actionIcon {
animation: sched-swayIcon 1.8s ease-in-out infinite;
}
.scheduler-container .card.status-upcoming .actionIcon {
animation: sched-blinkIcon 2s ease-in-out infinite;
}
@keyframes sched-pulseIcon {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.25);
}
}
@keyframes sched-swayIcon {
0%,
100% {
transform: rotate(0deg);
}
25% {
transform: rotate(-5deg);
}
75% {
transform: rotate(5deg);
}
}
@keyframes sched-blinkIcon {
0%,
100% {
opacity: 1;
}
50% {
opacity: .4;
}
}
.scheduler-container .kv {
display: flex;
flex-wrap: wrap;
gap: .45rem .8rem;
font-size: .9rem;
}
.scheduler-container .kv .k {
color: var(--muted);
}
.scheduler-container .tags {
display: flex;
flex-wrap: wrap;
gap: .35rem;
}
.scheduler-container .tag {
background: var(--panel);
color: var(--ink);
border: 1px solid var(--c-border-strong);
padding: .15rem .45rem;
border-radius: 999px;
font-size: .74rem;
box-shadow: var(--shadow);
}
.scheduler-container .meta {
color: color-mix(in oklab, var(--ink) 76%, #9aa7b2);
font-size: .82rem;
display: flex;
flex-wrap: wrap;
gap: .5rem .8rem;
}
.scheduler-container .btns {
display: flex;
flex-wrap: wrap;
gap: .4rem;
margin-top: .2rem;
}
.scheduler-container .btn {
background: var(--panel);
border: 1px solid var(--c-border-strong);
color: var(--ink);
padding: .35rem .6rem;
border-radius: 8px;
cursor: pointer;
}
.scheduler-container .btn:hover {
filter: brightness(1.08);
}
.scheduler-container .btn.danger {
background: color-mix(in oklab, #9c2b2b 22%, var(--panel));
border-color: #4a1515;
color: #ffd0d0;
}
.scheduler-container .btn.warn {
background: color-mix(in oklab, #9c6a2b 22%, var(--panel));
border-color: #5c2c0c;
color: #ffd8a8;
}
.scheduler-container .empty {
color: var(--muted);
text-align: center;
padding: .6rem;
}
@keyframes sched-pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
}
@keyframes sched-breathe {
0%,
100% {
filter: brightness(1);
}
50% {
filter: brightness(1.07);
}
}
@keyframes sched-halo {
0%,
100% {
box-shadow: 0 0 12px rgba(156, 194, 255, .25);
}
50% {
box-shadow: 0 0 22px rgba(156, 194, 255, .45);
}
}
@keyframes sched-haloGray {
0%,
100% {
box-shadow: 0 0 12px rgba(187, 187, 187, .15);
}
50% {
box-shadow: 0 0 22px rgba(187, 187, 187, .3);
}
}
@keyframes sched-haloBlue {
0%,
100% {
box-shadow: 0 0 12px rgba(74, 168, 255, .25);
}
50% {
box-shadow: 0 0 26px rgba(74, 168, 255, .5);
}
}
/* Timer / Progress */
.scheduler-container .timer {
font-size: .82rem;
color: color-mix(in oklab, var(--ink) 80%, #bcd7ff);
display: flex;
align-items: center;
gap: .4rem;
}
.scheduler-container .timer .cd {
font-variant-numeric: tabular-nums;
}
.scheduler-container .progress {
height: 6px;
background: var(--panel);
border: 1px solid var(--c-border-strong);
border-radius: 999px;
overflow: hidden;
}
.scheduler-container .progress .bar {
height: 100%;
width: 0%;
background: linear-gradient(90deg, var(--c-running), #00d8ff);
}
/* More button */
.scheduler-container .moreWrap {
display: flex;
justify-content: center;
}
.scheduler-container .moreBtn {
background: var(--panel);
border: 1px solid var(--c-border-strong);
color: var(--ink);
border-radius: 10px;
padding: .45rem .8rem;
cursor: pointer;
transition: transform .15s;
margin: .25rem auto 0;
box-shadow: var(--shadow);
}
.scheduler-container .moreBtn:hover {
transform: translateY(-1px);
}
/* Notice */
.scheduler-container .notice {
padding: .6rem .8rem;
color: #ffd9d6;
background: color-mix(in oklab, #7a3838 55%, var(--panel));
border-bottom: 1px solid #7a3838;
display: none;
border-radius: 12px;
margin: .6rem;
}
/* Chips */
.scheduler-container .chips {
display: flex;
flex-wrap: wrap;
gap: .35rem;
margin: .1rem 0 .2rem;
justify-content: center;
}
.scheduler-container .chip {
--h: 200;
display: inline-flex;
align-items: center;
gap: .4rem;
padding: .25rem .55rem;
border-radius: 999px;
font-size: .82rem;
font-weight: 800;
color: #fff;
letter-spacing: .2px;
background: linear-gradient(135deg, rgba(255, 255, 255, .06), rgba(0, 0, 0, .12)), hsl(var(--h), 65%, 34%);
border: 1px solid hsla(var(--h), 70%, 60%, .35);
box-shadow: 0 6px 16px rgba(0, 0, 0, .22), inset 0 1px 0 rgba(255, 255, 255, .06);
transition: transform .15s ease, box-shadow .2s ease, filter .2s ease;
}
.scheduler-container .chip:hover {
transform: translateY(-1px);
box-shadow: 0 10px 22px rgba(0, 0, 0, .28);
}
.scheduler-container .chip .k {
opacity: .85;
font-weight: 700;
}
/* History modal */
.scheduler-container .modalOverlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, .5);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
}
.scheduler-container .modal {
width: min(860px, 92vw);
max-height: 80vh;
background: var(--panel);
border: 1px solid var(--c-border-strong);
border-radius: 14px;
box-shadow: 0 20px 56px rgba(0, 0, 0, .6);
display: flex;
flex-direction: column;
overflow: hidden;
}
.scheduler-container .modalHeader {
display: flex;
align-items: center;
gap: .6rem;
padding: .6rem .8rem;
border-bottom: 1px solid var(--c-border-strong);
background: linear-gradient(180deg, color-mix(in oklab, var(--panel) 96%, transparent), color-mix(in oklab, var(--panel) 88%, transparent));
}
.scheduler-container .modalHeader .title {
font-weight: 900;
}
.scheduler-container .modalHeader .spacer {
flex: 1;
}
.scheduler-container .modalBody {
padding: .6rem .8rem;
overflow: auto;
display: flex;
flex-direction: column;
gap: .35rem;
}
.scheduler-container .modalFooter {
padding: .5rem .8rem;
border-top: 1px solid var(--c-border-strong);
display: flex;
gap: .5rem;
justify-content: flex-end;
color: var(--muted);
}
.scheduler-container .xBtn,
.scheduler-container .miniToggle {
background: var(--panel);
color: var(--ink);
border: 1px solid var(--c-border-strong);
border-radius: 8px;
padding: .35rem .6rem;
cursor: pointer;
}
.scheduler-container .xBtn:hover,
.scheduler-container .miniToggle:hover {
filter: brightness(1.08);
}
.scheduler-container #searchBox {
width: 100%;
background: var(--c-panel);
border: 1px solid var(--c-border-strong);
color: var(--ink);
border-radius: 10px;
padding: .5rem .7rem;
box-shadow: var(--shadow);
font-weight: 700;
outline: none;
}
.scheduler-container .histRow {
display: flex;
align-items: center;
gap: .6rem;
padding: .45rem .6rem;
border-radius: 10px;
border: 1px solid var(--c-border-strong);
background: color-mix(in oklab, var(--ink) 2%, var(--panel));
}
.scheduler-container .histRow .ts {
color: var(--muted);
font-variant-numeric: tabular-nums;
}
.scheduler-container .histRow .st {
font-weight: 900;
margin-left: auto;
padding: .1rem .5rem;
border-radius: 999px;
font-size: .75rem;
color: #0a0d10;
}
.scheduler-container .hist-success {
background: color-mix(in oklab, var(--c-success) 8%, var(--panel));
border-left: 3px solid var(--c-success);
}
.scheduler-container .hist-failed {
background: color-mix(in oklab, var(--c-failed) 8%, var(--panel));
border-left: 3px solid var(--c-failed);
}
.scheduler-container .hist-running {
background: color-mix(in oklab, var(--c-running) 8%, var(--panel));
border-left: 3px solid var(--c-running);
}
.scheduler-container .hist-pending,
.scheduler-container .hist-scheduled {
background: color-mix(in oklab, var(--c-pending) 8%, var(--panel));
border-left: 3px solid var(--c-pending);
}
.scheduler-container .hist-expired {
background: color-mix(in oklab, var(--c-expired) 8%, var(--panel));
border-left: 3px solid var(--c-expired);
}
.scheduler-container .hist-cancelled {
background: color-mix(in oklab, var(--c-cancel) 8%, var(--panel));
border-left: 3px solid var(--c-cancel);
}
.scheduler-container .hist-superseded {
background: color-mix(in oklab, var(--c-super) 8%, var(--panel));
border-left: 3px solid var(--c-super);
}
@media (max-width:920px) {
.scheduler-container .board {
flex-direction: column;
min-width: 0;
}
.scheduler-container .lane {
width: auto;
}
.scheduler-container .stats {
width: 100%;
margin-left: 0;
}
.scheduler-container .boardWrap {
height: auto;
min-height: calc(100vh - (var(--h-topbar, 0px) + 5px));
}
}
@media (prefers-reduced-motion: reduce) {
.scheduler-container .card,
.scheduler-container .laneHeader .dot {
animation: none !important;
}
}

366
web/css/pages/sentinel.css Normal file
View File

@@ -0,0 +1,366 @@
/* ============================================================
Sentinel Watchdog — SPA page styles
============================================================ */
.sentinel-page {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
gap: 12px;
padding: 15px;
}
/* ── Header bar ─────────────────────────────────────────── */
.sentinel-header {
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
flex-wrap: wrap;
gap: 10px;
}
.sentinel-title {
margin: 0;
font-size: 1.3rem;
font-weight: 800;
color: var(--ink);
display: flex;
align-items: center;
gap: 8px;
}
.sentinel-title-icon {
font-size: 1.5rem;
}
.sentinel-controls {
display: flex;
gap: 8px;
align-items: center;
}
.sentinel-toggle {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 14px;
border-radius: 8px;
border: 1px solid var(--c-border);
background: var(--c-panel);
font-size: 0.8rem;
font-weight: 700;
cursor: pointer;
transition: 0.2s;
}
.sentinel-toggle.active {
border-color: var(--acid);
background: rgba(0, 255, 154, 0.08);
color: var(--acid);
box-shadow: 0 0 12px rgba(0, 255, 154, 0.15);
}
.sentinel-toggle .dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--muted-off);
transition: 0.2s;
}
.sentinel-toggle.active .dot {
background: var(--acid);
box-shadow: 0 0 6px var(--acid);
animation: sentinel-pulse 2s infinite;
}
@keyframes sentinel-pulse {
0%, 100% { opacity: 0.7; box-shadow: 0 0 4px var(--acid); }
50% { opacity: 1; box-shadow: 0 0 12px var(--acid); }
}
/* ── Stats bar ──────────────────────────────────────────── */
.sentinel-stats {
display: flex;
gap: 10px;
flex-shrink: 0;
flex-wrap: wrap;
}
.sentinel-stat {
flex: 1 1 120px;
padding: 10px 14px;
background: var(--grad-card);
border: 1px solid var(--c-border);
border-radius: 10px;
text-align: center;
min-width: 100px;
}
.sentinel-stat-val {
font-size: 1.4rem;
font-weight: 800;
font-family: 'Fira Code', monospace;
color: var(--ink);
line-height: 1.2;
}
.sentinel-stat-lbl {
font-size: 0.65rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-top: 4px;
}
/* ── Main grid ──────────────────────────────────────────── */
.sentinel-grid {
display: grid;
grid-template-columns: 1fr 340px;
gap: 12px;
flex: 1;
min-height: 0;
}
/* ── Panels ─────────────────────────────────────────────── */
.sentinel-panel {
background: var(--grad-card);
border: 1px solid var(--c-border);
border-radius: 12px;
display: flex;
flex-direction: column;
overflow: hidden;
min-height: 0;
}
.sentinel-panel-head {
padding: 10px 14px;
font-size: 0.72rem;
font-weight: 700;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 1px;
border-bottom: 1px solid var(--c-border);
background: rgba(0, 0, 0, 0.15);
flex-shrink: 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.sentinel-panel-body {
flex: 1;
overflow-y: auto;
padding: 10px;
display: flex;
flex-direction: column;
gap: 6px;
}
/* ── Event cards ────────────────────────────────────────── */
.sentinel-event {
padding: 10px 12px;
border-radius: 8px;
border-left: 3px solid var(--c-border);
background: color-mix(in oklab, var(--c-panel) 80%, transparent);
transition: background 0.15s;
cursor: pointer;
}
.sentinel-event:hover {
background: color-mix(in oklab, var(--c-panel) 100%, transparent);
}
.sentinel-event.unread {
border-left-color: var(--acid);
}
.sentinel-event.sev-warning {
border-left-color: var(--warning);
}
.sentinel-event.sev-critical {
border-left-color: var(--danger);
}
.sentinel-event-head {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 8px;
margin-bottom: 4px;
}
.sentinel-event-title {
font-size: 0.82rem;
font-weight: 700;
color: var(--ink);
flex: 1;
}
.sentinel-event-time {
font-size: 0.65rem;
color: var(--muted);
white-space: nowrap;
flex-shrink: 0;
}
.sentinel-event-body {
font-size: 0.72rem;
color: var(--muted);
line-height: 1.4;
}
.sentinel-event-badge {
display: inline-block;
padding: 1px 6px;
border-radius: 4px;
font-size: 0.6rem;
font-weight: 800;
letter-spacing: 0.5px;
text-transform: uppercase;
margin-right: 6px;
}
.sentinel-event-badge.new_device { background: rgba(0,220,255,0.15); color: #00dcff; }
.sentinel-event-badge.device_join { background: rgba(0,255,160,0.15); color: #00ffa0; }
.sentinel-event-badge.device_leave { background: rgba(255,255,255,0.08); color: #888; }
.sentinel-event-badge.arp_spoof { background: rgba(255,59,59,0.15); color: #ff3b3b; }
.sentinel-event-badge.port_change { background: rgba(255,209,102,0.15); color: #ffd166; }
.sentinel-event-badge.mac_flood { background: rgba(255,59,59,0.2); color: #ff3b3b; }
.sentinel-event-badge.rogue_dhcp { background: rgba(255,100,180,0.15); color: #ff64b4; }
.sentinel-event-badge.dns_anomaly { background: rgba(180,140,255,0.15); color: #b48cff; }
/* ── Sidebar tabs ───────────────────────────────────────── */
.sentinel-side-tabs {
display: flex;
gap: 2px;
padding: 6px;
background: rgba(0, 0, 0, 0.15);
border-bottom: 1px solid var(--c-border);
flex-shrink: 0;
}
.sentinel-side-tab {
flex: 1;
padding: 5px 8px;
border: none;
border-radius: 6px;
background: transparent;
color: var(--muted);
font-size: 0.7rem;
font-weight: 700;
cursor: pointer;
transition: 0.15s;
}
.sentinel-side-tab.active {
background: var(--c-panel);
color: var(--acid);
}
/* ── Rules list ─────────────────────────────────────────── */
.sentinel-rule {
padding: 8px 10px;
border-radius: 8px;
border: 1px solid var(--c-border);
background: color-mix(in oklab, var(--c-panel) 60%, transparent);
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.sentinel-rule-info {
flex: 1;
min-width: 0;
}
.sentinel-rule-name {
font-size: 0.78rem;
font-weight: 700;
color: var(--ink);
}
.sentinel-rule-type {
font-size: 0.65rem;
color: var(--muted);
}
.sentinel-rule-actions {
display: flex;
gap: 4px;
align-items: center;
}
/* ── Notifiers config ───────────────────────────────────── */
.sentinel-notifier-row {
display: flex;
flex-direction: column;
gap: 4px;
padding: 8px 10px;
border-radius: 8px;
border: 1px solid var(--c-border);
background: color-mix(in oklab, var(--c-panel) 60%, transparent);
}
.sentinel-notifier-label {
font-size: 0.72rem;
font-weight: 700;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.sentinel-notifier-input {
width: 100%;
padding: 6px 8px;
background: var(--c-panel);
border: 1px solid var(--c-border);
border-radius: 6px;
color: var(--ink);
font-size: 0.75rem;
font-family: 'Fira Code', monospace;
}
/* ── Responsive ─────────────────────────────────────────── */
@media (max-width: 900px) {
.sentinel-grid {
grid-template-columns: 1fr;
}
.sentinel-stats {
flex-wrap: wrap;
}
.sentinel-stat {
flex: 1 1 80px;
min-width: 70px;
padding: 8px 10px;
}
.sentinel-stat-val {
font-size: 1.1rem;
}
.sentinel-page {
padding: 10px;
gap: 8px;
}
.sentinel-header {
flex-direction: column;
align-items: flex-start;
}
}

217
web/css/pages/shared.css Normal file
View File

@@ -0,0 +1,217 @@
/* ==========================================================================
pages.css — Page-specific styles for all SPA page modules.
Each section is scoped under the page's wrapper class to avoid conflicts.
========================================================================== */
/* ===== Page-specific variables (extends global.css tokens) ===== */
:root {
/* Bridge aliases used by multiple pages (Credentials, Loot, Files, Attacks) */
--_bg: var(--bg);
--_panel: var(--c-panel-2);
--_panel-hi: color-mix(in oklab, var(--c-panel-2) 96%, transparent);
--_panel-lo: color-mix(in oklab, var(--c-panel-2) 86%, transparent);
--_border: var(--c-border);
--_ink: var(--ink);
--_muted: var(--muted);
--_acid: var(--acid);
--_acid2: var(--acid-2);
--_shadow: var(--shadow);
/* NetKB chip colors */
--kb-hostname-bg: color-mix(in oklab, var(--acid) 16%, transparent);
--kb-ip-bg: color-mix(in oklab, var(--acid-2) 18%, transparent);
--kb-mac-bg: color-mix(in oklab, var(--muted) 10%, transparent);
--kb-vendor-bg: color-mix(in oklab, #b18cff 16%, transparent);
--kb-ports-bg: color-mix(in oklab, #5fd1ff 16%, transparent);
--kb-essid-bg: color-mix(in oklab, #00e6c3 16%, transparent);
--kb-offline-bg: color-mix(in oklab, var(--bg-2) 88%, black 12%);
--kb-offline-brd: color-mix(in oklab, var(--c-border-strong) 60%, transparent);
--kb-offline-ring: color-mix(in oklab, #ff5b5b 30%, transparent);
--kb-badge-shimmer: linear-gradient(90deg, transparent, rgba(255, 255, 255, .22), transparent);
/* Attacks page */
--tile-min: 160px;
--ok-glow: rgba(34, 197, 94, .45);
--ko-glow: rgba(239, 68, 68, .45);
}
/* ===== Shared sidebar layout (SPA parity with web_old) ===== */
.page-with-sidebar {
--page-sidebar-w: 280px;
position: relative;
display: flex;
gap: 12px;
min-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 24px);
align-items: stretch;
}
.page-with-sidebar .page-sidebar {
width: var(--page-sidebar-w);
flex: 0 0 var(--page-sidebar-w);
position: sticky;
top: 0;
align-self: flex-start;
max-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 24px);
min-width: 0;
display: flex;
flex-direction: column;
border: 1px solid var(--c-border);
border-radius: 12px;
background: var(--grad-card);
box-shadow: var(--shadow);
overflow: hidden;
}
.page-with-sidebar .page-main {
min-width: 0;
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.page-with-sidebar .sidebar-toggle-btn {
display: inline-flex;
margin-bottom: 0;
align-self: auto;
}
.page-with-sidebar .sidebar-fab {
position: fixed;
right: 14px;
bottom: calc(var(--h-bottombar, 56px) + 14px);
z-index: 82;
border-radius: 999px;
width: 38px;
height: 38px;
min-width: 38px;
min-height: 38px;
padding: 0;
font-size: 16px;
color: var(--ink);
background: color-mix(in oklab, var(--c-panel) 88%, transparent);
border: 1px solid var(--c-border-strong);
box-shadow: 0 6px 16px rgba(0, 0, 0, .28);
opacity: .88;
display: inline-flex;
align-items: center;
justify-content: center;
line-height: 1;
}
.page-with-sidebar .sidebar-fab:hover {
opacity: 1;
transform: translateY(-1px);
}
.page-with-sidebar .sidebar-fab:active {
transform: translateY(0);
}
.page-sidebar-backdrop {
display: none;
position: fixed;
left: 0;
right: 0;
top: var(--h-topbar, 56px);
bottom: var(--h-bottombar, 56px);
background: rgba(0, 0, 0, .52);
border: 0;
z-index: 79;
}
.page-with-sidebar .sidehead {
padding: 10px;
border-bottom: 1px dashed var(--c-border);
display: flex;
align-items: center;
gap: 8px;
position: sticky;
top: 0;
z-index: 5;
background: var(--grad-card);
flex-shrink: 0;
}
.page-with-sidebar .sidetitle {
font-weight: 800;
color: var(--acid);
letter-spacing: .05em;
}
.page-with-sidebar .sidecontent {
padding: 10px;
overflow: auto;
min-height: 0;
flex: 1;
}
.page-with-sidebar.sidebar-collapsed .page-sidebar {
width: 0;
flex-basis: 0;
padding: 0;
border-width: 0;
overflow: hidden;
}
@media (max-width: 900px) {
.page-with-sidebar {
min-height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 12px);
}
.page-with-sidebar .sidebar-fab {
right: 10px;
bottom: calc(var(--h-bottombar, 56px) + 10px);
}
.sidebar-fab-unified {
position: fixed;
z-index: 82;
border-radius: 999px;
width: 38px;
height: 38px;
min-width: 38px;
min-height: 38px;
padding: 0;
font-size: 16px;
color: var(--ink);
background: color-mix(in oklab, var(--c-panel) 88%, transparent);
border: 1px solid var(--c-border-strong);
box-shadow: 0 6px 16px rgba(0, 0, 0, .28);
opacity: .88;
display: inline-flex;
align-items: center;
justify-content: center;
line-height: 1;
}
.sidebar-fab-unified:hover {
opacity: 1;
transform: translateY(-1px);
}
.sidebar-fab-unified:active {
transform: translateY(0);
}
.page-with-sidebar .page-sidebar {
position: fixed;
top: var(--h-topbar, 56px);
bottom: var(--h-bottombar, 56px);
left: 0;
z-index: 80;
width: min(86vw, 320px);
flex-basis: auto;
transform: translateX(-105%);
transition: transform .2s ease;
}
.page-with-sidebar.sidebar-open .page-sidebar {
transform: translateX(0);
}
.page-with-sidebar.sidebar-open .page-sidebar-backdrop {
display: block;
}
}

View File

@@ -0,0 +1,698 @@
/* ==========================================================================
VULNERABILITIES
========================================================================== */
.vuln-container {
padding: var(--gap-4);
min-height: calc(100vh - var(--h-topbar) - var(--h-bottombar));
animation: vuln-fadeIn 0.5s ease-in;
}
@keyframes vuln-fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.vuln-container .stats-header {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: var(--gap-4);
margin-bottom: var(--gap-3);
}
.vuln-container .stat-card {
background: var(--grad-card);
border-radius: var(--radius);
padding: var(--gap-4);
text-align: center;
border: 1px solid var(--c-border);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
box-shadow: var(--elev);
}
.vuln-container .stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
animation: vuln-pulse 2s infinite;
}
.vuln-container .stat-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-hover);
}
.vuln-container .stat-number {
font-size: 28px;
font-weight: bold;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin: 5px 0;
}
.vuln-container .stat-label {
font-size: 12px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 1px;
}
.vuln-container .control-bar {
background: var(--grad-card);
border-radius: var(--radius);
padding: var(--gap-4);
margin-bottom: var(--gap-3);
display: flex;
flex-wrap: wrap;
gap: var(--gap-3);
align-items: center;
border: 1px solid var(--c-border);
box-shadow: var(--elev);
}
.vuln-container .search-box {
flex: 1;
min-width: 200px;
position: relative;
}
.vuln-container .search-input {
width: 100%;
height: var(--control-h);
padding: 0 40px 0 var(--control-pad-x);
background: var(--c-panel);
border: 1px solid var(--c-border-strong);
border-radius: var(--control-r);
color: var(--ink);
font-size: 14px;
transition: all 0.3s ease;
}
.vuln-container .search-input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--glow-weak);
}
.vuln-container .clear-search {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: var(--danger);
cursor: pointer;
font-size: 18px;
display: none;
transition: color 0.3s ease;
}
.vuln-container .clear-search:hover {
color: var(--acid-2);
}
.vuln-container .clear-search.show {
display: block;
}
.vuln-container .filter-buttons {
display: flex;
gap: var(--gap-3);
}
.vuln-container .filter-btn.active {
background: linear-gradient(90deg, var(--accent), var(--accent-2));
border-color: var(--accent);
}
.vuln-container .severity-filter {
display: flex;
gap: var(--gap-2);
}
.vuln-container .severity-btn.critical.active {
background: var(--danger);
border-color: var(--danger);
color: var(--white);
}
.vuln-container .severity-btn.high.active {
background: var(--warning);
border-color: var(--warning);
color: var(--ink-invert);
}
.vuln-container .severity-btn.medium.active {
background: var(--accent-2);
border-color: var(--accent-2);
color: var(--ink-invert);
}
.vuln-container .severity-btn.low.active {
background: var(--ok);
border-color: var(--ok);
color: var(--ink-invert);
}
.vuln-container .vuln-grid {
display: grid;
gap: var(--gap-4);
max-height: calc(100vh - 250px);
overflow-y: auto;
}
.vuln-container .vuln-card {
background: var(--grad-card);
border-radius: var(--radius);
border: 1px solid var(--c-border);
overflow: hidden;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
animation: vuln-slideIn 0.4s ease-out;
box-shadow: var(--elev);
}
@keyframes vuln-slideIn {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.vuln-container .vuln-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-hover);
border-color: var(--accent);
}
.vuln-container .vuln-card.inactive {
opacity: 0.6;
border-color: var(--muted-off);
}
.vuln-container .vuln-header {
padding: var(--gap-4);
background: var(--grad-quickpanel);
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
user-select: none;
border-bottom: 1px solid var(--c-border);
}
.vuln-container .vuln-title {
display: flex;
align-items: center;
gap: var(--gap-3);
flex: 1;
}
.vuln-container .vuln-id {
font-weight: bold;
font-size: 14px;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.vuln-container .severity-badge {
padding: 4px 10px;
border-radius: 20px;
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.5px;
animation: vuln-pulse 2s infinite;
}
@keyframes vuln-pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.7;
}
100% {
opacity: 1;
}
}
.vuln-container .severity-critical {
background: var(--danger);
color: var(--white);
}
.vuln-container .severity-high {
background: var(--warning);
color: var(--ink-invert);
}
.vuln-container .severity-medium {
background: var(--accent-2);
color: var(--ink-invert);
}
.vuln-container .severity-low {
background: var(--ok);
color: var(--ink-invert);
}
.vuln-container .vuln-meta {
display: flex;
gap: var(--gap-4);
font-size: 12px;
color: var(--muted);
}
.vuln-container .meta-item {
display: flex;
align-items: center;
gap: var(--gap-2);
}
.vuln-container .expand-icon {
color: var(--muted);
transition: transform 0.3s ease;
font-size: 18px;
}
.vuln-container .vuln-card.expanded .expand-icon {
transform: rotate(180deg);
}
.vuln-container .vuln-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
.vuln-container .vuln-card.expanded .vuln-content {
max-height: 1000px;
}
.vuln-container .vuln-details {
padding: var(--gap-4);
border-top: 1px solid var(--c-border);
background: var(--c-panel);
}
.vuln-container .detail-section {
margin-bottom: var(--gap-4);
}
.vuln-container .detail-title {
font-size: 12px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: var(--gap-2);
font-weight: 600;
}
.vuln-container .detail-content {
font-size: 14px;
line-height: 1.6;
color: var(--ink);
}
.vuln-container .tags-container {
display: flex;
flex-wrap: wrap;
gap: var(--gap-2);
}
.vuln-container .tag {
padding: 4px 8px;
background: var(--c-chip-bg);
border: 1px solid var(--c-border);
border-radius: var(--gap-2);
font-size: 11px;
color: var(--muted);
}
.vuln-container .action-buttons {
display: flex;
gap: var(--gap-3);
padding: var(--gap-4);
border-top: 1px solid var(--c-border);
background: var(--c-panel-2);
}
.vuln-container .action-btn {
flex: 1;
justify-content: center;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.vuln-container .btn-remediate {
background: var(--ok);
border-color: var(--ok);
color: var(--ink-invert);
}
.vuln-container .btn-details {
background: var(--accent-2);
border-color: var(--accent-2);
color: var(--ink-invert);
}
.vuln-container .btn-export {
background: linear-gradient(90deg, var(--accent), var(--accent-2));
border-color: var(--accent);
color: var(--white);
}
/* Host view */
.vuln-container .host-card {
background: var(--grad-card);
border-radius: var(--radius);
border: 1px solid var(--c-border);
margin-bottom: var(--gap-4);
overflow: hidden;
animation: vuln-slideIn 0.4s ease-out;
box-shadow: var(--elev);
}
.vuln-container .host-header {
background: var(--grad-quickpanel);
padding: var(--gap-4);
cursor: pointer;
user-select: none;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--c-border);
}
.vuln-container .host-header:hover {
background: var(--grad-modal);
}
.vuln-container .host-info {
display: flex;
flex-direction: column;
gap: var(--gap-2);
}
.vuln-container .host-name {
font-size: 16px;
font-weight: bold;
color: var(--ink);
display: flex;
align-items: center;
gap: var(--gap-3);
}
.vuln-container .host-details {
display: flex;
gap: var(--gap-4);
font-size: 12px;
color: var(--muted);
}
.vuln-container .host-stats {
display: flex;
gap: var(--gap-3);
align-items: center;
}
.vuln-container .host-stat-badge {
padding: 5px 10px;
border-radius: 20px;
font-size: 11px;
font-weight: bold;
display: flex;
align-items: center;
gap: var(--gap-2);
}
.vuln-container .host-vulns {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
.vuln-container .host-card.expanded .host-vulns {
max-height: 2000px;
}
.vuln-container .host-vuln-list {
padding: var(--gap-4);
background: var(--c-panel);
}
.vuln-container .host-vuln-item {
background: var(--c-panel-2);
border: 1px solid var(--c-border);
border-radius: var(--control-r);
padding: var(--gap-3);
margin-bottom: var(--gap-3);
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.3s ease;
}
.vuln-container .host-vuln-item:hover {
background: var(--grad-card);
border-color: var(--accent);
transform: translateX(5px);
}
.vuln-container .host-summary {
background: var(--grad-quickpanel);
padding: var(--gap-3);
border-radius: var(--control-r);
margin-bottom: var(--gap-3);
display: flex;
justify-content: space-around;
text-align: center;
}
.vuln-container .host-summary-item {
display: flex;
flex-direction: column;
gap: var(--gap-2);
}
.vuln-container .host-summary-value {
font-size: 18px;
font-weight: bold;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.vuln-container .host-summary-label {
font-size: 10px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Badges */
.vuln-container .badge-kev {
background: var(--danger);
padding: 2px 6px;
border-radius: 10px;
font-size: 10px;
color: var(--white);
font-weight: bold;
}
.vuln-container .badge-exploit {
background: linear-gradient(135deg, #9c27b0, #e1bee7);
padding: 2px 6px;
border-radius: 10px;
font-size: 10px;
color: var(--white);
font-weight: bold;
}
.vuln-container .badge-epss-high {
background: linear-gradient(135deg, var(--danger), var(--warning));
padding: 2px 6px;
border-radius: 10px;
font-size: 10px;
color: var(--white);
font-weight: bold;
}
.vuln-container .badge-epss-medium {
background: linear-gradient(135deg, var(--warning), var(--accent-2));
padding: 2px 6px;
border-radius: 10px;
font-size: 10px;
color: var(--white);
font-weight: bold;
}
/* Pagination */
.vuln-container .pagination {
display: flex;
justify-content: center;
gap: var(--gap-3);
margin-top: var(--gap-4);
padding: var(--gap-3);
}
.vuln-container .page-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.vuln-container .page-btn.active {
background: linear-gradient(90deg, var(--accent), var(--accent-2));
border-color: var(--accent);
color: var(--white);
}
.vuln-container .page-info {
display: flex;
align-items: center;
color: var(--muted);
font-size: 13px;
}
/* Modal */
.vuln-container .modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--glass-8);
z-index: 1000;
animation: vuln-fadeIn 0.3s ease;
}
.vuln-container .modal.show {
display: flex;
align-items: center;
justify-content: center;
}
.vuln-container .modal-content {
background: var(--grad-modal);
border-radius: var(--radius);
max-width: 800px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
animation: vuln-slideUp 0.3s ease;
border: 1px solid var(--c-border-strong);
box-shadow: var(--shadow-hover);
}
@keyframes vuln-slideUp {
from {
transform: translateY(50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.vuln-container .modal-header {
padding: var(--gap-4);
border-bottom: 1px solid var(--c-border);
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
background: var(--grad-quickpanel);
z-index: 1;
}
.vuln-container .modal-title {
font-size: 18px;
font-weight: bold;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.vuln-container .close-modal {
background: none;
border: none;
color: var(--muted);
font-size: 24px;
cursor: pointer;
transition: color 0.3s ease;
}
.vuln-container .close-modal:hover {
color: var(--ink);
}
.vuln-container .modal-body {
padding: var(--gap-4);
}
@media (max-width:768px) {
.vuln-container .stats-header {
grid-template-columns: repeat(2, 1fr);
}
.vuln-container .control-bar {
flex-direction: column;
}
.vuln-container .search-box {
width: 100%;
}
.vuln-container .filter-buttons {
width: 100%;
justify-content: space-between;
}
.vuln-container .severity-filter {
width: 100%;
justify-content: space-between;
}
.vuln-container .vuln-header {
flex-direction: column;
align-items: flex-start;
gap: var(--gap-3);
}
.vuln-container .vuln-meta {
flex-direction: column;
gap: var(--gap-2);
}
.vuln-container .modal-content {
width: 95%;
max-height: 90vh;
}
}

View File

@@ -0,0 +1,493 @@
/* ==========================================================================
ZOMBIELAND (C2 Module) CSS
========================================================================== */
/* Main layout constraints */
.zombieland-container.page-with-sidebar {
height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 24px);
display: flex;
min-height: 0;
}
.zl-sidebar.page-sidebar {
width: 260px;
flex-shrink: 0;
display: flex;
flex-direction: column;
background: var(--grad-card);
border: 1px solid var(--c-border);
border-radius: 12px;
overflow-y: auto;
}
.zl-main.page-main {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 12px;
}
/* Sidebar structure */
.zl-stats-grid {
display: grid;
grid-template-columns: 1fr;
gap: 8px;
margin-bottom: 15px;
}
.stat-item {
background: rgba(0, 0, 0, 0.2);
border: 1px solid var(--c-border);
padding: 10px;
border-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
}
.stat-item .stat-value {
font-weight: bold;
color: var(--acid);
font-size: 1.1rem;
}
.stat-item .stat-label {
font-size: 0.8rem;
color: var(--muted);
text-transform: uppercase;
}
.zl-toolbar {
display: flex;
flex-direction: column;
gap: 6px;
}
/* Modals */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: var(--grad-card);
border: 1px solid var(--c-border);
padding: 20px;
border-radius: 12px;
width: 100%;
max-width: 400px;
}
/* Main Grid Layout */
.zl-main-grid {
display: grid;
grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
gap: 12px;
flex: 1;
/* Takes available space except logs */
min-height: 0;
}
.zl-console-panel,
.zl-agents-panel,
.zl-logs-panel {
border: 1px solid var(--c-border);
border-radius: 12px;
background: var(--c-panel);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Log Panel underneath */
.zl-logs-panel {
height: 150px;
flex-shrink: 0;
}
.zl-panel-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: rgba(0, 0, 0, 0.2);
border-bottom: 1px solid var(--c-border);
flex-shrink: 0;
}
.zl-panel-title {
font-weight: bold;
font-size: 0.9rem;
color: var(--acid);
}
.zl-quickbar {
display: flex;
gap: 4px;
}
.quick-cmd {
background: transparent;
border: 1px solid var(--c-border-strong);
color: var(--muted);
font-size: 0.7rem;
padding: 2px 6px;
border-radius: 4px;
cursor: pointer;
transition: 0.2s;
}
.quick-cmd:hover {
color: var(--acid);
border-color: var(--acid);
}
.zl-console-output,
.zl-logs-output {
flex: 1;
overflow-y: auto;
padding: 10px;
font-family: 'Fira Code', monospace;
font-size: 0.8rem;
background: #020406;
min-height: 0;
}
/* Controls */
.zl-console-input-row {
display: flex;
gap: 8px;
padding: 8px;
border-top: 1px solid var(--c-border);
background: var(--c-panel-2);
flex-shrink: 0;
}
.zl-target-select,
.zl-cmd-input,
.zl-search-input {
background: #000;
color: #fff;
border: 1px solid var(--c-border-strong);
padding: 6px 10px;
border-radius: 6px;
}
.zl-cmd-input {
flex: 1;
}
.zl-toolbar-left {
display: flex;
position: relative;
flex: 1;
max-width: 200px;
margin-left: 10px;
}
.zl-search-input {
width: 100%;
border-radius: 6px;
padding: 4px 20px 4px 8px;
font-size: 0.8rem;
}
.zl-search-clear {
position: absolute;
right: 5px;
top: 5px;
color: var(--muted);
background: none;
border: none;
cursor: pointer;
}
.zl-agents-list {
flex: 1;
overflow-y: auto;
padding: 10px;
display: flex;
flex-direction: column;
gap: 8px;
}
/* Agent Card Styles */
.zl-agent-card {
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--c-border);
border-radius: 8px;
padding: 8px 12px;
display: flex;
flex-direction: column;
gap: 6px;
transition: 0.2s ease-out;
}
.zl-agent-card.selected {
border-color: var(--acid);
background: rgba(0, 255, 160, 0.05);
}
.zl-agent-card:hover {
border-color: var(--c-border-hi);
}
.zl-card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.zl-card-identity {
display: flex;
flex-direction: column;
gap: 2px;
line-height: 1;
}
.zl-card-hostname {
font-weight: bold;
color: #fff;
font-size: 0.9rem;
}
.zl-card-id {
font-size: 0.7rem;
color: var(--muted);
}
.zl-pill {
padding: 2px 8px;
border-radius: 12px;
font-size: 0.7rem;
font-weight: bold;
background: #222;
}
.zl-pill.online {
color: #00ffa0;
background: rgba(0, 255, 160, 0.1);
}
.zl-pill.idle {
color: #ffcc00;
background: rgba(255, 204, 0, 0.1);
}
.zl-pill.offline {
color: #ff3333;
background: rgba(255, 51, 51, 0.1);
}
/* ECG Animation */
.zl-ecg-row {
display: flex;
align-items: center;
gap: 8px;
}
.ecg {
width: 100%;
height: 24px;
max-width: 140px;
position: relative;
overflow: hidden;
background: rgba(0, 0, 0, 0.5);
border-radius: 6px;
border: 1px solid #111;
}
.ecg-wrapper {
display: flex;
width: 300%;
animation: ecg-slide linear infinite;
}
.ecg svg {
width: 33.33%;
height: 100%;
}
.ecg path {
fill: none;
stroke-width: 1.5;
stroke-linecap: round;
stroke-linejoin: round;
}
.ecg.green path {
stroke: #00ffa0;
filter: drop-shadow(0 0 2px #00ffa0);
}
.ecg.yellow path {
stroke: #ffcc00;
filter: drop-shadow(0 0 2px #ffcc00);
}
.ecg.orange path {
stroke: #ff8800;
filter: drop-shadow(0 0 2px #ff8800);
}
.ecg.red path {
stroke: #ff3333;
}
.ecg.flat .ecg-wrapper {
animation: none;
}
@keyframes ecg-slide {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-33.33%);
}
}
.zl-ecg-counter {
font-size: 0.7rem;
color: var(--muted);
font-family: monospace;
}
.zl-card-info {
display: flex;
justify-content: space-between;
font-size: 0.75rem;
color: #ccc;
background: rgba(0, 0, 0, 0.2);
padding: 4px 8px;
border-radius: 4px;
}
.zl-card-actions {
display: flex;
justify-content: flex-end;
gap: 4px;
margin-top: 4px;
}
/* Console output items */
.console-line {
margin-bottom: 4px;
display: flex;
gap: 8px;
}
.console-time {
color: var(--muted);
}
.console-type {
font-weight: bold;
}
.console-type.tx {
color: var(--acid);
}
.console-type.rx {
color: #00aaff;
}
.console-type.info {
color: #ccc;
}
.console-type.error {
color: #ff3333;
}
.console-type.success {
color: #00ffa0;
}
.console-target {
color: #aaa;
}
.console-content pre {
margin: 0;
white-space: pre-wrap;
font-family: inherit;
}
.zl-log-line {
display: flex;
gap: 8px;
margin-bottom: 4px;
}
/* Mobile Optimization */
@media (max-width: 900px) {
.zombieland-container.page-with-sidebar {
height: auto;
flex-direction: column;
}
.zombieland-container .zl-sidebar {
width: 100%;
max-height: none;
flex-shrink: 0;
border-radius: 8px;
}
.zl-stats-grid {
grid-template-columns: repeat(2, 1fr);
}
.zl-toolbar {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px;
}
.zl-main-grid {
grid-template-columns: 1fr;
gap: 12px;
}
.zl-console-panel {
height: 350px;
flex: none;
}
.zl-agents-panel {
height: 350px;
flex: none;
}
.zl-console-input-row {
flex-wrap: wrap;
}
.zl-target-select,
.zl-cmd-input {
width: 100%;
box-sizing: border-box;
}
.zl-card-header {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.zl-card-info {
flex-direction: column;
gap: 2px;
}
}

View File

@@ -980,7 +980,7 @@ body.console-docked .app-container {
.cfg-host {
display: grid;
gap: 10px;
max-height: 56vh;
max-height: min(56vh, calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 140px));
overflow: auto;
padding-right: 4px;
}
@@ -1137,16 +1137,59 @@ body.console-docked .app-container {
width: 100%;
}
/* Config sub-tab navigation */
.cfg-subtabs {
display: flex;
gap: 4px;
padding: 4px;
margin-bottom: 10px;
border-radius: 12px;
background: color-mix(in oklab, var(--c-panel-2) 60%, transparent);
border: 1px solid var(--c-border);
overflow-x: auto;
flex-wrap: wrap;
}
.cfg-subtab {
flex: 1 1 auto;
padding: 7px 10px;
border: 1px solid transparent;
border-radius: 8px;
background: transparent;
color: var(--muted);
font-size: 12px;
font-weight: 700;
cursor: pointer;
white-space: nowrap;
transition: background .15s, color .15s, border-color .15s;
}
.cfg-subtab:hover {
color: var(--ink);
background: color-mix(in oklab, var(--c-panel) 50%, transparent);
}
.cfg-subtab.active {
color: var(--acid);
background: var(--c-panel);
border-color: var(--c-border-strong);
box-shadow: var(--shadow);
}
/* Inline switch (modal lists) */
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 26px;
min-width: 46px;
flex-shrink: 0;
background: var(--switch-track);
border: 1px solid var(--c-border-hi);
border-radius: 99px;
cursor: pointer;
box-shadow: inset 0 0 0 1px var(--glow-mid);
vertical-align: middle;
}
.switch::after {
@@ -1170,6 +1213,11 @@ body.console-docked .app-container {
transform: translateX(20px);
}
/* Suppress ::after thumb when .slider span is used (settings config toggles) */
.switch:has(.slider)::after {
display: none;
}
/* Sheet (WiFi/BT dialogs) */
.sheet-backdrop {
position: fixed;
@@ -1178,7 +1226,7 @@ body.console-docked .app-container {
display: none;
align-items: center;
justify-content: center;
z-index: 75;
z-index: 95;
}
.sheet-backdrop.show {
@@ -1285,6 +1333,20 @@ body.console-docked .app-container {
min-width: 50px;
max-width: 100%;
}
.cfg-subtabs {
flex-wrap: nowrap;
overflow-x: auto;
gap: 2px;
padding: 3px;
-webkit-overflow-scrolling: touch;
}
.cfg-subtab {
flex: 0 0 auto;
padding: 6px 8px;
font-size: 11px;
}
}
/* ---- Liveview dropdown (character hover) ---- */
@@ -1602,6 +1664,20 @@ input[type="color"].theme-input {
margin-bottom: 8px;
}
.theme-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-top: 12px;
padding-top: 12px;
border-top: 1px dashed var(--c-border);
}
.theme-actions .btn {
flex: 1 1 auto;
min-width: 80px;
}
/* ---- Toast notifications ---- */
.toast-container {
position: fixed;

493
web/css/zombieland.css Normal file
View File

@@ -0,0 +1,493 @@
/* ==========================================================================
ZOMBIELAND (C2 Module) CSS
========================================================================== */
/* Main layout constraints */
.zombieland-container.page-with-sidebar {
height: calc(100vh - var(--h-topbar, 56px) - var(--h-bottombar, 56px) - 24px);
display: flex;
min-height: 0;
}
.zl-sidebar.page-sidebar {
width: 260px;
flex-shrink: 0;
display: flex;
flex-direction: column;
background: var(--grad-card);
border: 1px solid var(--c-border);
border-radius: 12px;
overflow-y: auto;
}
.zl-main.page-main {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 12px;
}
/* Sidebar structure */
.zl-stats-grid {
display: grid;
grid-template-columns: 1fr;
gap: 8px;
margin-bottom: 15px;
}
.stat-item {
background: rgba(0, 0, 0, 0.2);
border: 1px solid var(--c-border);
padding: 10px;
border-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
}
.stat-item .stat-value {
font-weight: bold;
color: var(--acid);
font-size: 1.1rem;
}
.stat-item .stat-label {
font-size: 0.8rem;
color: var(--muted);
text-transform: uppercase;
}
.zl-toolbar {
display: flex;
flex-direction: column;
gap: 6px;
}
/* Modals */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: var(--grad-card);
border: 1px solid var(--c-border);
padding: 20px;
border-radius: 12px;
width: 100%;
max-width: 400px;
}
/* Main Grid Layout */
.zl-main-grid {
display: grid;
grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
gap: 12px;
flex: 1;
/* Takes available space except logs */
min-height: 0;
}
.zl-console-panel,
.zl-agents-panel,
.zl-logs-panel {
border: 1px solid var(--c-border);
border-radius: 12px;
background: var(--c-panel);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Log Panel underneath */
.zl-logs-panel {
height: 150px;
flex-shrink: 0;
}
.zl-panel-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: rgba(0, 0, 0, 0.2);
border-bottom: 1px solid var(--c-border);
flex-shrink: 0;
}
.zl-panel-title {
font-weight: bold;
font-size: 0.9rem;
color: var(--acid);
}
.zl-quickbar {
display: flex;
gap: 4px;
}
.quick-cmd {
background: transparent;
border: 1px solid var(--c-border-strong);
color: var(--muted);
font-size: 0.7rem;
padding: 2px 6px;
border-radius: 4px;
cursor: pointer;
transition: 0.2s;
}
.quick-cmd:hover {
color: var(--acid);
border-color: var(--acid);
}
.zl-console-output,
.zl-logs-output {
flex: 1;
overflow-y: auto;
padding: 10px;
font-family: 'Fira Code', monospace;
font-size: 0.8rem;
background: #020406;
min-height: 0;
}
/* Controls */
.zl-console-input-row {
display: flex;
gap: 8px;
padding: 8px;
border-top: 1px solid var(--c-border);
background: var(--c-panel-2);
flex-shrink: 0;
}
.zl-target-select,
.zl-cmd-input,
.zl-search-input {
background: #000;
color: #fff;
border: 1px solid var(--c-border-strong);
padding: 6px 10px;
border-radius: 6px;
}
.zl-cmd-input {
flex: 1;
}
.zl-toolbar-left {
display: flex;
position: relative;
flex: 1;
max-width: 200px;
margin-left: 10px;
}
.zl-search-input {
width: 100%;
border-radius: 6px;
padding: 4px 20px 4px 8px;
font-size: 0.8rem;
}
.zl-search-clear {
position: absolute;
right: 5px;
top: 5px;
color: var(--muted);
background: none;
border: none;
cursor: pointer;
}
.zl-agents-list {
flex: 1;
overflow-y: auto;
padding: 10px;
display: flex;
flex-direction: column;
gap: 8px;
}
/* Agent Card Styles */
.zl-agent-card {
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--c-border);
border-radius: 8px;
padding: 8px 12px;
display: flex;
flex-direction: column;
gap: 6px;
transition: 0.2s ease-out;
}
.zl-agent-card.selected {
border-color: var(--acid);
background: rgba(0, 255, 160, 0.05);
}
.zl-agent-card:hover {
border-color: var(--c-border-hi);
}
.zl-card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.zl-card-identity {
display: flex;
flex-direction: column;
gap: 2px;
line-height: 1;
}
.zl-card-hostname {
font-weight: bold;
color: #fff;
font-size: 0.9rem;
}
.zl-card-id {
font-size: 0.7rem;
color: var(--muted);
}
.zl-pill {
padding: 2px 8px;
border-radius: 12px;
font-size: 0.7rem;
font-weight: bold;
background: #222;
}
.zl-pill.online {
color: #00ffa0;
background: rgba(0, 255, 160, 0.1);
}
.zl-pill.idle {
color: #ffcc00;
background: rgba(255, 204, 0, 0.1);
}
.zl-pill.offline {
color: #ff3333;
background: rgba(255, 51, 51, 0.1);
}
/* ECG Animation */
.zl-ecg-row {
display: flex;
align-items: center;
gap: 8px;
}
.ecg {
width: 100%;
height: 24px;
max-width: 140px;
position: relative;
overflow: hidden;
background: rgba(0, 0, 0, 0.5);
border-radius: 6px;
border: 1px solid #111;
}
.ecg-wrapper {
display: flex;
width: 300%;
animation: ecg-slide linear infinite;
}
.ecg svg {
width: 33.33%;
height: 100%;
}
.ecg path {
fill: none;
stroke-width: 1.5;
stroke-linecap: round;
stroke-linejoin: round;
}
.ecg.green path {
stroke: #00ffa0;
filter: drop-shadow(0 0 2px #00ffa0);
}
.ecg.yellow path {
stroke: #ffcc00;
filter: drop-shadow(0 0 2px #ffcc00);
}
.ecg.orange path {
stroke: #ff8800;
filter: drop-shadow(0 0 2px #ff8800);
}
.ecg.red path {
stroke: #ff3333;
}
.ecg.flat .ecg-wrapper {
animation: none;
}
@keyframes ecg-slide {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-33.33%);
}
}
.zl-ecg-counter {
font-size: 0.7rem;
color: var(--muted);
font-family: monospace;
}
.zl-card-info {
display: flex;
justify-content: space-between;
font-size: 0.75rem;
color: #ccc;
background: rgba(0, 0, 0, 0.2);
padding: 4px 8px;
border-radius: 4px;
}
.zl-card-actions {
display: flex;
justify-content: flex-end;
gap: 4px;
margin-top: 4px;
}
/* Console output items */
.console-line {
margin-bottom: 4px;
display: flex;
gap: 8px;
}
.console-time {
color: var(--muted);
}
.console-type {
font-weight: bold;
}
.console-type.tx {
color: var(--acid);
}
.console-type.rx {
color: #00aaff;
}
.console-type.info {
color: #ccc;
}
.console-type.error {
color: #ff3333;
}
.console-type.success {
color: #00ffa0;
}
.console-target {
color: #aaa;
}
.console-content pre {
margin: 0;
white-space: pre-wrap;
font-family: inherit;
}
.zl-log-line {
display: flex;
gap: 8px;
margin-bottom: 4px;
}
/* Mobile Optimization */
@media (max-width: 900px) {
.zombieland-container.page-with-sidebar {
height: auto;
flex-direction: column;
}
.zombieland-container .zl-sidebar {
width: 100%;
max-height: none;
flex-shrink: 0;
border-radius: 8px;
}
.zl-stats-grid {
grid-template-columns: repeat(2, 1fr);
}
.zl-toolbar {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px;
}
.zl-main-grid {
grid-template-columns: 1fr;
gap: 12px;
}
.zl-console-panel {
height: 350px;
flex: none;
}
.zl-agents-panel {
height: 350px;
flex: none;
}
.zl-console-input-row {
flex-wrap: wrap;
}
.zl-target-select,
.zl-cmd-input {
width: 100%;
box-sizing: border-box;
}
.zl-card-header {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.zl-card-info {
flex-direction: column;
gap: 2px;
}
}