2026-03-14 21:08:57 -04:00
<!DOCTYPE html>
<!--
2026-03-25 18:47:26 -04:00
LOTUSGUILD TERMINAL DESIGN SYSTEM v2.0 — base.html
2026-03-14 21:08:57 -04:00
Reference template showing every component and layout pattern.
This file is a STATIC DEMO. Framework - specific wiring is in:
php/ → PHP / Tinker Tickets
python/ → Flask / Jinja2 / GANDALF
node/ → Express / EJS / PULSE
To build a new app, copy one of the framework skeletons from those
sub - directories and reference base.css + base.js.
-->
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
2026-03-25 18:47:26 -04:00
< meta name = "viewport" content = "width=device-width, initial-scale=1.0, viewport-fit=cover" >
< meta name = "theme-color" content = "#030508" >
< meta name = "mobile-web-app-capable" content = "yes" >
< meta name = "apple-mobile-web-app-capable" content = "yes" >
< meta name = "apple-mobile-web-app-status-bar-style" content = "black-translucent" >
2026-03-14 21:08:57 -04:00
< title > MY APP — LotusGuild< / title >
< meta name = "description" content = "LotusGuild infrastructure application" >
<!-- =========================================================
Security headers are set server - side. CSP nonce is injected
by SecurityHeadersMiddleware (PHP) / helmet (Node) / Flask.
All <script> tags need: nonce="NONCE_PLACEHOLDER"
========================================================= -->
2026-03-25 18:47:26 -04:00
<!-- Monospace font: JetBrains Mono -->
< link rel = "preconnect" href = "https://fonts.googleapis.com" >
< link rel = "preconnect" href = "https://fonts.gstatic.com" crossorigin >
< link href = "https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,600;0,700;1,400&display=swap" rel = "stylesheet" >
<!-- Hacker template design system CSS -->
2026-03-14 21:08:57 -04:00
< link rel = "stylesheet" href = "/web_template/base.css" >
<!--
App - specific CSS (override or extend after base.css):
<link rel="stylesheet" href="/assets/css/app.css">
-->
< link rel = "icon" href = "/assets/images/favicon.png" type = "image/png" >
< / head >
< body >
<!-- ===========================================================
BOOT SEQUENCE OVERLAY
Displays once per session. Remove if not desired.
data - app - name → used in the boot text banner.
=========================================================== -->
< div id = "lt-boot" class = "lt-boot-overlay" data-app-name = "MY APP" style = "display:none" >
< pre id = "lt-boot-text" class = "lt-boot-text" > < / pre >
< / div >
<!-- ===========================================================
HEADER
=========================================================== -->
2026-03-25 18:47:26 -04:00
<!-- ===========================================================
MOBILE NAV DRAWER (hidden on desktop, slides in on mobile)
=========================================================== -->
< div id = "lt-nav-drawer" class = "lt-nav-drawer" aria-hidden = "true" role = "dialog" aria-label = "Navigation menu" >
< div class = "lt-nav-drawer-header" >
< span class = "lt-brand-title" > MY APP< / span >
< button class = "lt-nav-drawer-close" id = "lt-nav-drawer-close" aria-label = "Close menu" > ✕< / button >
< / div >
< nav class = "lt-nav-drawer-links" aria-label = "Mobile navigation" >
< a href = "/" class = "lt-nav-drawer-link active" aria-current = "page" > Dashboard< / a >
< a href = "/tickets" class = "lt-nav-drawer-link" > Tickets< / a >
< a href = "/workers" class = "lt-nav-drawer-link" > Workers< / a >
< div class = "lt-nav-drawer-section" > Admin< / div >
< a href = "/admin/templates" class = "lt-nav-drawer-link lt-nav-drawer-link--indent" > Templates< / a >
< a href = "/admin/workflow" class = "lt-nav-drawer-link lt-nav-drawer-link--indent" > Workflow< / a >
< a href = "/admin/audit-log" class = "lt-nav-drawer-link lt-nav-drawer-link--indent" > Audit Log< / a >
< a href = "/admin/api-keys" class = "lt-nav-drawer-link lt-nav-drawer-link--indent" > API Keys< / a >
< / nav >
< / div >
< div id = "lt-nav-overlay" class = "lt-nav-drawer-overlay" > < / div >
2026-03-14 21:08:57 -04:00
< header class = "lt-header" >
< div class = "lt-header-left" >
2026-03-25 18:47:26 -04:00
<!-- Hamburger — visible only on tablet/mobile (CSS hides on desktop) -->
< button class = "lt-menu-btn" id = "lt-menu-btn" aria-label = "Open navigation menu" aria-expanded = "false" aria-controls = "lt-nav-drawer" >
< span > < / span > < span > < / span > < span > < / span >
< / button >
2026-03-14 21:08:57 -04:00
<!-- Brand -->
< div class = "lt-brand" >
2026-03-25 18:47:26 -04:00
< span class = "lt-brand-title lt-glitch" data-text = "MY APP" > MY APP< / span >
2026-03-14 21:08:57 -04:00
< span class = "lt-brand-subtitle" > LotusGuild Infrastructure< / span >
< / div >
2026-03-25 18:47:26 -04:00
<!-- Horizontal nav links (desktop) -->
2026-03-14 21:08:57 -04:00
< nav class = "lt-nav" aria-label = "Main navigation" >
< a href = "/" class = "lt-nav-link active" > Dashboard< / a >
< a href = "/tickets" class = "lt-nav-link" > Tickets< / a >
< a href = "/workers" class = "lt-nav-link" > Workers< / a >
<!-- Dropdown example (admin menu) -->
< div class = "lt-nav-dropdown" >
< a href = "#" class = "lt-nav-link" > Admin ▾< / a >
< ul class = "lt-nav-dropdown-menu" >
< li > < a href = "/admin/templates" > Templates< / a > < / li >
< li > < a href = "/admin/workflow" > Workflow< / a > < / li >
< li > < a href = "/admin/audit-log" > Audit Log< / a > < / li >
< li > < a href = "/admin/api-keys" > API Keys< / a > < / li >
< / ul >
< / div >
< / nav >
< / div >
< div class = "lt-header-right" >
2026-03-25 22:29:55 -04:00
<!-- WebSocket status indicator -->
< div class = "lt-ws-status" id = "lt-ws-indicator" data-state = "disconnected" aria-label = "WebSocket status" >
< span class = "lt-dot" > < / span > < span > Offline< / span >
< / div >
<!-- Theme toggle -->
< button class = "lt-theme-btn" id = "lt-theme-btn" aria-label = "Switch to light mode" title = "Switch to light mode" > ☀< / button >
<!-- Notifications with badge -->
< span class = "lt-notif-wrap" id = "lt-notif-bell" aria-label = "Notifications" >
< button class = "lt-btn lt-btn-sm" style = "padding:0 0.5rem;min-height:32px;" aria-label = "Open notifications" > 🔔< / button >
< / span >
2026-03-14 21:08:57 -04:00
<!-- Current user -->
< span class = "lt-header-user" > operator< / span >
<!-- Admin badge (only show for admins) -->
< span class = "lt-badge-admin" > admin< / span >
< / div >
< / header >
2026-03-25 22:29:55 -04:00
<!-- Right - side detail drawer -->
< div id = "lt-detail-drawer" class = "lt-drawer-right" aria-hidden = "true" role = "dialog" aria-label = "Detail panel" data-overlay = "lt-detail-overlay" >
< div class = "lt-drawer-right-header" >
< span class = "lt-drawer-right-title" > // TICKET DETAIL< / span >
< button class = "lt-drawer-right-close" data-drawer-close aria-label = "Close detail panel" > ✕< / button >
< / div >
< div class = "lt-drawer-right-body" >
< div class = "lt-kv-grid" >
< div class = "lt-kv-row" > < span class = "lt-kv-label" > ID< / span > < span class = "lt-kv-value lt-text-cyan" > #123456789< / span > < / div >
< div class = "lt-kv-row" > < span class = "lt-kv-label" > Status< / span > < span class = "lt-kv-value" > < span class = "lt-badge lt-badge-open" > Open< / span > < / span > < / div >
< div class = "lt-kv-row" > < span class = "lt-kv-label" > Priority< / span > < span class = "lt-kv-value" > < span class = "lt-badge lt-badge-p1" > P1 Critical< / span > < / span > < / div >
< div class = "lt-kv-row" > < span class = "lt-kv-label" > Assignee< / span > < span class = "lt-kv-value" style = "display:flex;align-items:center;gap:0.5rem" > < span class = "lt-avatar lt-avatar--sm lt-avatar--orange" > JD< / span > jdoe< / span > < / div >
< div class = "lt-kv-row" > < span class = "lt-kv-label" > Created< / span > < span class = "lt-kv-value" > 2026-03-10< / span > < / div >
< / div >
< div class = "lt-divider-label" style = "margin:1rem 0" > Description< / div >
< p style = "font-size:0.78rem;color:var(--text-secondary);line-height:1.6" > Storage array link-down on compute-storage-01. Affects prod write path. Investigate RAID controller firmware.< / p >
< div class = "lt-divider-label" style = "margin:1rem 0" > Activity< / div >
< div class = "lt-timeline" >
< div class = "lt-timeline-item lt-timeline-item--orange" >
< div class = "lt-timeline-meta" > < span class = "lt-timeline-actor" > jdoe< / span > assigned ticket< span class = "lt-timeline-time" > 2h ago< / span > < / div >
< div class = "lt-timeline-body" > Assigned to self, escalated to P1.< / div >
< / div >
< div class = "lt-timeline-item" >
< div class = "lt-timeline-meta" > < span class = "lt-timeline-actor" > sysbot< / span > auto-created< span class = "lt-timeline-time" > 3h ago< / span > < / div >
< div class = "lt-timeline-body" > Alert triggered: < code > node_network_up = 0< / code > < / div >
< / div >
< div class = "lt-timeline-item lt-timeline-item--dim" >
< div class = "lt-timeline-meta" > < span class = "lt-timeline-actor" > monitor< / span > detected< span class = "lt-timeline-time" > 3h ago< / span > < / div >
< div class = "lt-timeline-body" > NIC link-down detected on large1:enp35s0.< / div >
< / div >
< / div >
< / div >
< div class = "lt-drawer-right-footer" >
< button class = "lt-btn lt-btn-sm" onclick = "lt.rightDrawer.close('lt-detail-drawer')" > Close< / button >
< button class = "lt-btn lt-btn-primary lt-btn-sm" > Save Changes< / button >
< / div >
< / div >
< div id = "lt-detail-overlay" class = "lt-drawer-right-overlay" > < / div >
2026-03-14 21:08:57 -04:00
<!-- ===========================================================
MAIN CONTENT AREA
=========================================================== -->
< main class = "lt-main lt-container" >
<!-- Page title bar -->
< div class = "lt-page-header" >
< h1 class = "lt-page-title" > Dashboard< / h1 >
< div class = "lt-btn-group" >
< a href = "/create" class = "lt-btn lt-btn-primary" > New Ticket< / a >
< button class = "lt-btn lt-btn-sm" data-modal-open = "export-modal" > Export< / button >
< / div >
< / div >
<!-- ==========================================================
STATS WIDGETS
data - filter - key / data - filter - val → wired by lt.statsFilter
========================================================== -->
< div class = "lt-stats-grid" >
< div class = "lt-stat-card active" data-filter-key = "status" data-filter-val = "Open" >
< span class = "lt-stat-icon" > 📋< / span >
< div class = "lt-stat-info" >
< span class = "lt-stat-value" > 42< / span >
< span class = "lt-stat-label" > Open< / span >
< / div >
< / div >
< div class = "lt-stat-card" data-filter-key = "priority" data-filter-val = "1" >
< span class = "lt-stat-icon" > 🔴< / span >
< div class = "lt-stat-info" >
< span class = "lt-stat-value" > 3< / span >
< span class = "lt-stat-label" > Critical< / span >
< / div >
< / div >
< div class = "lt-stat-card" data-filter-key = "assigned_to" data-filter-val = "0" >
< span class = "lt-stat-icon" > 👤< / span >
< div class = "lt-stat-info" >
< span class = "lt-stat-value" > 11< / span >
< span class = "lt-stat-label" > Unassigned< / span >
< / div >
< / div >
< div class = "lt-stat-card" data-filter-key = "created" data-filter-val = "today" >
< span class = "lt-stat-icon" > 📅< / span >
< div class = "lt-stat-info" >
< span class = "lt-stat-value" > 7< / span >
< span class = "lt-stat-label" > Today< / span >
< / div >
< / div >
< / div >
<!-- ==========================================================
TAB NAVIGATION
========================================================== -->
< div class = "lt-tabs" >
< button class = "lt-tab active" data-tab = "tab-table" > Table View< / button >
< button class = "lt-tab" data-tab = "tab-kanban" > Kanban< / button >
< button class = "lt-tab" data-tab = "tab-workers" > Workers< / button >
< / div >
<!-- ==========================================================
SIDEBAR + CONTENT LAYOUT
========================================================== -->
< div class = "lt-layout" >
<!-- Sidebar filter panel -->
< aside class = "lt-sidebar" id = "lt-sidebar" >
< div class = "lt-sidebar-header" >
Filters
< button class = "lt-sidebar-toggle" data-sidebar-toggle = "lt-sidebar"
aria-label = "Collapse filters" > ◀< / button >
< / div >
< div class = "lt-sidebar-body" >
< div class = "lt-filter-group" >
< span class = "lt-filter-label" > Status< / span >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" checked > Open
< / label >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > Pending
< / label >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > In Progress
< / label >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > Closed
< / label >
< / div >
< div class = "lt-filter-group" >
< span class = "lt-filter-label" > Priority< / span >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > P1 Critical
< / label >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > P2 High
< / label >
< label class = "lt-filter-option" >
< input type = "checkbox" class = "lt-checkbox" > P3 Medium
< / label >
< / div >
< div class = "lt-filter-group" >
< span class = "lt-filter-label" > Assigned To< / span >
< div class = "lt-form-group" >
< select class = "lt-select lt-btn-sm" >
< option value = "" > All users< / option >
< option > operator< / option >
< option > admin< / option >
< / select >
< / div >
< / div >
< div class = "lt-btn-group" >
< button class = "lt-btn lt-btn-sm" > Apply< / button >
< button class = "lt-btn lt-btn-sm lt-btn-ghost" > Reset< / button >
< / div >
< / div >
< / aside >
<!-- Main content -->
< div class = "lt-content" >
<!-- Toolbar: search + actions -->
< div class = "lt-toolbar" >
< div class = "lt-toolbar-left" >
< div class = "lt-search" >
< input type = "search" class = "lt-input lt-search-input"
placeholder = "Search tickets..." aria-label = "Search" >
< / div >
< button class = "lt-btn lt-btn-sm" > Advanced ▾< / button >
< / div >
< div class = "lt-toolbar-right" >
< span class = "lt-text-muted lt-text-xs" > 42 results< / span >
< button class = "lt-btn lt-btn-sm lt-btn-ghost" > Bulk Actions< / button >
< / div >
< / div >
<!-- ==================================================
TAB PANEL: TABLE VIEW
================================================== -->
< div id = "tab-table" class = "lt-tab-panel active" >
<!-- Outer ASCII frame wrapping the table -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚< / span >
< span class = "lt-frame-br" > ╝< / span >
< div class = "lt-section-header" > Ticket Queue< / div >
< div class = "lt-table-wrap" >
2026-03-25 18:47:26 -04:00
< table class = "lt-table lt-table-responsive" id = "ticket-table" >
2026-03-14 21:08:57 -04:00
< thead >
< tr >
< th > < input type = "checkbox" class = "lt-checkbox" aria-label = "Select all" > < / th >
< th data-sort-key = "id" > ID< / th >
< th data-sort-key = "priority" > Priority< / th >
< th data-sort-key = "title" > Title< / th >
< th data-sort-key = "status" > Status< / th >
< th data-sort-key = "assignee" > Assignee< / th >
< th data-sort-key = "created" > Created< / th >
< th > Actions< / th >
< / tr >
< / thead >
< tbody >
<!-- P1 Critical row -->
< tr class = "lt-row-p1 lt-row-critical" >
2026-03-25 18:47:26 -04:00
< td data-label = "Select" > < input type = "checkbox" class = "lt-checkbox" > < / td >
< td data-label = "ID" > < a href = "/ticket/123456789" > #123456789< / a > < / td >
< td data-label = "Priority" > < span class = "lt-p1" > P1 Critical< / span > < / td >
< td data-label = "Title" > Storage array link-down on compute-storage-01< / td >
< td data-label = "Status" > < span class = "lt-status lt-status-open" > Open< / span > < / td >
< td data-label = "Assignee" class = "lt-text-muted" > Unassigned< / td >
< td data-label = "Created" class = "lt-text-xs lt-text-muted" > 5m ago< / td >
< td data-label = "Actions" >
2026-03-14 21:08:57 -04:00
< div class = "lt-btn-group" >
< a href = "/ticket/123456789" class = "lt-btn lt-btn-sm" > View< / a >
< button class = "lt-btn lt-btn-sm lt-btn-danger" > Close< / button >
< / div >
< / td >
< / tr >
<!-- P2 High row -->
< tr class = "lt-row-p2 lt-row-warning" >
2026-03-25 18:47:26 -04:00
< td data-label = "Select" > < input type = "checkbox" class = "lt-checkbox" > < / td >
< td data-label = "ID" > < a href = "/ticket/987654321" > #987654321< / a > < / td >
< td data-label = "Priority" > < span class = "lt-p2" > P2 High< / span > < / td >
< td data-label = "Title" > Switch port flapping on USW-Pro-24< / td >
< td data-label = "Status" > < span class = "lt-status lt-status-in-progress" > In Progress< / span > < / td >
< td data-label = "Assignee" > operator< / td >
< td data-label = "Created" class = "lt-text-xs lt-text-muted" > 2h ago< / td >
< td data-label = "Actions" >
2026-03-14 21:08:57 -04:00
< div class = "lt-btn-group" >
< a href = "/ticket/987654321" class = "lt-btn lt-btn-sm" > View< / a >
< / div >
< / td >
< / tr >
<!-- P3 Medium row -->
< tr class = "lt-row-p3" >
2026-03-25 18:47:26 -04:00
< td data-label = "Select" > < input type = "checkbox" class = "lt-checkbox" > < / td >
< td data-label = "ID" > < a href = "/ticket/111222333" > #111222333< / a > < / td >
< td data-label = "Priority" > < span class = "lt-p3" > P3 Med< / span > < / td >
< td data-label = "Title" > Scheduled maintenance: replace SFP+ on large1< / td >
< td data-label = "Status" > < span class = "lt-status lt-status-pending" > Pending< / span > < / td >
< td data-label = "Assignee" > admin< / td >
< td data-label = "Created" class = "lt-text-xs lt-text-muted" > 1d ago< / td >
< td data-label = "Actions" >
2026-03-14 21:08:57 -04:00
< div class = "lt-btn-group" >
< a href = "/ticket/111222333" class = "lt-btn lt-btn-sm" > View< / a >
< / div >
< / td >
< / tr >
<!-- P4 closed -->
< tr class = "lt-row-p4" >
2026-03-25 18:47:26 -04:00
< td data-label = "Select" > < input type = "checkbox" class = "lt-checkbox" > < / td >
< td data-label = "ID" > < a href = "/ticket/444555666" > #444555666< / a > < / td >
< td data-label = "Priority" > < span class = "lt-p4" > P4 Low< / span > < / td >
< td data-label = "Title" > Update SSL cert on wiki.lotusguild.org< / td >
< td data-label = "Status" > < span class = "lt-status lt-status-closed" > Closed< / span > < / td >
< td data-label = "Assignee" > operator< / td >
< td data-label = "Created" class = "lt-text-xs lt-text-muted" > 3d ago< / td >
< td data-label = "Actions" >
2026-03-14 21:08:57 -04:00
< div class = "lt-btn-group" >
< a href = "/ticket/444555666" class = "lt-btn lt-btn-sm" > View< / a >
< / div >
< / td >
< / tr >
< / tbody >
< / table >
< / div >
< / div > <!-- /.lt - frame -->
< / div > <!-- /#tab - table -->
<!-- ==================================================
TAB PANEL: KANBAN
================================================== -->
< div id = "tab-kanban" class = "lt-tab-panel" >
< div class = "lt-grid-4" >
<!-- Kanban column: Open -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚< / span >
< span class = "lt-frame-br" > ╝< / span >
< div class = "lt-section-header" > Open< / div >
< div class = "lt-section-body" >
< div class = "lt-card lt-mb-md lt-row-p1" >
< div class = "lt-flex lt-flex-between lt-mb-md" >
< span class = "lt-p1" > P1< / span >
< span class = "lt-dot lt-dot-up" > < / span >
< / div >
< div class = "lt-text-sm" > Storage array link-down< / div >
< div class = "lt-text-xs lt-text-muted lt-mt-sm" > 5m ago · Unassigned< / div >
< / div >
< div class = "lt-card lt-row-p3" >
< div class = "lt-flex lt-flex-between lt-mb-md" >
< span class = "lt-p3" > P3< / span >
< span class = "lt-dot lt-dot-up" > < / span >
< / div >
< div class = "lt-text-sm" > Update node_exporter on micro1< / div >
< div class = "lt-text-xs lt-text-muted lt-mt-sm" > 1d ago · operator< / div >
< / div >
< / div >
< / div >
<!-- Kanban column: Pending -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚< / span >
< span class = "lt-frame-br" > ╝< / span >
< div class = "lt-section-header" > Pending< / div >
< div class = "lt-section-body" >
< div class = "lt-card lt-row-p2" >
< div class = "lt-flex lt-flex-between lt-mb-md" >
< span class = "lt-p2" > P2< / span >
< span class = "lt-dot lt-dot-warn" > < / span >
< / div >
< div class = "lt-text-sm" > Scheduled maintenance window< / div >
< div class = "lt-text-xs lt-text-muted lt-mt-sm" > 2d ago · admin< / div >
< / div >
< / div >
< / div >
<!-- Kanban column: In Progress -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚< / span >
< span class = "lt-frame-br" > ╝< / span >
< div class = "lt-section-header" > In Progress< / div >
< div class = "lt-section-body" >
< div class = "lt-card lt-row-p2 lt-item-running" >
< div class = "lt-flex lt-flex-between lt-mb-md" >
< span class = "lt-p2" > P2< / span >
< span class = "lt-dot lt-dot-warn" > < / span >
< / div >
< div class = "lt-text-sm" > Switch port flapping on USW-Pro< / div >
< div class = "lt-text-xs lt-text-muted lt-mt-sm" > 2h ago · operator< / div >
< / div >
< / div >
< / div >
<!-- Kanban column: Closed -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚< / span >
< span class = "lt-frame-br" > ╝< / span >
< div class = "lt-section-header" > Closed< / div >
< div class = "lt-section-body" >
< div class = "lt-card lt-row-p4" style = "opacity:0.6" >
< div class = "lt-flex lt-flex-between lt-mb-md" >
< span class = "lt-p4" > P4< / span >
< span class = "lt-dot lt-dot-idle" > < / span >
< / div >
< div class = "lt-text-sm" > Update SSL cert on wiki< / div >
< div class = "lt-text-xs lt-text-muted lt-mt-sm" > 3d ago · operator< / div >
< / div >
< / div >
< / div >
< / div > <!-- /.lt - grid - 4 -->
< / div > <!-- /#tab - kanban -->
<!-- ==================================================
TAB PANEL: WORKERS
================================================== -->
< div id = "tab-workers" class = "lt-tab-panel" >
< div class = "lt-grid-3" >
< div class = "lt-card" >
< div class = "lt-flex lt-flex-between lt-mb-md" >
< span class = "lt-text-amber lt-text-upper" > pulse-worker-01< / span >
< span class = "lt-status lt-status-online" > Online< / span >
< / div >
< div class = "lt-data-table-wrapper" >
< table class = "lt-data-table" >
< tr > < td class = "lt-text-muted" > CPU< / td > < td > 12%< / td > < / tr >
< tr > < td class = "lt-text-muted" > Memory< / td > < td > 2.1 GB / 8 GB< / td > < / tr >
< tr > < td class = "lt-text-muted" > Load< / td > < td > 0.42 / 0.51 / 0.48< / td > < / tr >
< tr > < td class = "lt-text-muted" > Uptime< / td > < td > 14d 6h< / td > < / tr >
< tr > < td class = "lt-text-muted" > Tasks< / td > < td > 2 / 5< / td > < / tr >
< / table >
< / div >
< / div >
< div class = "lt-card" >
< div class = "lt-flex lt-flex-between lt-mb-md" >
< span class = "lt-text-amber lt-text-upper" > pulse-worker-02< / span >
< span class = "lt-status lt-status-offline" > Offline< / span >
< / div >
< div class = "lt-msg lt-msg-warning" > Last seen 14m ago< / div >
< / div >
< div class = "lt-card" >
< div class = "lt-empty" > No more workers registered.< / div >
< / div >
< / div >
< / div > <!-- /#tab - workers -->
< / div > <!-- /.lt - content -->
< / div > <!-- /.lt - layout -->
<!-- ==========================================================
COMPONENT SHOWCASE (remove in production)
========================================================== -->
< div class = "lt-divider" > < / div >
<!-- Inner frame + subsection example -->
< div class = "lt-frame" >
< span class = "lt-frame-bl" > ╚< / span >
< span class = "lt-frame-br" > ╝< / span >
< div class = "lt-section-header" > Component Reference< / div >
< div class = "lt-frame-inner" >
< div class = "lt-subsection-header" > Buttons< / div >
< div class = "lt-section-body" >
< div class = "lt-btn-group" >
< button class = "lt-btn" > Default< / button >
< button class = "lt-btn lt-btn-primary" > Primary< / button >
< button class = "lt-btn lt-btn-danger" > Danger< / button >
< button class = "lt-btn lt-btn-sm" > Small< / button >
< button class = "lt-btn lt-btn-ghost" > Ghost< / button >
< button class = "lt-btn" disabled > Disabled< / button >
< / div >
< / div >
< div class = "lt-subsection-header" > Status Badges< / div >
< div class = "lt-section-body lt-flex lt-flex-wrap lt-gap-md" >
< span class = "lt-status lt-status-open" > Open< / span >
< span class = "lt-status lt-status-pending" > Pending< / span >
< span class = "lt-status lt-status-in-progress" > In Progress< / span >
< span class = "lt-status lt-status-closed" > Closed< / span >
< span class = "lt-status lt-status-online" > Online< / span >
< span class = "lt-status lt-status-offline" > Offline< / span >
< span class = "lt-status lt-status-running" > Running< / span >
< span class = "lt-status lt-status-completed" > Completed< / span >
< span class = "lt-status lt-status-failed" > Failed< / span >
< / div >
< div class = "lt-subsection-header" > Priority Badges< / div >
< div class = "lt-section-body lt-flex lt-flex-wrap lt-gap-md" >
< span class = "lt-priority lt-p1" > P1 Critical< / span >
< span class = "lt-priority lt-p2" > P2 High< / span >
< span class = "lt-priority lt-p3" > P3 Med< / span >
< span class = "lt-priority lt-p4" > P4 Low< / span >
< span class = "lt-priority lt-p5" > P5 Min< / span >
< / div >
< div class = "lt-subsection-header" > Chips & Badges< / div >
< div class = "lt-section-body lt-flex lt-flex-wrap lt-gap-sm" >
< span class = "lt-chip lt-chip-ok" > OK< / span >
< span class = "lt-chip lt-chip-warn" > Warning< / span >
< span class = "lt-chip lt-chip-critical" > Critical< / span >
< span class = "lt-chip lt-chip-info" > Info< / span >
< span class = "lt-badge lt-badge-green" > v1.0< / span >
< span class = "lt-badge lt-badge-amber" > Beta< / span >
< span class = "lt-badge lt-badge-red" > Deprecated< / span >
< span class = "lt-badge-admin" > admin< / span >
< / div >
< div class = "lt-subsection-header" > Status Dots< / div >
< div class = "lt-section-body lt-flex lt-gap-md" >
< span class = "lt-flex lt-gap-sm" > < span class = "lt-dot lt-dot-up" > < / span > Up< / span >
< span class = "lt-flex lt-gap-sm" > < span class = "lt-dot lt-dot-down" > < / span > Down< / span >
< span class = "lt-flex lt-gap-sm" > < span class = "lt-dot lt-dot-warn" > < / span > Degraded< / span >
< span class = "lt-flex lt-gap-sm" > < span class = "lt-dot lt-dot-idle" > < / span > Idle< / span >
< / div >
< div class = "lt-subsection-header" > Inline Messages< / div >
< div class = "lt-section-body" >
< div class = "lt-msg lt-msg-error" > Database connection refused on 10.10.10.50< / div >
< div class = "lt-msg lt-msg-success" > Ticket #123456789 updated successfully< / div >
< div class = "lt-msg lt-msg-warning" > Rate limit: 80% consumed (40/50 req/min)< / div >
< div class = "lt-msg lt-msg-info" > Auto-refresh active — updates every 30s< / div >
< / div >
< div class = "lt-subsection-header" > Forms< / div >
< div class = "lt-section-body" >
< div class = "lt-grid-2" >
< div >
< div class = "lt-form-group" >
< label class = "lt-label lt-label-required" for = "eg-title" > Ticket Title< / label >
< input id = "eg-title" type = "text" class = "lt-input" placeholder = "Describe the issue" >
< / div >
< div class = "lt-form-group" >
< label class = "lt-label" for = "eg-priority" > Priority< / label >
< select id = "eg-priority" class = "lt-select" >
< option > P1 — Critical< / option >
< option > P2 — High< / option >
< option selected > P3 — Medium< / option >
< option > P4 — Low< / option >
< option > P5 — Minimal< / option >
< / select >
< / div >
< div class = "lt-form-group" >
< label class = "lt-label" for = "eg-desc" > Description< / label >
< textarea id = "eg-desc" class = "lt-textarea" placeholder = "Markdown supported..." > < / textarea >
< span class = "lt-form-hint" > Markdown supported. Use #123456789 to link tickets.< / span >
< / div >
< div class = "lt-form-group" >
< label class = "lt-label" >
< input type = "checkbox" class = "lt-checkbox" > Confidential ticket
< / label >
< / div >
< div class = "lt-btn-group" >
< button class = "lt-btn lt-btn-primary" > Submit< / button >
< button class = "lt-btn lt-btn-ghost" > Cancel< / button >
< / div >
< / div >
< div >
< div class = "lt-search lt-form-group" >
< label class = "lt-label" for = "eg-search" > Search< / label >
< input id = "eg-search" type = "search" class = "lt-input lt-search-input"
placeholder = "Ctrl+K to focus" >
< / div >
< div class = "lt-form-group" >
< label class = "lt-label" > Loading state (skeleton)< / label >
< div class = "lt-skeleton lt-p-md" style = "height:40px" > < / div >
< / div >
< div class = "lt-form-group" >
< label class = "lt-label" > Empty state< / label >
< div class = "lt-empty" style = "padding:1rem" > No results found< / div >
< / div >
< div class = "lt-form-group" >
< label class = "lt-label" > Loading indicator< / label >
< div class = "lt-loading" style = "padding:1rem" > < / div >
< / div >
< / div >
< / div >
< / div >
< div class = "lt-subsection-header" > Log / Timeline Entries< / div >
< div class = "lt-section-body" >
< div class = "lt-log-entry success" >
< div class = "lt-log-ts" > 2026-03-14 09:12:33 EDT< / div >
< strong > Status changed:< / strong > Open → In Progress by operator
< / div >
< div class = "lt-log-entry warning" >
< div class = "lt-log-ts" > 2026-03-14 09:10:11 EDT< / div >
< strong > Priority escalated:< / strong > P3 → P1 by GANDALF auto-alert
< / div >
< div class = "lt-log-entry error" >
< div class = "lt-log-ts" > 2026-03-14 09:09:55 EDT< / div >
< strong > Alert triggered:< / strong > NIC link-down on large1:enp35s0
< div class = "lt-log-output" > node_exporter metric: node_network_up{interface="enp35s0"} = 0< / div >
< / div >
< / div >
< / div > <!-- /.lt - frame - inner -->
< / div > <!-- /.lt - frame (component showcase) -->
2026-03-25 18:47:26 -04:00
<!-- ===========================================================
COMPONENT SHOWCASE — v1.2 ADDITIONS
=========================================================== -->
< div class = "lt-frame lt-mt-lg" >
< div class = "lt-frame-inner" >
<!-- PROGRESS BARS -->
< div class = "lt-section-header" > Progress Bars< / div >
< div class = "lt-section-body" >
< div style = "display:flex;flex-direction:column;gap:var(--space-md)" >
< div >
< div class = "lt-progress-label" > < span > CPU LOAD< / span > < span > 72%< / span > < / div >
< div class = "lt-progress" > < div class = "lt-progress-bar" style = "width:72%" data-width = "72%" > < / div > < / div >
< / div >
< div >
< div class = "lt-progress-label" > < span > MEMORY< / span > < span > 45%< / span > < / div >
< div class = "lt-progress lt-progress--cyan" > < div class = "lt-progress-bar" style = "width:45%" data-width = "45%" > < / div > < / div >
< / div >
< div >
< div class = "lt-progress-label" > < span > DISK I/O< / span > < span > 89%< / span > < / div >
< div class = "lt-progress lt-progress--red lt-progress--lg" > < div class = "lt-progress-bar" style = "width:89%" data-width = "89%" > < / div > < / div >
< / div >
< div >
< div class = "lt-progress-label" > < span > UPTIME< / span > < span > 100%< / span > < / div >
< div class = "lt-progress lt-progress--green lt-progress--striped" > < div class = "lt-progress-bar" style = "width:100%" data-width = "100%" > < / div > < / div >
< / div >
< / div >
< / div >
<!-- BREADCRUMBS & PAGINATION -->
< div class = "lt-section-header" > Breadcrumbs / Pagination< / div >
< div class = "lt-section-body" style = "display:flex;flex-direction:column;gap:var(--space-md)" >
< nav class = "lt-breadcrumb" aria-label = "breadcrumb" >
< div class = "lt-breadcrumb-item" > < a href = "#" > ROOT< / a > < / div >
< div class = "lt-breadcrumb-sep" > < / div >
< div class = "lt-breadcrumb-item" > < a href = "#" > SYSTEMS< / a > < / div >
< div class = "lt-breadcrumb-sep" > < / div >
< div class = "lt-breadcrumb-item" > < a href = "#" > NETWORK< / a > < / div >
< div class = "lt-breadcrumb-sep" > < / div >
< div class = "lt-breadcrumb-item active" > NODE-07< / div >
< / nav >
< nav class = "lt-pagination" aria-label = "pagination" >
< button class = "lt-page-btn" disabled > « < / button >
< button class = "lt-page-btn" disabled > 1< / button >
< button class = "lt-page-btn active" > 2< / button >
< button class = "lt-page-btn" > 3< / button >
< button class = "lt-page-btn" > 4< / button >
< button class = "lt-page-btn" > 5< / button >
< button class = "lt-page-btn" > » < / button >
< / nav >
< / div >
<!-- ACCORDION -->
< div class = "lt-section-header" > Accordion< / div >
< div class = "lt-section-body" >
< div >
< div class = "lt-accordion" >
< button class = "lt-accordion-header" aria-expanded = "false" data-accordion >
SYSTEM OVERVIEW
< svg class = "lt-accordion-icon" viewBox = "0 0 10 6" fill = "none" stroke = "currentColor" stroke-width = "1.5" > < path d = "M1 1l4 4 4-4" / > < / svg >
< / button >
< div class = "lt-accordion-body" > < div class = "lt-accordion-content" > Node running at 72% CPU. 12 active processes. Last restart: 3d ago.< / div > < / div >
< / div >
< div class = "lt-accordion" >
< button class = "lt-accordion-header" aria-expanded = "false" data-accordion >
NETWORK CONFIG
< svg class = "lt-accordion-icon" viewBox = "0 0 10 6" fill = "none" stroke = "currentColor" stroke-width = "1.5" > < path d = "M1 1l4 4 4-4" / > < / svg >
< / button >
< div class = "lt-accordion-body" > < div class = "lt-accordion-content" > eth0: 10.0.0.7 — MTU 1500 — RX 4.2 GB — TX 1.1 GB< / div > < / div >
< / div >
< div class = "lt-accordion" >
< button class = "lt-accordion-header" aria-expanded = "false" data-accordion >
FIREWALL RULES
< svg class = "lt-accordion-icon" viewBox = "0 0 10 6" fill = "none" stroke = "currentColor" stroke-width = "1.5" > < path d = "M1 1l4 4 4-4" / > < / svg >
< / button >
< div class = "lt-accordion-body" > < div class = "lt-accordion-content" > 22/tcp ALLOW — 80/tcp ALLOW — 443/tcp ALLOW — */* DENY< / div > < / div >
< / div >
< / div >
< / div >
<!-- ALERTS -->
< div class = "lt-section-header" > Alert Banners< / div >
< div class = "lt-section-body" style = "display:flex;flex-direction:column;gap:var(--space-sm)" >
< div class = "lt-alert" >
< span class = "lt-alert-icon" > ℹ < / span >
< div class = "lt-alert-body" > < div class = "lt-alert-title" > Information< / div > < div class = "lt-alert-msg" > Scheduled maintenance window: Sunday 02:00– 04:00 UTC.< / div > < / div >
< button class = "lt-alert-close" data-alert-close aria-label = "Dismiss" > ✕< / button >
< / div >
< div class = "lt-alert lt-alert--warning" >
< span class = "lt-alert-icon" > ⚠< / span >
< div class = "lt-alert-body" > < div class = "lt-alert-title" > Warning< / div > < div class = "lt-alert-msg" > Disk usage on NODE-03 at 87%. Consider cleanup.< / div > < / div >
< button class = "lt-alert-close" data-alert-close aria-label = "Dismiss" > ✕< / button >
< / div >
< div class = "lt-alert lt-alert--error" >
< span class = "lt-alert-icon" > ✕< / span >
< div class = "lt-alert-body" > < div class = "lt-alert-title" > Critical< / div > < div class = "lt-alert-msg" > NODE-11 unreachable. Last seen 14m ago.< / div > < / div >
< button class = "lt-alert-close" data-alert-close aria-label = "Dismiss" > ✕< / button >
< / div >
< div class = "lt-alert lt-alert--success" >
< span class = "lt-alert-icon" > ✓< / span >
< div class = "lt-alert-body" > < div class = "lt-alert-title" > Success< / div > < div class = "lt-alert-msg" > Deployment v2.4.1 completed successfully.< / div > < / div >
< button class = "lt-alert-close" data-alert-close aria-label = "Dismiss" > ✕< / button >
< / div >
< / div >
<!-- TOGGLES, RANGE, TAGS -->
< div class = "lt-section-header" > Toggles / Range / Tags< / div >
< div class = "lt-section-body" style = "display:flex;flex-direction:column;gap:var(--space-lg)" >
< div style = "display:flex;gap:var(--space-lg);flex-wrap:wrap" >
< label class = "lt-toggle" >
< input type = "checkbox" checked >
< div class = "lt-toggle-track" > < div class = "lt-toggle-thumb" > < / div > < / div >
< span class = "lt-toggle-label" > Auto-refresh< / span >
< / label >
< label class = "lt-toggle" >
< input type = "checkbox" >
< div class = "lt-toggle-track" > < div class = "lt-toggle-thumb" > < / div > < / div >
< span class = "lt-toggle-label" > Dark alerts< / span >
< / label >
< label class = "lt-toggle" >
< input type = "checkbox" checked >
< div class = "lt-toggle-track" > < div class = "lt-toggle-thumb" > < / div > < / div >
< span class = "lt-toggle-label" > Notifications< / span >
< / label >
< / div >
< div class = "lt-range-wrap" style = "max-width:320px" >
< div class = "lt-range-header" >
< span class = "lt-range-label" > Refresh Interval (s)< / span >
< span class = "lt-range-value" > 30< / span >
< / div >
< input type = "range" class = "lt-range" min = "5" max = "60" value = "30" >
< / div >
< div class = "lt-tags" >
< span class = "lt-tag lt-tag--orange" > CRITICAL< / span >
< span class = "lt-tag lt-tag--cyan" > NETWORK< / span >
< span class = "lt-tag lt-tag--green" > ONLINE< / span >
< span class = "lt-tag lt-tag--red" > OFFLINE< / span >
< span class = "lt-tag lt-tag--purple" > ADMIN< / span >
< span class = "lt-tag" > UNTAGGED< / span >
< / div >
< / div >
<!-- CODE BLOCK -->
< div class = "lt-section-header" > Code Block< / div >
< div class = "lt-section-body" >
< div class = "lt-code-block" >
< div class = "lt-code-header" >
< span class = "lt-code-lang" > bash< / span >
< button class = "lt-code-copy" data-copy = "systemctl restart nginx && systemctl status nginx" > COPY< / button >
< / div >
< pre > < code > < span class = "tok-cmt" > # Restart and verify nginx< / span >
< span class = "tok-kw" > systemctl< / span > restart nginx < span class = "tok-kw" > & & < / span > < span class = "tok-kw" > systemctl< / span > status nginx
< span class = "tok-cmt" > # Check active connections< / span >
< span class = "tok-kw" > ss< / span > -tlnp | < span class = "tok-kw" > grep< / span > < span class = "tok-str" > ':80\|:443'< / span > < / code > < / pre >
< / div >
< / div >
<!-- KEY - VALUE + STEPPER -->
< div class = "lt-section-header" > Data Grid / Stepper< / div >
< div class = "lt-section-body" style = "display:grid;grid-template-columns:1fr 1fr;gap:var(--space-lg)" >
< div >
< div class = "lt-kv-grid" >
< div class = "lt-kv-key" > Hostname< / div > < div class = "lt-kv-val lt-kv-val--cyan" > node-07.prod< / div >
< div class = "lt-kv-key" > IP Address< / div > < div class = "lt-kv-val" > 10.0.0.7< / div >
< div class = "lt-kv-key" > Status< / div > < div class = "lt-kv-val lt-kv-val--green" > ONLINE< / div >
< div class = "lt-kv-key" > Uptime< / div > < div class = "lt-kv-val" > 3d 14h 22m< / div >
< div class = "lt-kv-key" > Load Avg< / div > < div class = "lt-kv-val lt-kv-val--orange" > 2.14 / 1.87 / 1.60< / div >
< div class = "lt-kv-key" > Region< / div > < div class = "lt-kv-val" > US-EAST-1< / div >
< / div >
< / div >
< div >
< div class = "lt-stepper" >
< div class = "lt-step complete" >
< div class = "lt-step-num" > ✓< / div >
< div class = "lt-step-label" > INIT< / div >
< / div >
< div class = "lt-step complete" >
< div class = "lt-step-num" > ✓< / div >
< div class = "lt-step-label" > BUILD< / div >
< / div >
< div class = "lt-step active" >
< div class = "lt-step-num" > 3< / div >
< div class = "lt-step-label" > DEPLOY< / div >
< / div >
< div class = "lt-step" >
< div class = "lt-step-num" > 4< / div >
< div class = "lt-step-label" > VERIFY< / div >
< / div >
< / div >
< / div >
< / div >
<!-- LIST GROUP + BADGES -->
< div class = "lt-section-header" > List Group / Badges< / div >
< div class = "lt-section-body" style = "display:grid;grid-template-columns:1fr 1fr;gap:var(--space-lg)" >
< div class = "lt-list-group" >
< div class = "lt-list-item lt-list-item--active" >
< span class = "lt-dot lt-dot--green" > < / span >
< a href = "#" > node-01.prod< / a >
< span class = "lt-list-item-meta lt-kv-val--green" > ONLINE< / span >
< / div >
< div class = "lt-list-item" >
< span class = "lt-dot lt-dot--green" > < / span >
< a href = "#" > node-02.prod< / a >
< span class = "lt-list-item-meta lt-kv-val--green" > ONLINE< / span >
< / div >
< div class = "lt-list-item" >
< span class = "lt-dot lt-dot--orange" > < / span >
< a href = "#" > node-03.prod< / a >
< span class = "lt-list-item-meta lt-kv-val--orange" > DEGRADED< / span >
< / div >
< div class = "lt-list-item" >
< span class = "lt-dot lt-dot--red" > < / span >
< a href = "#" > node-11.prod< / a >
< span class = "lt-list-item-meta lt-kv-val--red" > OFFLINE< / span >
< / div >
< / div >
< div style = "display:flex;flex-direction:column;gap:var(--space-md)" >
< div style = "display:flex;gap:var(--space-md);align-items:center;flex-wrap:wrap" >
< div class = "lt-badge-wrap" >
< button class = "lt-btn lt-btn-sm" > Alerts< / button >
< span class = "lt-badge" > 3< / span >
< / div >
< div class = "lt-badge-wrap" >
< button class = "lt-btn lt-btn-sm" > Messages< / button >
< span class = "lt-badge lt-badge--orange" > 12< / span >
< / div >
< div class = "lt-badge-wrap" >
< button class = "lt-btn lt-btn-sm" > Updates< / button >
< span class = "lt-badge lt-badge--cyan" > 5< / span >
< / div >
< / div >
< div style = "display:flex;gap:var(--space-sm);align-items:center;flex-wrap:wrap" >
< div class = "lt-bar-loader" > < span > < / span > < span > < / span > < span > < / span > < span > < / span > < / div >
< div class = "lt-spinner" > < / div >
< div class = "lt-spinner lt-spinner--cyan lt-spinner--sm" > < / div >
< div class = "lt-pulse-dot" > < / div >
< / div >
< / div >
< / div >
<!-- TAB BAR -->
< div class = "lt-section-header" > Tab Bar< / div >
< div class = "lt-section-body" >
< div class = "lt-tab-bar" role = "tablist" >
< button class = "lt-tab active" role = "tab" data-tab-target = "tab-overview" aria-selected = "true" > Overview< / button >
< button class = "lt-tab" role = "tab" data-tab-target = "tab-logs" aria-selected = "false" > Logs< / button >
< button class = "lt-tab" role = "tab" data-tab-target = "tab-metrics" aria-selected = "false" > Metrics< / button >
< button class = "lt-tab" role = "tab" data-tab-target = "tab-config" aria-selected = "false" > Config< / button >
< / div >
< div class = "lt-tab-panels" style = "padding:var(--space-md) 0" >
< div id = "tab-overview" class = "lt-tab-panel active" > System overview: all nodes nominal. 14 active sessions.< / div >
< div id = "tab-logs" class = "lt-tab-panel" >
< pre style = "font-family:var(--font-mono);font-size:0.75rem;color:var(--text-secondary);margin:0" > [03:41:22] nginx: 200 GET /api/nodes (12ms)
[03:41:23] cron: heartbeat OK
[03:41:24] alert: disk 87% on node-03< / pre >
< / div >
< div id = "tab-metrics" class = "lt-tab-panel" > CPU avg: 34% — Mem avg: 61% — Net TX: 4.2 Mbps< / div >
< div id = "tab-config" class = "lt-tab-panel" > Config last updated: 2026-03-20 by admin@lotusguild.io< / div >
< / div >
< / div >
<!-- TOOLTIP & DIVIDER DEMO -->
< div class = "lt-section-header" > Tooltips / Divider< / div >
< div class = "lt-section-body" style = "display:flex;flex-direction:column;gap:var(--space-md)" >
< div style = "display:flex;gap:var(--space-lg);flex-wrap:wrap;padding-top:var(--space-md)" >
< span data-tooltip = "Primary accent — use for actions" class = "lt-tag lt-tag--orange" > ORANGE< / span >
< span data-tooltip = "Info / border color" data-tooltip-pos = "bottom" class = "lt-tag lt-tag--cyan" > CYAN (bottom)< / span >
< span data-tooltip = "Success states" class = "lt-tag lt-tag--green" > GREEN< / span >
< span data-tooltip = "Critical / error" class = "lt-tag lt-tag--red" > RED< / span >
< / div >
< div class = "lt-divider" > < span class = "lt-divider-label" > SECTION BREAK< / span > < / div >
< div class = "lt-divider lt-divider--orange" > < span class = "lt-divider-label" > CRITICAL ZONE< / span > < / div >
< / div >
<!-- DROPZONE -->
< div class = "lt-section-header" > File Dropzone< / div >
< div class = "lt-section-body" >
< div class = "lt-dropzone" data-dropzone data-accept = ".json,.csv,.log" data-max-size = "10485760" >
< input type = "file" accept = ".json,.csv,.log" multiple >
< div class = "lt-dropzone-icon" > ⤓< / div >
< div class = "lt-dropzone-text" > Drop files here or < strong > click to browse< / strong > < / div >
< div class = "lt-dropzone-hint" > Accepts .json, .csv, .log — max 10 MB each< / div >
< / div >
< div class = "lt-file-list" id = "demo-file-list" > < / div >
< / div >
<!-- COMMAND PALETTE TRIGGER -->
< div class = "lt-section-header" > Command Palette< / div >
< div class = "lt-section-body" >
< p style = "font-family:var(--font-mono);font-size:0.8rem;color:var(--text-secondary);margin:0 0 var(--space-sm)" >
Press < kbd style = "background:var(--bg-tertiary);border:1px solid var(--border-dim);padding:2px 6px;font-family:var(--font-mono)" > Ctrl+K< / kbd > or click the button below to open the command palette.
< / p >
< button class = "lt-btn lt-btn-ghost" onclick = "lt.cmdPalette.open()" > ⌘ Open Command Palette< / button >
< / div >
< / div > <!-- /.lt - frame - inner (v1.2 additions) -->
< / div > <!-- /.lt - frame (v1.2 additions) -->
2026-03-25 22:29:55 -04:00
<!-- ═══════════════════════════════════════════════════════════
v1.1 COMPONENT SHOWCASE
═══════════════════════════════════════════════════════════ -->
<!-- Theme Toggle -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // THEME TOGGLE< / span >
< / div >
< div class = "lt-section-body" >
< p style = "font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem" > Dark/light mode with OS preference detection and localStorage persistence.< / p >
< div class = "lt-flex lt-gap-sm lt-align-center lt-wrap" >
< button class = "lt-btn lt-btn-primary" onclick = "lt.theme.toggle()" > Toggle Theme< / button >
< button class = "lt-btn" onclick = "lt.theme.set('dark')" > Force Dark< / button >
< button class = "lt-btn" onclick = "lt.theme.set('light')" > Force Light< / button >
< code style = "font-size:0.72rem;color:var(--accent-cyan)" > lt.theme.toggle() | .set('light') | .get()< / code >
< / div >
< / div >
< / div >
<!-- Skeleton Loaders -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // SKELETON LOADERS< / span >
< / div >
< div class = "lt-section-body" >
< div class = "lt-grid lt-grid-3" style = "gap:1rem;align-items:start" >
< div class = "lt-skeleton-card" >
< div class = "lt-skeleton-card-header" >
< span class = "lt-skeleton lt-skeleton-avatar" > < / span >
< div style = "flex:1" > < span class = "lt-skeleton lt-skeleton-title" > < / span > < span class = "lt-skeleton lt-skeleton-line-sm" > < / span > < / div >
< / div >
< span class = "lt-skeleton lt-skeleton-text" > < / span >
< span class = "lt-skeleton lt-skeleton-line-lg" > < / span >
< span class = "lt-skeleton lt-skeleton-text" style = "width:70%" > < / span >
< div class = "lt-flex lt-gap-sm" style = "margin-top:0.25rem" >
< span class = "lt-skeleton lt-skeleton-btn" > < / span >
< span class = "lt-skeleton lt-skeleton-badge" > < / span >
< / div >
< / div >
< div style = "grid-column:span 2" >
< div class = "lt-skeleton-row" > < span class = "lt-skeleton" style = "height:1rem" > < / span > < span class = "lt-skeleton lt-skeleton-text" > < / span > < span class = "lt-skeleton" style = "height:0.8rem;width:80%" > < / span > < span class = "lt-skeleton lt-skeleton-badge" > < / span > < span class = "lt-skeleton" style = "height:0.8rem;width:60%" > < / span > < span class = "lt-skeleton lt-skeleton-btn" > < / span > < / div >
< div class = "lt-skeleton-row" style = "opacity:0.65" > < span class = "lt-skeleton" style = "height:1rem" > < / span > < span class = "lt-skeleton lt-skeleton-text" > < / span > < span class = "lt-skeleton" style = "height:0.8rem;width:65%" > < / span > < span class = "lt-skeleton lt-skeleton-badge" > < / span > < span class = "lt-skeleton" style = "height:0.8rem;width:50%" > < / span > < span class = "lt-skeleton lt-skeleton-btn" > < / span > < / div >
< div class = "lt-skeleton-row" style = "opacity:0.35" > < span class = "lt-skeleton" style = "height:1rem" > < / span > < span class = "lt-skeleton lt-skeleton-text" > < / span > < span class = "lt-skeleton" style = "height:0.8rem;width:55%" > < / span > < span class = "lt-skeleton lt-skeleton-badge" > < / span > < span class = "lt-skeleton" style = "height:0.8rem;width:40%" > < / span > < span class = "lt-skeleton lt-skeleton-btn" > < / span > < / div >
< / div >
< / div >
< / div >
< / div >
<!-- Empty States -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // EMPTY STATES< / span >
< / div >
< div class = "lt-section-body" >
< div class = "lt-grid lt-grid-3" style = "gap:1rem" >
< div class = "lt-card" > < div class = "lt-empty-state" > < div class = "lt-empty-state-icon" > 📭< / div > < div class = "lt-empty-state-title" > No Tickets Found< / div > < div class = "lt-empty-state-body" > No tickets match your current filters.< / div > < button class = "lt-btn lt-btn-sm lt-btn-primary" > Clear Filters< / button > < / div > < / div >
< div class = "lt-card" > < div class = "lt-empty-state" > < div class = "lt-empty-state-icon" > 🔌< / div > < div class = "lt-empty-state-title" > No Workers Online< / div > < div class = "lt-empty-state-body" > All workers are offline or unreachable.< / div > < button class = "lt-btn lt-btn-sm" onclick = "lt.toast.warning('Checking workers…')" > Retry< / button > < / div > < / div >
< div class = "lt-card" > < div class = "lt-empty-state lt-empty-state--sm" > < div class = "lt-empty-state-icon" > 🗂< / div > < div class = "lt-empty-state-title" > No Results< / div > < div class = "lt-empty-state-body" > Try a different search term.< / div > < / div > < / div >
< / div >
< / div >
< / div >
<!-- Avatars & Notification Badges -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // AVATARS & NOTIFICATION BADGES< / span >
< / div >
< div class = "lt-section-body" >
< div class = "lt-flex lt-gap-lg lt-wrap lt-align-center" style = "margin-bottom:1rem" >
< span class = "lt-avatar lt-avatar--xs lt-avatar--orange" > AB< / span >
< span class = "lt-avatar lt-avatar--sm lt-avatar--green" > CD< / span >
< span class = "lt-avatar lt-avatar--purple" > EF< / span >
< span class = "lt-avatar lt-avatar--lg lt-avatar--red" > GH< / span >
< div class = "lt-avatar-wrap" > < span class = "lt-avatar lt-avatar--orange" > JD< / span > < span class = "lt-avatar-status lt-avatar-status--online" > < / span > < / div >
< div class = "lt-avatar-wrap" > < span class = "lt-avatar" > SK< / span > < span class = "lt-avatar-status lt-avatar-status--away" > < / span > < / div >
< div class = "lt-avatar-wrap" > < span class = "lt-avatar lt-avatar--red" > MR< / span > < span class = "lt-avatar-status lt-avatar-status--busy" > < / span > < / div >
< div class = "lt-avatar-group" >
< span class = "lt-avatar lt-avatar--sm lt-avatar--orange" > AA< / span >
< span class = "lt-avatar lt-avatar--sm lt-avatar--green" > BB< / span >
< span class = "lt-avatar lt-avatar--sm lt-avatar--purple" > CC< / span >
< span class = "lt-avatar lt-avatar--sm" style = "background:var(--bg-tertiary);color:var(--text-muted);font-size:0.6rem" > +4< / span >
< / div >
< / div >
< div class = "lt-flex lt-gap-md lt-align-center lt-wrap" >
< span class = "lt-notif-wrap" id = "demo-notif-btn" > < button class = "lt-btn lt-btn-sm" > 🔔 Alerts< / button > < / span >
< button class = "lt-btn lt-btn-sm" onclick = "lt.notif.inc('#demo-notif-btn')" > +1 Badge< / button >
< button class = "lt-btn lt-btn-sm" onclick = "lt.notif.clear('#demo-notif-btn')" > Clear< / button >
< button class = "lt-btn lt-btn-sm" onclick = "lt.notif.inc('#lt-notif-bell')" > +1 Header Bell< / button >
< / div >
< / div >
< / div >
<!-- Timeline -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // TIMELINE / ACTIVITY FEED< / span >
< / div >
< div class = "lt-section-body" >
< div class = "lt-grid lt-grid-2" style = "gap:1.5rem" >
< div class = "lt-timeline" >
< div class = "lt-timeline-item lt-timeline-item--red" >
< div class = "lt-timeline-meta" > < span class = "lt-timeline-actor" > alertmanager< / span > fired< span class = "lt-timeline-time lt-num" > 14:22:05< / span > < / div >
< div class = "lt-timeline-body" > CRITICAL: Storage array link-down on < code > compute-storage-01< / code > . Ticket auto-created.< / div >
< / div >
< div class = "lt-timeline-item lt-timeline-item--orange" >
< div class = "lt-timeline-meta" > < span class = "lt-timeline-actor" > jdoe< / span > assigned< span class = "lt-timeline-time lt-num" > 14:24:11< / span > < / div >
< div class = "lt-timeline-body" > Escalated to P1 Critical. Paged on-call team.< / div >
< / div >
< div class = "lt-timeline-item" >
< div class = "lt-timeline-meta" > < span class = "lt-timeline-actor" > jdoe< / span > commented< span class = "lt-timeline-time lt-num" > 14:31:44< / span > < / div >
< div class = "lt-timeline-body" > Confirmed NIC failure. Ordered replacement hardware. ETA 2h.< / div >
< / div >
< div class = "lt-timeline-item lt-timeline-item--green" >
< div class = "lt-timeline-meta" > < span class = "lt-timeline-actor" > jdoe< / span > resolved< span class = "lt-timeline-time lt-num" > 16:55:00< / span > < / div >
< div class = "lt-timeline-body" > Hardware replaced. Link restored. Monitoring for 30 min.< / div >
< / div >
< / div >
< div style = "display:flex;flex-direction:column;gap:0.75rem" >
< div class = "lt-stat-card" > < div class = "lt-stat-label" > Resolution Time< / div > < div class = "lt-stat-value lt-text-green lt-num" > 2h 33m< / div > < / div >
< div class = "lt-stat-card" > < div class = "lt-stat-label" > Events< / div > < div class = "lt-stat-value lt-num" > 4< / div > < / div >
< div class = "lt-stat-card" > < div class = "lt-stat-label" > SLA Status< / div > < div class = "lt-stat-value lt-text-orange" > Within SLA< / div > < / div >
< / div >
< / div >
< / div >
< / div >
<!-- Right Drawer -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // RIGHT-SIDE DRAWER< / span >
< / div >
< div class = "lt-section-body" >
< p style = "font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem" > Detail/inspect panel from the right. Focus trap, ESC close, overlay backdrop, return-focus.< / p >
< div class = "lt-flex lt-gap-sm lt-wrap" >
< button class = "lt-btn lt-btn-primary" data-drawer-open = "lt-detail-drawer" > Open Detail Panel< / button >
< code style = "font-size:0.72rem;color:var(--accent-cyan)" > lt.rightDrawer.open('id') | .close() | .toggle()< / code >
< / div >
< / div >
< / div >
<!-- Context Menu -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // CONTEXT MENU< / span >
< / div >
< div class = "lt-section-body" >
< p style = "font-size:0.78rem;color:var(--text-secondary);margin-bottom:1rem" > Right-click any element with < code > data-context-menu< / code > or trigger programmatically.< / p >
< div class = "lt-flex lt-gap-sm lt-wrap lt-align-center" >
< div data-context-menu = "demo-ctx" class = "lt-card" style = "padding:0.75rem 1.25rem;cursor:context-menu;border-style:dashed;display:inline-block" >
Right-click this card ›
< / div >
< button class = "lt-btn lt-btn-sm" id = "demo-ctx-btn" > Show Context Menu< / button >
< / div >
< / div >
< / div >
<!-- Combobox + Typeahead -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // COMBOBOX & TYPEAHEAD< / span >
< / div >
< div class = "lt-section-body" >
< div class = "lt-grid lt-grid-2" style = "gap:1.5rem" >
< div >
< label class = "lt-label" > Assign Workers (multi-select)< / label >
< div class = "lt-combobox" id = "demo-combobox" >
< div class = "lt-combobox-input-wrap" >
< input type = "text" class = "lt-combobox-input" id = "demo-combobox-input" placeholder = "Search workers…" autocomplete = "off" >
< / div >
< div class = "lt-combobox-dropdown" > < / div >
< / div >
< / div >
< div >
< label class = "lt-label" > Search Tickets (typeahead)< / label >
< div class = "lt-typeahead" id = "demo-typeahead" >
< input type = "text" class = "lt-input lt-w-full" id = "demo-typeahead-input" placeholder = "Type to search…" autocomplete = "off" >
< div class = "lt-typeahead-dropdown" > < / div >
< / div >
< / div >
< / div >
< / div >
< / div >
<!-- Sticky Table -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // STICKY TABLE HEADERS< / span >
< / div >
< div class = "lt-section-body" >
< div class = "lt-table-sticky-wrap" >
< table class = "lt-table lt-table-responsive" >
< thead > < tr > < th > ID< / th > < th > Priority< / th > < th > Title< / th > < th > Status< / th > < th > Assignee< / th > < / tr > < / thead >
< tbody >
< tr > < td data-label = "ID" > < a href = "#" class = "lt-text-cyan" > #001< / a > < / td > < td data-label = "Priority" > < span class = "lt-badge lt-badge-p1" > P1< / span > < / td > < td data-label = "Title" > Link-down on compute-storage-01< / td > < td data-label = "Status" > < span class = "lt-badge lt-badge-open" > Open< / span > < / td > < td data-label = "Assignee" > jdoe< / td > < / tr >
< tr > < td data-label = "ID" > < a href = "#" class = "lt-text-cyan" > #002< / a > < / td > < td data-label = "Priority" > < span class = "lt-badge lt-badge-p2" > P2< / span > < / td > < td data-label = "Title" > Switch port flapping USW-Pro-24< / td > < td data-label = "Status" > < span class = "lt-badge lt-badge-in-progress" > In Progress< / span > < / td > < td data-label = "Assignee" > smith< / td > < / tr >
< tr > < td data-label = "ID" > < a href = "#" class = "lt-text-cyan" > #003< / a > < / td > < td data-label = "Priority" > < span class = "lt-badge lt-badge-p3" > P3< / span > < / td > < td data-label = "Title" > Scheduled SFP+ replacement< / td > < td data-label = "Status" > < span class = "lt-badge lt-badge-pending" > Pending< / span > < / td > < td data-label = "Assignee" > ops-bot< / td > < / tr >
< tr > < td data-label = "ID" > < a href = "#" class = "lt-text-cyan" > #004< / a > < / td > < td data-label = "Priority" > < span class = "lt-badge lt-badge-p4" > P4< / span > < / td > < td data-label = "Title" > SSL cert renewal wiki< / td > < td data-label = "Status" > < span class = "lt-badge lt-badge-open" > Open< / span > < / td > < td data-label = "Assignee" > admin< / td > < / tr >
< tr > < td data-label = "ID" > < a href = "#" class = "lt-text-cyan" > #005< / a > < / td > < td data-label = "Priority" > < span class = "lt-badge lt-badge-p1" > P1< / span > < / td > < td data-label = "Title" > RAID controller firmware< / td > < td data-label = "Status" > < span class = "lt-badge lt-badge-open" > Open< / span > < / td > < td data-label = "Assignee" > jdoe< / td > < / tr >
< tr > < td data-label = "ID" > < a href = "#" class = "lt-text-cyan" > #006< / a > < / td > < td data-label = "Priority" > < span class = "lt-badge lt-badge-p2" > P2< / span > < / td > < td data-label = "Title" > Backup job failure nas-01< / td > < td data-label = "Status" > < span class = "lt-badge lt-badge-closed" > Closed< / span > < / td > < td data-label = "Assignee" > backup-bot< / td > < / tr >
< tr > < td data-label = "ID" > < a href = "#" class = "lt-text-cyan" > #007< / a > < / td > < td data-label = "Priority" > < span class = "lt-badge lt-badge-p3" > P3< / span > < / td > < td data-label = "Title" > Prometheus alert rule tuning< / td > < td data-label = "Status" > < span class = "lt-badge lt-badge-in-progress" > In Progress< / span > < / td > < td data-label = "Assignee" > ops-team< / td > < / tr >
< / tbody >
< / table >
< / div >
< / div >
< / div >
<!-- Chart Containers -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // CHART CONTAINERS< / span >
< / div >
< div class = "lt-section-body" >
< div class = "lt-grid lt-grid-2" style = "gap:1rem" >
< div class = "lt-chart-wrap" >
< div class = "lt-chart-header" >
< span class = "lt-chart-title" > Ticket Volume (7d)< / span >
< div class = "lt-chart-legend" >
< span class = "lt-chart-legend-item" > < span class = "lt-chart-legend-dot" style = "background:var(--accent-cyan)" > < / span > Open< / span >
< span class = "lt-chart-legend-item" > < span class = "lt-chart-legend-dot" style = "background:var(--accent-green)" > < / span > Closed< / span >
< / div >
< / div >
< div class = "lt-chart-body" style = "min-height:120px;display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-size:0.72rem;letter-spacing:0.1em" > [ Plug in Chart.js / D3 here ]< / div >
< div class = "lt-chart-axis" > < span > Mon< / span > < span > Tue< / span > < span > Wed< / span > < span > Thu< / span > < span > Fri< / span > < span > Sat< / span > < span > Sun< / span > < / div >
< / div >
< div class = "lt-chart-wrap is-loading" >
< div class = "lt-chart-header" > < span class = "lt-chart-title" > Worker Uptime< / span > < / div >
< div class = "lt-chart-body" > < / div >
< / div >
< / div >
< / div >
< / div >
<!-- Split Pane -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // SPLIT PANE< / span >
< / div >
< div class = "lt-section-body" >
< div class = "lt-split" id = "demo-split" style = "height:200px;border:1px solid var(--border-dim)" >
< div class = "lt-split-pane" style = "padding:1rem;overflow:auto" >
< div class = "lt-section-label" style = "margin-bottom:0.5rem" > Panel A< / div >
< p style = "font-size:0.78rem;color:var(--text-secondary)" > Drag the divider to resize. Stacks vertically on mobile.< / p >
< / div >
< div class = "lt-split-divider" title = "Drag to resize" > < / div >
< div class = "lt-split-pane" style = "padding:1rem;overflow:auto" >
< div class = "lt-section-label" style = "margin-bottom:0.5rem" > Panel B< / div >
< p style = "font-size:0.78rem;color:var(--text-secondary)" > Both panels maintain independent scrolling.< / p >
< / div >
< / div >
< p style = "font-size:0.72rem;color:var(--text-muted);margin-top:0.5rem" > < code > lt.splitPane.init(el, { initial: 0.4, minA: 120 } )< / code > < / p >
< / div >
< / div >
<!-- WebSocket & Offline -->
< div class = "lt-section" >
< div class = "lt-section-header" >
< span class = "lt-section-title" > // WEBSOCKET & OFFLINE DETECTION< / span >
< / div >
< div class = "lt-section-body" >
< div class = "lt-flex lt-gap-md lt-wrap lt-align-center" style = "margin-bottom:1rem" >
< div class = "lt-ws-status" data-state = "connected" > < span class = "lt-dot" > < / span > < span > Connected< / span > < / div >
< div class = "lt-ws-status" data-state = "connecting" > < span class = "lt-dot" > < / span > < span > Connecting…< / span > < / div >
< div class = "lt-ws-status" data-state = "disconnected" > < span class = "lt-dot" > < / span > < span > Disconnected< / span > < / div >
< / div >
< div class = "lt-flex lt-gap-sm lt-wrap" >
< button class = "lt-btn lt-btn-sm" onclick = "lt.offline.isOnline() ? lt.toast.success('Online ✓') : lt.toast.error('Offline ✗')" > Check Online Status< / button >
< button class = "lt-btn lt-btn-sm" onclick = "lt.toast.info('lt.ws.connect(url, { reconnect:true, onMessage: fn })')" > WS API Hint< / button >
< / div >
< p style = "font-size:0.72rem;color:var(--text-muted);margin-top:0.75rem" > Offline banner + body class auto-applied on < code > navigator.onLine< / code > change. WS manager has exponential backoff, event emitter, status indicator binding.< / p >
< / div >
< / div >
2026-03-14 21:08:57 -04:00
< / main > <!-- /.lt - main -->
<!-- ===========================================================
TOAST DEMO BUTTONS (remove in production)
=========================================================== -->
< div style = "position:fixed;bottom:1rem;left:1rem;display:flex;flex-direction:column;gap:0.5rem;z-index:900" >
< button class = "lt-btn lt-btn-sm" onclick = "lt.toast.success('Ticket saved successfully')" > ✓ Toast< / button >
< button class = "lt-btn lt-btn-sm lt-btn-danger" onclick = "lt.toast.error('Network error — retry in 5s', 5000)" > ✗ Error< / button >
< button class = "lt-btn lt-btn-sm" onclick = "lt.toast.warning('Rate limit 80% used')" > ! Warn< / button >
< button class = "lt-btn lt-btn-sm lt-btn-ghost" onclick = "lt.toast.info('Auto-refresh triggered')" > i Info< / button >
< / div >
<!-- ===========================================================
MODAL EXAMPLE: Export
=========================================================== -->
< div id = "export-modal" class = "lt-modal-overlay" aria-hidden = "true" >
< div class = "lt-modal" >
< div class = "lt-modal-header" >
< span class = "lt-modal-title" > Export Tickets< / span >
< button class = "lt-modal-close" data-modal-close aria-label = "Close" > ✕< / button >
< / div >
< div class = "lt-modal-body" >
< div class = "lt-form-group" >
< label class = "lt-label" for = "export-fmt" > Format< / label >
< select id = "export-fmt" class = "lt-select" >
< option > CSV< / option >
< option > JSON< / option >
< / select >
< / div >
< div class = "lt-form-group" >
< label class = "lt-label" >
< input type = "checkbox" class = "lt-checkbox" checked > Selected tickets only
< / label >
< / div >
< div class = "lt-msg lt-msg-info" > Exports include all visible columns.< / div >
< / div >
< div class = "lt-modal-footer" >
< button class = "lt-btn lt-btn-ghost" data-modal-close > Cancel< / button >
< button class = "lt-btn lt-btn-primary" onclick = "lt.toast.success('Export started'); lt.modal.close('export-modal')" > Export< / button >
< / div >
< / div >
< / div >
<!-- ===========================================================
MODAL EXAMPLE: Keyboard shortcuts help
=========================================================== -->
< div id = "lt-keys-help" class = "lt-modal-overlay" aria-hidden = "true" >
< div class = "lt-modal" >
< div class = "lt-modal-header" >
< span class = "lt-modal-title" > Keyboard Shortcuts< / span >
< button class = "lt-modal-close" data-modal-close aria-label = "Close" > ✕< / button >
< / div >
< div class = "lt-modal-body" >
< table class = "lt-data-table" style = "width:100%" >
< thead >
< tr > < th > Shortcut< / th > < th > Action< / th > < / tr >
< / thead >
< tbody >
< tr > < td > Ctrl / ⌘ + K< / td > < td > Focus search box< / td > < / tr >
< tr > < td > Ctrl / ⌘ + E< / td > < td > Toggle edit mode (ticket page)< / td > < / tr >
< tr > < td > Ctrl / ⌘ + S< / td > < td > Save changes (ticket page)< / td > < / tr >
< tr > < td > j / ↓< / td > < td > Select next row< / td > < / tr >
< tr > < td > k / ↑< / td > < td > Select previous row< / td > < / tr >
< tr > < td > Enter< / td > < td > Open selected ticket< / td > < / tr >
< tr > < td > n< / td > < td > New ticket< / td > < / tr >
< tr > < td > ?< / td > < td > Show this help< / td > < / tr >
< tr > < td > ESC< / td > < td > Close modal / cancel< / td > < / tr >
< / tbody >
< / table >
< / div >
< div class = "lt-modal-footer" >
< button class = "lt-btn" data-modal-close > Close< / button >
< / div >
< / div >
< / div >
2026-03-25 18:47:26 -04:00
<!-- ===========================================================
COMMAND PALETTE OVERLAY
=========================================================== -->
< div id = "lt-cmd-overlay" class = "lt-cmd-overlay" role = "dialog" aria-modal = "true" aria-label = "Command palette" >
< div class = "lt-cmd-palette" id = "lt-cmd-palette" >
< div class = "lt-cmd-input-wrap" >
< span class = "lt-cmd-prompt" > > < / span >
< input id = "lt-cmd-input" class = "lt-cmd-input" type = "text" placeholder = "Search commands…" autocomplete = "off" spellcheck = "false" >
< / div >
< div class = "lt-cmd-results" id = "lt-cmd-results" >
< div class = "lt-cmd-empty" > Start typing to search…< / div >
< / div >
< div class = "lt-cmd-footer" >
< span > < kbd > ↑< / kbd > < kbd > ↓< / kbd > Navigate< / span >
< span > < kbd > Enter< / kbd > Select< / span >
< span > < kbd > Esc< / kbd > Close< / span >
< / div >
< / div >
< / div >
2026-03-14 21:08:57 -04:00
<!-- ===========================================================
SCRIPTS
=========================================================== -->
<!--
PHP apps inject CSRF token + config here (with CSP nonce):
<script nonce="<?php echo $nonce; ?>">
window.CSRF_TOKEN = '<?php echo CsrfMiddleware::getToken(); ?>';
window.APP_TIMEZONE = '<?php echo $config['TIMEZONE']; ?>';
</script>
Node/Express (EJS):
<script nonce="<%= nonce %>">
window.CSRF_TOKEN = '<%= csrfToken %>';
</script>
Flask/Jinja2:
<script nonce="{{ nonce }}">
window.CSRF_TOKEN = '{{ csrf_token() }}';
</script>
-->
2026-03-25 18:47:26 -04:00
<!-- LotusGuild Terminal Design System JS v1.2 -->
2026-03-14 21:08:57 -04:00
< script src = "/web_template/base.js" > < / script >
2026-03-25 18:47:26 -04:00
<!-- Init v1.2 modules -->
< script >
document . addEventListener ( 'DOMContentLoaded' , ( ) => {
// Accordion
lt . accordion . init ( ) ;
// Tooltips (JS-positioned — optional, CSS tooltips work without this)
lt . tooltip . init ( ) ;
// Clipboard copy buttons ([data-copy])
lt . clipboard . init ( ) ;
// Alert dismiss buttons
lt . alerts . init ( ) ;
// Command palette demo commands
lt . cmdPalette . init ( [
{
group : 'Navigation' ,
items : [
{ icon : '⌂' , label : 'Dashboard' , kbd : 'G D' , action : ( ) => lt . toast . info ( '→ Dashboard' ) } ,
{ icon : '⊞' , label : 'All Tickets' , kbd : 'G T' , action : ( ) => lt . toast . info ( '→ Tickets' ) } ,
{ icon : '★' , label : 'Saved Filters' , action : ( ) => lt . toast . info ( '→ Filters' ) } ,
]
} ,
{
group : 'Actions' ,
items : [
{ icon : '+' , label : 'New Ticket' , kbd : 'N' , action : ( ) => lt . toast . success ( 'New ticket form' ) } ,
{ icon : '⤓' , label : 'Export CSV' , action : ( ) => lt . modal . open ( 'export-modal' ) } ,
{ icon : '⟳' , label : 'Refresh Data' , kbd : 'R' , action : ( ) => lt . toast . info ( 'Refreshing...' ) } ,
]
} ,
{
group : 'Help' ,
items : [
{ icon : '?' , label : 'Keyboard Shortcuts' , kbd : '?' , action : ( ) => lt . modal . open ( 'lt-keys-help' ) } ,
]
} ,
] ) ;
// Range slider live value display
document . querySelectorAll ( 'input[type="range"].lt-range' ) . forEach ( r => {
const wrap = r . closest ( '.lt-range-wrap' ) ;
const label = wrap && wrap . querySelector ( '.lt-range-value' ) ;
if ( label ) {
label . textContent = r . value ;
r . addEventListener ( 'input' , ( ) => { label . textContent = r . value ; } ) ;
}
} ) ;
// Animate the demo progress bars on load
document . querySelectorAll ( '.lt-progress-bar[data-width]' ) . forEach ( bar => {
requestAnimationFrame ( ( ) => { bar . style . width = bar . dataset . width ; } ) ;
} ) ;
2026-03-25 22:29:55 -04:00
// Theme toggle button
document . getElementById ( 'lt-theme-btn' ) . addEventListener ( 'click' , ( ) => lt . theme . toggle ( ) ) ;
// Context menu: register demo menu items
lt . contextMenu . register ( 'demo-ctx' , [
{ icon : '📋' , label : 'Copy ID' , kbd : 'C' , action : ( ) => lt . toast . info ( 'ID copied' ) } ,
{ icon : '👁' , label : 'View Details' , action : ( ) => lt . rightDrawer . open ( 'lt-detail-drawer' ) } ,
{ icon : '✏️' , label : 'Edit Ticket' , kbd : 'E' , action : ( ) => lt . toast . info ( 'Edit…' ) } ,
{ divider : true } ,
{ icon : '🗑' , label : 'Delete' , danger : true , action : ( ) => lt . toast . error ( 'Deleted' ) } ,
] ) ;
// Programmatic context menu button
const demoCtxBtn = document . getElementById ( 'demo-ctx-btn' ) ;
if ( demoCtxBtn ) demoCtxBtn . addEventListener ( 'click' , e => {
const r = demoCtxBtn . getBoundingClientRect ( ) ;
lt . contextMenu . show ( r . left , r . bottom + 4 , [
{ icon : '📋' , label : 'Copy ID' , action : ( ) => lt . toast . info ( 'Copied' ) } ,
{ icon : '✏️' , label : 'Edit' , action : ( ) => lt . toast . info ( 'Edit' ) } ,
{ divider : true } ,
{ icon : '🗑' , label : 'Delete' , danger : true , action : ( ) => lt . toast . error ( 'Deleted' ) } ,
] ) ;
} ) ;
// Combobox demo
lt . combobox . init (
document . getElementById ( 'demo-combobox-input' ) ,
[
{ value : 'worker-01' , label : 'worker-01' , icon : '🖥' } ,
{ value : 'worker-02' , label : 'worker-02' , icon : '🖥' } ,
{ value : 'worker-03' , label : 'worker-03' , icon : '🖥' } ,
{ value : 'gpu-01' , label : 'gpu-01' , icon : '⚡' } ,
{ value : 'gpu-02' , label : 'gpu-02' , icon : '⚡' } ,
{ value : 'storage-01' , label : 'storage-01' , icon : '💾' } ,
] ,
{ onChange : vals => console . log ( '[combobox]' , vals ) }
) ;
// Typeahead demo
lt . typeahead . init (
document . getElementById ( 'demo-typeahead-input' ) ,
[
{ value : '001' , label : 'Link-down on compute-storage-01' , icon : '🔴' , meta : 'P1' } ,
{ value : '002' , label : 'Switch port flapping USW-Pro-24' , icon : '🟠' , meta : 'P2' } ,
{ value : '003' , label : 'Scheduled SFP+ replacement large1' , icon : '🔵' , meta : 'P3' } ,
{ value : '004' , label : 'SSL cert renewal wiki.lotusguild.org' , icon : '🟢' , meta : 'P4' } ,
{ value : '005' , label : 'RAID controller firmware update' , icon : '🔴' , meta : 'P1' } ,
] ,
{ onSelect : item => lt . toast . info ( ` Selected: ${ item . label } ` ) }
) ;
// Split pane demo
lt . splitPane . init ( document . getElementById ( 'demo-split' ) , { initial : 0.4 , minA : 100 , minB : 100 } ) ;
// Demo notification badge initial count
lt . notif . set ( '#lt-notif-bell' , 3 ) ;
2026-03-25 18:47:26 -04:00
// Tab bar switching
document . querySelectorAll ( '.lt-tab-bar' ) . forEach ( bar => {
bar . addEventListener ( 'click' , e => {
const tab = e . target . closest ( '.lt-tab' ) ;
if ( ! tab ) return ;
const targetId = tab . dataset . tabTarget ;
if ( ! targetId ) return ;
const panels = bar . nextElementSibling ;
bar . querySelectorAll ( '.lt-tab' ) . forEach ( t => { t . classList . remove ( 'active' ) ; t . setAttribute ( 'aria-selected' , 'false' ) ; } ) ;
tab . classList . add ( 'active' ) ;
tab . setAttribute ( 'aria-selected' , 'true' ) ;
if ( panels ) {
panels . querySelectorAll ( '.lt-tab-panel' ) . forEach ( p => p . classList . remove ( 'active' ) ) ;
const target = panels . querySelector ( '#' + targetId ) ;
if ( target ) target . classList . add ( 'active' ) ;
}
} ) ;
} ) ;
} ) ;
< / script >
2026-03-14 21:08:57 -04:00
< / body >
< / html >