mirror of
https://github.com/RetroShare/RSNewWebUI.git
synced 2026-09-14 11:05:47 +05:00
The Debug page has been redesigned to match the styling and layout of the new webui, with full phone / mobile compatibility.
This commit is contained in:
parent
2bb09c4bcf
commit
4e2fb1a9fc
@ -23,6 +23,12 @@ const Debug = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const latencyClass = (ms) => {
|
||||
if (ms < 50) return 'debug-latency--fast';
|
||||
if (ms < 200) return 'debug-latency--moderate';
|
||||
return 'debug-latency--slow';
|
||||
};
|
||||
|
||||
return {
|
||||
oninit: () => {
|
||||
loadCoreVersion();
|
||||
@ -35,72 +41,208 @@ const Debug = () => {
|
||||
view: (vnode) => {
|
||||
const s = rs.apiStats;
|
||||
const version = vnode.attrs.version || '';
|
||||
const isConnected = rs.connectionState.status;
|
||||
const core = coreVersion
|
||||
? `${coreVersion.major}.${coreVersion.minor}.${coreVersion.mini}${coreVersion.extra || ''} (${coreVersion.human || ''})`
|
||||
: 'unknown';
|
||||
? `${coreVersion.major}.${coreVersion.minor}.${coreVersion.mini}${coreVersion.extra || ''}`
|
||||
: 'Unknown';
|
||||
const coreHuman = coreVersion && coreVersion.human ? coreVersion.human : '';
|
||||
|
||||
return m('.debug-page', [
|
||||
m('h2', 'Debug'),
|
||||
|
||||
m('.debug-section', [
|
||||
m('h3', 'Build'),
|
||||
m('.debug-grid', [
|
||||
m('span', 'Web UI'), m('strong', version),
|
||||
m('span', 'Core'), m('strong', core),
|
||||
m('span', 'Core round trip'), m('strong', coreVersion ? coreVersionAt + ' ms' : '-'),
|
||||
m('span', 'Page loaded'), m('strong', ago(s.startedAt)),
|
||||
m('span', 'Viewport'), m('strong', `${window.innerWidth} x ${window.innerHeight} px`),
|
||||
// Page Header
|
||||
m('.debug-header', [
|
||||
m('.debug-header__title', [
|
||||
m('.debug-header__icon', m('i.fas.fa-bug')),
|
||||
m('.debug-header__text', [
|
||||
m('h1', 'Debug & Diagnostics'),
|
||||
m('p', 'Real-time build information, API performance metrics, and connection health.'),
|
||||
]),
|
||||
]),
|
||||
m('.debug-actions', [
|
||||
// The page keeps the code it loaded until reloaded, and a phone
|
||||
// browser hides that action away: a new build shows only after this.
|
||||
m('button', { onclick: () => window.location.reload(true) }, [m('i.fas.fa-sync-alt'), ' Reload the web UI']),
|
||||
m('button', { onclick: loadCoreVersion }, [m('i.fas.fa-stopwatch'), ' Ping the core']),
|
||||
m('.debug-header__actions', [
|
||||
m('button.debug-btn[type=button]', {
|
||||
onclick: () => window.location.reload(true),
|
||||
title: 'Force reload the Web UI bundle',
|
||||
}, [m('i.fas.fa-sync-alt'), m('span', 'Reload Web UI')]),
|
||||
m('button.debug-btn[type=button]', {
|
||||
onclick: loadCoreVersion,
|
||||
title: 'Ping the RetroShare core for version & latency',
|
||||
}, [m('i.fas.fa-stopwatch'), m('span', 'Ping Core')]),
|
||||
m('button.debug-btn.debug-btn--danger[type=button]', {
|
||||
onclick: () => rs.resetApiStats(),
|
||||
title: 'Reset API counters and latency tracking',
|
||||
}, [m('i.fas.fa-eraser'), m('span', 'Reset Stats')]),
|
||||
]),
|
||||
]),
|
||||
|
||||
m('.debug-section', [
|
||||
m('h3', 'API from this browser'),
|
||||
m('.debug-grid', [
|
||||
m('span', 'Requests in flight'), m('strong', s.pending),
|
||||
m('span', 'Requests since load'), m('strong', s.total),
|
||||
m('span', 'Last sendChat'), m('strong', s.lastSend ? `${s.lastSend.ms} ms, ${ago(s.lastSend.at)}` : 'none yet'),
|
||||
// KPI Summary Cards
|
||||
m('.debug-kpi-grid', [
|
||||
m('.debug-kpi-card', [
|
||||
m('.debug-kpi-card__icon.debug-kpi-card__icon--blue', m('i.fas.fa-server')),
|
||||
m('.debug-kpi-card__body', [
|
||||
m('.debug-kpi-card__label', 'Core Latency'),
|
||||
m('.debug-kpi-card__value', coreVersion ? `${coreVersionAt} ms` : '-'),
|
||||
m('.debug-kpi-card__subtext', [
|
||||
m(`span.debug-status-dot.${isConnected ? 'online' : 'offline'}`),
|
||||
isConnected ? 'Connected' : 'Offline',
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
m('h4', 'Slowest requests'),
|
||||
s.slowest.length === 0
|
||||
? m('p.debug-empty', 'Nothing yet.')
|
||||
: m('table.debug-table', [
|
||||
m('thead', m('tr', [m('th', 'Request'), m('th', 'Time'), m('th', 'When')])),
|
||||
m('tbody', s.slowest.map((e) => m('tr', [
|
||||
m('td', short(e.path)),
|
||||
m('td', e.ms + ' ms'),
|
||||
m('td', ago(e.at)),
|
||||
]))),
|
||||
]),
|
||||
m('h4', 'Last requests'),
|
||||
s.recent.length === 0
|
||||
? m('p.debug-empty', 'Nothing yet.')
|
||||
: m('table.debug-table', [
|
||||
m('thead', m('tr', [m('th', 'Request'), m('th', 'Time'), m('th', 'When')])),
|
||||
m('tbody', s.recent.map((e) => m('tr', [
|
||||
m('td', short(e.path)),
|
||||
m('td', e.ms + ' ms'),
|
||||
m('td', ago(e.at)),
|
||||
]))),
|
||||
]),
|
||||
m('.debug-actions', [
|
||||
m('button', { onclick: () => rs.resetApiStats() }, [m('i.fas.fa-eraser'), ' Reset counters']),
|
||||
m('.debug-kpi-card', [
|
||||
m('.debug-kpi-card__icon.debug-kpi-card__icon--purple', m('i.fas.fa-network-wired')),
|
||||
m('.debug-kpi-card__body', [
|
||||
m('.debug-kpi-card__label', 'Active Requests'),
|
||||
m('.debug-kpi-card__value', s.pending),
|
||||
m('.debug-kpi-card__subtext', `${s.total.toLocaleString()} total calls`),
|
||||
]),
|
||||
]),
|
||||
m('.debug-kpi-card', [
|
||||
m('.debug-kpi-card__icon.debug-kpi-card__icon--green', m('i.fas.fa-comment-dots')),
|
||||
m('.debug-kpi-card__body', [
|
||||
m('.debug-kpi-card__label', 'Last sendChat'),
|
||||
m('.debug-kpi-card__value', s.lastSend ? `${s.lastSend.ms} ms` : 'None yet'),
|
||||
m('.debug-kpi-card__subtext', s.lastSend ? ago(s.lastSend.at) : 'No chat sent'),
|
||||
]),
|
||||
]),
|
||||
m('.debug-kpi-card', [
|
||||
m('.debug-kpi-card__icon.debug-kpi-card__icon--amber', m('i.fas.fa-satellite-dish')),
|
||||
m('.debug-kpi-card__body', [
|
||||
m('.debug-kpi-card__label', 'Event Stream'),
|
||||
m('.debug-kpi-card__value', rs.formatBytes(s.eventsBytes)),
|
||||
m('.debug-kpi-card__subtext', `${s.eventsRestarts} reconnects • ${ago(s.lastEventAt)}`),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
m('.debug-section', [
|
||||
m('h3', 'Event stream'),
|
||||
m('.debug-grid', [
|
||||
m('span', 'Received'), m('strong', rs.formatBytes(s.eventsBytes)),
|
||||
m('span', 'Last event'), m('strong', ago(s.lastEventAt)),
|
||||
m('span', 'Reconnections'), m('strong', s.eventsRestarts),
|
||||
// Detail Sections Grid (Build info + Event stream info)
|
||||
m('.debug-grid-2col', [
|
||||
m('.debug-section', [
|
||||
m('.debug-section__header', [
|
||||
m('i.fas.fa-cube'),
|
||||
m('h3', 'Build & Environment'),
|
||||
]),
|
||||
m('.debug-info-list', [
|
||||
m('.debug-info-row', [
|
||||
m('span.debug-info-label', 'Web UI Version'),
|
||||
m('span.debug-info-value', m('span.debug-badge.debug-badge--blue', version || 'dev')),
|
||||
]),
|
||||
m('.debug-info-row', [
|
||||
m('span.debug-info-label', 'Core Version'),
|
||||
m('span.debug-info-value', [
|
||||
m('span.debug-badge.debug-badge--slate', core),
|
||||
coreHuman && m('small.debug-sublabel', coreHuman),
|
||||
]),
|
||||
]),
|
||||
m('.debug-info-row', [
|
||||
m('span.debug-info-label', 'Core Round Trip'),
|
||||
m('span.debug-info-value', coreVersion ? `${coreVersionAt} ms` : '-'),
|
||||
]),
|
||||
m('.debug-info-row', [
|
||||
m('span.debug-info-label', 'Page Loaded'),
|
||||
m('span.debug-info-value', ago(s.startedAt)),
|
||||
]),
|
||||
m('.debug-info-row', [
|
||||
m('span.debug-info-label', 'Browser Viewport'),
|
||||
m('span.debug-info-value', `${window.innerWidth} × ${window.innerHeight} px`),
|
||||
]),
|
||||
m('.debug-info-row', [
|
||||
m('span.debug-info-label', 'Core Connection'),
|
||||
m('span.debug-info-value', [
|
||||
m(`span.debug-status-dot.${isConnected ? 'online' : 'offline'}`),
|
||||
isConnected ? 'Online' : 'Disconnected',
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
m('.debug-section', [
|
||||
m('.debug-section__header', [
|
||||
m('i.fas.fa-stream'),
|
||||
m('h3', 'Event Stream & Connection'),
|
||||
]),
|
||||
m('.debug-info-list', [
|
||||
m('.debug-info-row', [
|
||||
m('span.debug-info-label', 'Data Received'),
|
||||
m('span.debug-info-value', rs.formatBytes(s.eventsBytes)),
|
||||
]),
|
||||
m('.debug-info-row', [
|
||||
m('span.debug-info-label', 'Last Event Time'),
|
||||
m('span.debug-info-value', ago(s.lastEventAt)),
|
||||
]),
|
||||
m('.debug-info-row', [
|
||||
m('span.debug-info-label', 'Reconnections'),
|
||||
m('span.debug-info-value', String(s.eventsRestarts)),
|
||||
]),
|
||||
m('.debug-info-row', [
|
||||
m('span.debug-info-label', 'Connection Mode'),
|
||||
m('span.debug-info-value', m('span.debug-badge.debug-badge--green', 'Active Long-Poll / SSE')),
|
||||
]),
|
||||
]),
|
||||
m('.debug-callout', [
|
||||
m('i.fas.fa-info-circle'),
|
||||
m('p', 'The event stream carries every real-time event from the core over a single persistent HTTP channel. Browsers typically keep up to 6 simultaneous connections per host, allowing the remaining 5 to handle concurrent API requests.'),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
// API Performance Section
|
||||
m('.debug-section', [
|
||||
m('.debug-section__header', [
|
||||
m('i.fas.fa-tachometer-alt'),
|
||||
m('h3', 'API Performance & Request History'),
|
||||
]),
|
||||
|
||||
m('.debug-tables-grid', [
|
||||
m('.debug-table-panel', [
|
||||
m('.debug-table-panel__header', [
|
||||
m('h4', 'Slowest Requests'),
|
||||
m('span.debug-count-badge', `${s.slowest.length} recorded`),
|
||||
]),
|
||||
s.slowest.length === 0
|
||||
? m('.debug-empty', [
|
||||
m('i.fas.fa-check-circle'),
|
||||
m('p', 'No slow requests recorded yet.'),
|
||||
])
|
||||
: m('.debug-table-wrap', [
|
||||
m('table.debug-table', [
|
||||
m('thead', m('tr', [
|
||||
m('th', 'Endpoint'),
|
||||
m('th', 'Latency'),
|
||||
m('th', 'When'),
|
||||
])),
|
||||
m('tbody', s.slowest.map((e) => m('tr', [
|
||||
m('td.debug-table__endpoint', m('code', short(e.path))),
|
||||
m('td.debug-table__latency', m(`span.debug-latency-badge.${latencyClass(e.ms)}`, `${e.ms} ms`)),
|
||||
m('td.debug-table__when', ago(e.at)),
|
||||
]))),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
m('.debug-table-panel', [
|
||||
m('.debug-table-panel__header', [
|
||||
m('h4', 'Recent Requests'),
|
||||
m('span.debug-count-badge', `${s.recent.length} recent`),
|
||||
]),
|
||||
s.recent.length === 0
|
||||
? m('.debug-empty', [
|
||||
m('i.fas.fa-inbox'),
|
||||
m('p', 'No requests recorded yet.'),
|
||||
])
|
||||
: m('.debug-table-wrap', [
|
||||
m('table.debug-table', [
|
||||
m('thead', m('tr', [
|
||||
m('th', 'Endpoint'),
|
||||
m('th', 'Latency'),
|
||||
m('th', 'When'),
|
||||
])),
|
||||
m('tbody', s.recent.map((e) => m('tr', [
|
||||
m('td.debug-table__endpoint', m('code', short(e.path))),
|
||||
m('td.debug-table__latency', m(`span.debug-latency-badge.${latencyClass(e.ms)}`, `${e.ms} ms`)),
|
||||
m('td.debug-table__when', ago(e.at)),
|
||||
]))),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
m('p.debug-hint', 'The stream carries every event of the core over one long request; it holds one of the six connections a browser keeps to a host, the others queue the requests above.'),
|
||||
]),
|
||||
]);
|
||||
},
|
||||
|
||||
@ -1,86 +1,647 @@
|
||||
// The debug page (debug/debug.js): build, API timings, event stream.
|
||||
// ---------------------------------------------------------------------------
|
||||
// Debug & Diagnostics page (debug/debug.js)
|
||||
// Follows the modern design patterns used in Network, People, Chat, and Statistics.
|
||||
// ---------------------------------------------------------------------------
|
||||
@use '../abstracts' as *;
|
||||
|
||||
.debug-page {
|
||||
padding: 1.25rem;
|
||||
max-width: 720px;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 1.5rem;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 1rem;
|
||||
font-size: 1.25rem;
|
||||
// ── Header Section ──
|
||||
.debug-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: #64748b;
|
||||
&__icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 0.75rem;
|
||||
background: #e0f2fe;
|
||||
color: #0284c7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.4rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 0.75rem 0 0.25rem;
|
||||
font-size: 0.8rem;
|
||||
color: #475569;
|
||||
&__text {
|
||||
h1 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 800;
|
||||
color: #1e293b;
|
||||
margin: 0 0 0.25rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.9rem;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
padding: 0.55rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
border: 1px solid #cbd5e1;
|
||||
background: #ffffff;
|
||||
color: #334155;
|
||||
transition: all 0.15s ease;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:hover {
|
||||
background: #f8fafc;
|
||||
border-color: #94a3b8;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
&--danger {
|
||||
color: #dc2626;
|
||||
border-color: #fecaca;
|
||||
background: #ffffff;
|
||||
|
||||
&:hover {
|
||||
background: #fef2f2;
|
||||
border-color: #fca5a5;
|
||||
color: #b91c1c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── KPI Summary Cards ──
|
||||
.debug-kpi-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.debug-kpi-card {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.15rem 1.25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
border-radius: 0.6rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.25rem;
|
||||
flex-shrink: 0;
|
||||
|
||||
&--blue {
|
||||
background: #e0f2fe;
|
||||
color: #0284c7;
|
||||
}
|
||||
|
||||
&--purple {
|
||||
background: #f3e8ff;
|
||||
color: #7e22ce;
|
||||
}
|
||||
|
||||
&--green {
|
||||
background: #dcfce7;
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
&--amber {
|
||||
background: #fef3c7;
|
||||
color: #d97706;
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
margin-bottom: 0.2rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-size: 1.35rem;
|
||||
font-weight: 800;
|
||||
color: #1e293b;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__subtext {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
font-size: 0.78rem;
|
||||
color: #64748b;
|
||||
margin-top: 0.25rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Status Dots ──
|
||||
.debug-status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.online {
|
||||
background: #10b981;
|
||||
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.25);
|
||||
}
|
||||
|
||||
&.offline {
|
||||
background: #ef4444;
|
||||
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Detail Grid 2-Column ──
|
||||
.debug-grid-2col {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 1.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
// ── Detail Sections ──
|
||||
.debug-section {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.25rem 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
|
||||
margin-bottom: 1.25rem;
|
||||
|
||||
.debug-grid {
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr;
|
||||
gap: 0.25rem 1rem;
|
||||
font-size: 0.9rem;
|
||||
color: #334155;
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
padding-bottom: 0.85rem;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
strong {
|
||||
word-break: break-word;
|
||||
i {
|
||||
color: #0788cb;
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.05rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.debug-actions {
|
||||
// ── Info List ──
|
||||
.debug-info-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.75rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 1px solid #cbd5e1;
|
||||
background: #f8fafc;
|
||||
color: #334155;
|
||||
border-radius: 0.375rem;
|
||||
padding: 0.4rem 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
.debug-info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 0.55rem 0;
|
||||
border-bottom: 1px solid #f8fafc;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-info-label {
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.debug-info-value {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.debug-sublabel {
|
||||
color: #94a3b8;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
// ── Badges ──
|
||||
.debug-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.18rem 0.55rem;
|
||||
border-radius: 0.35rem;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
|
||||
&--blue {
|
||||
background: #e0f2fe;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
&--slate {
|
||||
background: #f1f5f9;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
&--green {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Callout Box ──
|
||||
.debug-callout {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: flex-start;
|
||||
margin-top: 1rem;
|
||||
padding: 0.85rem 1rem;
|
||||
background: #f0f9ff;
|
||||
border: 1px solid #bae6fd;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
i {
|
||||
color: #0284c7;
|
||||
font-size: 1.1rem;
|
||||
margin-top: 0.15rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 0.82rem;
|
||||
color: #0369a1;
|
||||
line-height: 1.45;
|
||||
}
|
||||
}
|
||||
|
||||
// ── API Tables Grid ──
|
||||
.debug-tables-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.debug-table-panel {
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.debug-count-badge {
|
||||
font-size: 0.75rem;
|
||||
color: #64748b;
|
||||
background: #e2e8f0;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 0.3rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.debug-table-wrap {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.debug-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.85rem;
|
||||
font-size: 0.84rem;
|
||||
|
||||
th, td {
|
||||
th {
|
||||
text-align: left;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
padding: 0.55rem 0.65rem;
|
||||
font-size: 0.74rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: #64748b;
|
||||
border-bottom: 2px solid #e2e8f0;
|
||||
background: #ffffff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
td {
|
||||
padding: 0.55rem 0.65rem;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
vertical-align: middle;
|
||||
background: #ffffff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
tbody tr:hover td {
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 0.82rem;
|
||||
color: #0284c7;
|
||||
background: #f0f9ff;
|
||||
padding: 0.15rem 0.35rem;
|
||||
border-radius: 0.25rem;
|
||||
border: 1px solid #e0f2fe;
|
||||
display: inline-block;
|
||||
max-width: 280px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.debug-latency-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 0.3rem;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
|
||||
&.debug-latency--fast {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
&.debug-latency--moderate {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
&.debug-latency--slow {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-table__when {
|
||||
color: #94a3b8;
|
||||
font-size: 0.8rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-empty, .debug-hint {
|
||||
font-size: 0.8rem;
|
||||
color: #64748b;
|
||||
margin: 0.25rem 0 0;
|
||||
.debug-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
padding: 2rem 1rem;
|
||||
color: #94a3b8;
|
||||
text-align: center;
|
||||
|
||||
i {
|
||||
font-size: 2rem;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mobile Responsiveness (Phone Mode) ──
|
||||
@media (max-width: 900px) {
|
||||
.debug-kpi-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.debug-grid-2col {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.debug-tables-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.debug-page {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.debug-header {
|
||||
margin-bottom: 1rem;
|
||||
gap: 0.75rem;
|
||||
|
||||
&__title {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 1.2rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
&__text h1 {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
&__text p {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-btn {
|
||||
padding: 0.5rem 0.25rem;
|
||||
justify-content: center;
|
||||
font-size: 0.78rem;
|
||||
min-height: 40px;
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-kpi-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.debug-kpi-card {
|
||||
padding: 0.75rem 0.65rem;
|
||||
gap: 0.6rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
&__icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
font-size: 1rem;
|
||||
border-radius: 0.45rem;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
&__subtext {
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-section {
|
||||
padding: 0.85rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
|
||||
&__header {
|
||||
padding-bottom: 0.6rem;
|
||||
margin-bottom: 0.65rem;
|
||||
|
||||
h3 {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.debug-info-row {
|
||||
padding: 0.45rem 0;
|
||||
}
|
||||
|
||||
.debug-info-label {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.debug-info-value {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.debug-table-panel {
|
||||
padding: 0.65rem;
|
||||
}
|
||||
|
||||
.debug-table {
|
||||
font-size: 0.8rem;
|
||||
|
||||
th, td {
|
||||
padding: 0.45rem 0.5rem;
|
||||
}
|
||||
|
||||
code {
|
||||
max-width: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.debug-header__actions {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.debug-kpi-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.debug-info-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.2rem;
|
||||
|
||||
.debug-info-value {
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user