fix(devops): responsive UI + overflow guards in DevopsPanel
@bytelyst/devops 0.1.3: - Wrap tables in scrollable container (overflow-x: auto) so long values never push the panel wider than its parent. - table-layout: fixed + min-width on key column prevents column blow-out. - Code/value cells use overflow-wrap: anywhere + word-break: break-word so long commit SHAs and Docker image strings wrap. - pre block uses pre-wrap + break-word for raw JSON. - Header actions wrap on narrow viewports. - Tabs row scrolls horizontally rather than wrapping awkwardly. - root container: maxWidth 100% + minWidth 0 to play nicely with flex parents. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
51fc8d09b0
commit
d2420f5d3c
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bytelyst/devops",
|
"name": "@bytelyst/devops",
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Runtime devops metadata (build info, uptime, health) — server collector + React UI",
|
"description": "Runtime devops metadata (build info, uptime, health) — server collector + React UI",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@ -231,18 +231,20 @@ interface KvRow {
|
|||||||
|
|
||||||
function KvTable({ rows }: { rows: KvRow[] }): React.ReactElement {
|
function KvTable({ rows }: { rows: KvRow[] }): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<table style={styles.table}>
|
<div style={styles.tableWrap}>
|
||||||
<tbody>
|
<table style={styles.table}>
|
||||||
{rows.map((r, i) => (
|
<tbody>
|
||||||
<tr key={i} style={styles.tr}>
|
{rows.map((r, i) => (
|
||||||
<th style={styles.th} scope="row">
|
<tr key={i} style={styles.tr}>
|
||||||
{r.key}
|
<th style={styles.th} scope="row">
|
||||||
</th>
|
{r.key}
|
||||||
<td style={styles.td}>{r.value}</td>
|
</th>
|
||||||
</tr>
|
<td style={styles.td}>{r.value}</td>
|
||||||
))}
|
</tr>
|
||||||
</tbody>
|
))}
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,30 +254,32 @@ function DependenciesTable({
|
|||||||
deps: NonNullable<DevopsInfo['dependencies']>;
|
deps: NonNullable<DevopsInfo['dependencies']>;
|
||||||
}): React.ReactElement {
|
}): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<table style={styles.table}>
|
<div style={styles.tableWrap}>
|
||||||
<thead>
|
<table style={styles.table}>
|
||||||
<tr style={styles.tr}>
|
<thead>
|
||||||
<th style={{ ...styles.th, textAlign: 'left' }}>Name</th>
|
<tr style={styles.tr}>
|
||||||
<th style={{ ...styles.th, textAlign: 'left' }}>Status</th>
|
<th style={{ ...styles.th, textAlign: 'left' }}>Name</th>
|
||||||
<th style={{ ...styles.th, textAlign: 'left' }}>Latency</th>
|
<th style={{ ...styles.th, textAlign: 'left' }}>Status</th>
|
||||||
<th style={{ ...styles.th, textAlign: 'left' }}>Detail</th>
|
<th style={{ ...styles.th, textAlign: 'left' }}>Latency</th>
|
||||||
</tr>
|
<th style={{ ...styles.th, textAlign: 'left' }}>Detail</th>
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{deps.map((d, i) => (
|
|
||||||
<tr key={i} style={styles.tr}>
|
|
||||||
<td style={styles.td}>{d.name}</td>
|
|
||||||
<td style={styles.td}>
|
|
||||||
<span style={{ ...styles.badge, ...(d.ok ? styles.badgeOk : styles.badgeErr) }}>
|
|
||||||
{d.ok ? 'OK' : 'FAIL'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td style={styles.td}>{d.latencyMs != null ? `${d.latencyMs} ms` : '—'}</td>
|
|
||||||
<td style={styles.td}>{d.detail ?? '—'}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{deps.map((d, i) => (
|
||||||
|
<tr key={i} style={styles.tr}>
|
||||||
|
<td style={styles.td}>{d.name}</td>
|
||||||
|
<td style={styles.td}>
|
||||||
|
<span style={{ ...styles.badge, ...(d.ok ? styles.badgeOk : styles.badgeErr) }}>
|
||||||
|
{d.ok ? 'OK' : 'FAIL'}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td style={styles.td}>{d.latencyMs != null ? `${d.latencyMs} ms` : '—'}</td>
|
||||||
|
<td style={styles.td}>{d.detail ?? '—'}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +293,7 @@ function formatTimestamp(iso: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles: Record<string, React.CSSProperties> = {
|
const styles: Record<string, React.CSSProperties> = {
|
||||||
root: { display: 'flex', flexDirection: 'column', gap: 12 },
|
root: { display: 'flex', flexDirection: 'column', gap: 12, maxWidth: '100%', minWidth: 0 },
|
||||||
header: {
|
header: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
@ -300,7 +304,7 @@ const styles: Record<string, React.CSSProperties> = {
|
|||||||
title: { fontSize: 16, fontWeight: 600, color: 'var(--foreground, inherit)' },
|
title: { fontSize: 16, fontWeight: 600, color: 'var(--foreground, inherit)' },
|
||||||
subtitle: { fontSize: 13, color: 'var(--muted-foreground, #6b7280)', marginTop: 4 },
|
subtitle: { fontSize: 13, color: 'var(--muted-foreground, #6b7280)', marginTop: 4 },
|
||||||
muted: { fontSize: 12, color: 'var(--muted-foreground, #6b7280)' },
|
muted: { fontSize: 12, color: 'var(--muted-foreground, #6b7280)' },
|
||||||
headerActions: { display: 'flex', gap: 8, alignItems: 'center' },
|
headerActions: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' },
|
||||||
sourceToggle: {
|
sourceToggle: {
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
border: '1px solid var(--border, #e5e7eb)',
|
border: '1px solid var(--border, #e5e7eb)',
|
||||||
@ -332,6 +336,8 @@ const styles: Record<string, React.CSSProperties> = {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: 2,
|
gap: 2,
|
||||||
borderBottom: '1px solid var(--border, #e5e7eb)',
|
borderBottom: '1px solid var(--border, #e5e7eb)',
|
||||||
|
overflowX: 'auto',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
},
|
},
|
||||||
tab: {
|
tab: {
|
||||||
padding: '8px 14px',
|
padding: '8px 14px',
|
||||||
@ -341,14 +347,21 @@ const styles: Record<string, React.CSSProperties> = {
|
|||||||
borderBottom: '2px solid transparent',
|
borderBottom: '2px solid transparent',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
color: 'var(--muted-foreground, #6b7280)',
|
color: 'var(--muted-foreground, #6b7280)',
|
||||||
|
flexShrink: 0,
|
||||||
},
|
},
|
||||||
tabActive: {
|
tabActive: {
|
||||||
color: 'var(--foreground, inherit)',
|
color: 'var(--foreground, inherit)',
|
||||||
borderBottomColor: 'var(--accent, #2563eb)',
|
borderBottomColor: 'var(--accent, #2563eb)',
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
},
|
},
|
||||||
panel: { paddingTop: 8 },
|
panel: { paddingTop: 8, minWidth: 0 },
|
||||||
table: { width: '100%', borderCollapse: 'collapse', fontSize: 13 },
|
tableWrap: {
|
||||||
|
width: '100%',
|
||||||
|
overflowX: 'auto',
|
||||||
|
overflowY: 'visible',
|
||||||
|
WebkitOverflowScrolling: 'touch',
|
||||||
|
},
|
||||||
|
table: { width: '100%', borderCollapse: 'collapse', fontSize: 13, tableLayout: 'fixed' },
|
||||||
tr: { borderBottom: '1px solid var(--border, #f3f4f6)' },
|
tr: { borderBottom: '1px solid var(--border, #f3f4f6)' },
|
||||||
th: {
|
th: {
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
@ -357,14 +370,22 @@ const styles: Record<string, React.CSSProperties> = {
|
|||||||
padding: '8px 12px 8px 0',
|
padding: '8px 12px 8px 0',
|
||||||
verticalAlign: 'top',
|
verticalAlign: 'top',
|
||||||
width: '30%',
|
width: '30%',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
td: {
|
||||||
|
padding: '8px 0',
|
||||||
|
verticalAlign: 'top',
|
||||||
|
color: 'var(--foreground, inherit)',
|
||||||
|
overflowWrap: 'anywhere',
|
||||||
|
wordBreak: 'break-word',
|
||||||
},
|
},
|
||||||
td: { padding: '8px 0', verticalAlign: 'top', color: 'var(--foreground, inherit)' },
|
|
||||||
code: {
|
code: {
|
||||||
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
background: 'color-mix(in oklab, var(--foreground, #000) 5%, transparent)',
|
background: 'color-mix(in oklab, var(--foreground, #000) 5%, transparent)',
|
||||||
padding: '2px 6px',
|
padding: '2px 6px',
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
|
wordBreak: 'break-all',
|
||||||
},
|
},
|
||||||
pre: {
|
pre: {
|
||||||
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
||||||
@ -374,6 +395,8 @@ const styles: Record<string, React.CSSProperties> = {
|
|||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
maxHeight: 480,
|
maxHeight: 480,
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
wordBreak: 'break-word',
|
||||||
},
|
},
|
||||||
badge: {
|
badge: {
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user