mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-11 09:49:41 +02:00
feat(header): rings for CPU and MEM in the Compact style (#426)
Compact now draws every reading the same way, ring then label then value: CPU and MEM get an accent ring (red past 80%, like their value) in place of the sparklines, the plan windows keep their green, yellow or red rings, the dot separators go, and both pills share one label and one value style. The sparkline markup, history and CSS are removed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -163,19 +163,19 @@
|
||||
</div>
|
||||
<div class="header-system-stats" id="headerSystemStats" title="System resource usage">
|
||||
<div class="stat-item">
|
||||
<span class="stat-ring" id="statCpuRing" aria-hidden="true"></span>
|
||||
<span class="stat-label">CPU</span>
|
||||
<div class="stat-bar">
|
||||
<div class="stat-bar-fill stat-bar-cpu" id="statCpuBar"></div>
|
||||
</div>
|
||||
<span class="stat-spark" id="statCpuSpark" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
|
||||
<span class="stat-value" id="statCpu">--%</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-ring" id="statMemRing" aria-hidden="true"></span>
|
||||
<span class="stat-label">MEM</span>
|
||||
<div class="stat-bar">
|
||||
<div class="stat-bar-fill stat-bar-mem" id="statMemBar"></div>
|
||||
</div>
|
||||
<span class="stat-spark" id="statMemSpark" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
|
||||
<span class="stat-value" id="statMem">--</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1921,10 +1921,10 @@
|
||||
<label class="set-chip" data-preview="header" data-preview-order="13" data-preview-text="42%"><input type="checkbox" id="appSettingsShowPlanUsageLimits"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 18a8 8 0 1 1 16 0"/><path d="M12 18l4.5-5"/></svg><span>Plan Usage</span></label>
|
||||
<label class="set-chip" data-preview="header" data-preview-order="14"><input type="checkbox" id="appSettingsShowLifecycleLog"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg><span>Lifecycle Log</span></label>
|
||||
</div>
|
||||
<div class="set-row has-field" data-search="header stats style system cpu mem ws plan usage tiles compact rings sparkline">
|
||||
<div class="set-row has-field" data-search="header stats style system cpu mem ws plan usage tiles compact rings">
|
||||
<div class="set-row-text">
|
||||
<span class="set-row-label">Header Stats Style <span class="set-tag">desktop</span></span>
|
||||
<span class="set-row-desc">How WS, CPU, MEM and the plan-usage windows are drawn. Compact is one system pill with sparklines plus usage rings; Tiles put each label over its value with a bar underneath.</span>
|
||||
<span class="set-row-desc">How WS, CPU, MEM and the plan-usage windows are drawn. Compact puts a ring beside each value in two pills; Tiles put each label over its value with a bar underneath.</span>
|
||||
</div>
|
||||
<select id="appSettingsHeaderStatsStyle" class="set-select">
|
||||
<option value="classic">As before (bars)</option>
|
||||
|
||||
+13
-20
@@ -6028,10 +6028,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
}
|
||||
|
||||
// Sparklines for the Compact header style. Always recorded (four numbers a
|
||||
// poll), so switching styles shows history at once instead of empty bars.
|
||||
this._pushStatSpark('cpu', stats.cpu);
|
||||
this._pushStatSpark('mem', stats.memory?.percent);
|
||||
// Rings for the Compact header style, kept current in every style (two
|
||||
// style writes a poll) so switching styles never shows an empty ring.
|
||||
this._setStatRing('statCpuRing', stats.cpu);
|
||||
this._setStatRing('statMemRing', stats.memory?.percent);
|
||||
|
||||
if (memEl && memBar) {
|
||||
const memGB = (stats.memory.usedMB / 1024).toFixed(1);
|
||||
@@ -6051,24 +6051,17 @@ Object.assign(CodemanApp.prototype, {
|
||||
},
|
||||
|
||||
/**
|
||||
* Record one percentage sample and redraw that stat's sparkline: one bar per
|
||||
* sample, newest on the right, as many samples as the template has bars.
|
||||
* Bars stay 10% tall at minimum so an idle machine still draws a baseline.
|
||||
* Fill one stat ring (Compact header style) to `percent`, clamped to 0-100,
|
||||
* and flag it `high` above 80%, the same threshold at which the value next
|
||||
* to it turns red.
|
||||
*/
|
||||
_pushStatSpark(key, percent) {
|
||||
const history = (this._statSparkHistory ||= { cpu: [], mem: [] })[key];
|
||||
if (!history) return;
|
||||
const el = this.$(key === 'cpu' ? 'statCpuSpark' : 'statMemSpark');
|
||||
const size = el?.children.length || 4;
|
||||
_setStatRing(id, percent) {
|
||||
const ring = this.$(id);
|
||||
if (!ring) return;
|
||||
const value = Number(percent);
|
||||
history.push(Number.isFinite(value) ? Math.min(100, Math.max(0, value)) : 0);
|
||||
while (history.length > size) history.shift();
|
||||
if (!el) return;
|
||||
const offset = size - history.length;
|
||||
for (let i = 0; i < el.children.length; i++) {
|
||||
const sample = history[i - offset];
|
||||
el.children[i].style.height = sample === undefined ? '' : `${Math.max(10, Math.round(sample))}%`;
|
||||
}
|
||||
const fill = Number.isFinite(value) ? Math.round(Math.min(100, Math.max(0, value))) : 0;
|
||||
ring.style.setProperty('--pu', String(fill));
|
||||
ring.classList.toggle('high', fill > 80);
|
||||
},
|
||||
|
||||
// ─── Clipboard ──────────────────────────────────────────────────────────────
|
||||
|
||||
+53
-58
@@ -20107,27 +20107,37 @@ html[data-session-list='sidebar'][data-sidebar='collapsed'] .session-sidebar .ta
|
||||
it, so the cluster is contiguous. */
|
||||
|
||||
/* Parts only the two new styles draw. */
|
||||
.stat-spark,
|
||||
.stat-ring,
|
||||
.connection-tile,
|
||||
.header-plan-usage .pu-ring,
|
||||
.header-plan-usage .pu-meter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* --- Compact: one system pill with sparklines, one plan-ring pill ------- */
|
||||
html[data-header-stats='compact'] .header-system-stats {
|
||||
/* --- Compact: two pills, a ring beside every value ------------------------ */
|
||||
/* One system pill (WS, CPU, MEM) and one plan pill (5h, 7d). Every reading is
|
||||
the same shape, ring then label then value, so the two pills read as one
|
||||
set; CPU and MEM rings are the accent colour (red past 80%, like their
|
||||
value), the plan rings green, yellow or red by how much is used. */
|
||||
html[data-header-stats='compact'] .header-system-stats,
|
||||
html[data-header-stats='compact'] .header-plan-usage {
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
height: 24px;
|
||||
padding: 0 10px;
|
||||
min-height: 24px;
|
||||
padding: 0 11px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 12px;
|
||||
background: var(--control-bg);
|
||||
border: 1px solid var(--control-border);
|
||||
font-family: 'JetBrains Mono', var(--font-mono, monospace);
|
||||
font-size: 0.68rem;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-system-stats {
|
||||
gap: 13px;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-system-stats > .connection-indicator {
|
||||
font-size: inherit;
|
||||
color: var(--text);
|
||||
@@ -20138,66 +20148,46 @@ html[data-header-stats='compact'] .header-system-stats > .connection-indicator >
|
||||
height: 7px;
|
||||
}
|
||||
|
||||
/* Same bundled face as Tiles, so the pills read the same on every machine. */
|
||||
html[data-header-stats='compact'] .header-system-stats,
|
||||
html[data-header-stats='compact'] .header-plan-usage {
|
||||
font-family: 'JetBrains Mono', var(--font-mono, monospace);
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-system-stats > .connection-indicator::after,
|
||||
html[data-header-stats='compact'] .header-system-stats > .stat-item + .stat-item::before {
|
||||
content: '·';
|
||||
margin: 0 8px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-system-stats .stat-label {
|
||||
min-width: 0;
|
||||
font-size: 0.62rem;
|
||||
color: var(--text-muted);
|
||||
html[data-header-stats='compact'] .header-system-stats .stat-item {
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-system-stats .stat-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-system-stats .stat-value {
|
||||
/* One label style and one value style across both pills. */
|
||||
html[data-header-stats='compact'] .header-system-stats .stat-label,
|
||||
html[data-header-stats='compact'] .header-plan-usage .pu-label {
|
||||
min-width: 0;
|
||||
font-size: 0.6rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-system-stats .stat-value,
|
||||
html[data-header-stats='compact'] .header-plan-usage .pu-val {
|
||||
min-width: 0;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-system-stats .stat-value:not(.high) {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .stat-spark {
|
||||
display: inline-flex;
|
||||
align-items: flex-end;
|
||||
gap: 1px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .stat-spark i {
|
||||
width: 3px;
|
||||
height: 10%;
|
||||
border-radius: 1px;
|
||||
background: var(--accent);
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-plan-usage {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
height: 24px;
|
||||
min-height: 24px;
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 12px;
|
||||
background: var(--control-bg);
|
||||
border-color: var(--control-border);
|
||||
gap: 13px;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-plan-usage .pu-row {
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-plan-usage .pu-provider {
|
||||
@@ -20207,7 +20197,7 @@ html[data-header-stats='compact'] .header-plan-usage .pu-provider {
|
||||
|
||||
html[data-header-stats='compact'] .header-plan-usage .pu-windows {
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: 13px;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-plan-usage .pu-sep {
|
||||
@@ -20216,27 +20206,32 @@ html[data-header-stats='compact'] .header-plan-usage .pu-sep {
|
||||
|
||||
html[data-header-stats='compact'] .header-plan-usage .pu-win {
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .header-plan-usage .pu-label {
|
||||
font-weight: 500;
|
||||
text-transform: none;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
/* A ring per window: a conic fill masked down to a 2px stroke, coloured like
|
||||
the value next to it (the pu-green/-yellow/-red class sits on both). */
|
||||
/* A ring: a conic fill masked down to a 2px stroke. Its colour is currentColor,
|
||||
so a plan ring takes the pu-green/-yellow/-red class it shares with its
|
||||
value, and a stat ring the accent (red when .high). */
|
||||
html[data-header-stats='compact'] .stat-ring,
|
||||
html[data-header-stats='compact'] .header-plan-usage .pu-ring {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
flex: none;
|
||||
border-radius: 50%;
|
||||
background: conic-gradient(currentColor calc(var(--pu, 0) * 1%), var(--control-border) 0);
|
||||
background: conic-gradient(currentColor calc(var(--pu, 0) * 1%), var(--control-border-hover, var(--control-border)) 0);
|
||||
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), #000 calc(100% - 2px));
|
||||
mask: radial-gradient(farthest-side, transparent calc(100% - 2.5px), #000 calc(100% - 2px));
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .stat-ring {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
html[data-header-stats='compact'] .stat-ring.high {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
/* --- Tiles: label over value, a bar underneath, no icons ----------------- */
|
||||
/* Each tile is a three-row grid (label, value, bar) with pixel line-heights,
|
||||
so its height comes from the layout, not from whichever monospace face the
|
||||
|
||||
+1
-1
@@ -1457,7 +1457,7 @@ export const SettingsUpdateSchema = z
|
||||
* How the header draws its WS / CPU / MEM / plan-usage cluster. Display key
|
||||
* (per-device), desktop only (the cluster is hidden below 768px).
|
||||
* 'classic' = the bars and the 5H · 7D chip, as before
|
||||
* 'compact' = one system pill with sparklines plus a plan-usage ring pill
|
||||
* 'compact' = two pills (WS/CPU/MEM, the plan windows), a ring beside every value
|
||||
* 'tiles' = label over value with a bar underneath, no icons. The default.
|
||||
*/
|
||||
headerStatsStyle: z.enum(['classic', 'compact', 'tiles']).optional(),
|
||||
|
||||
Reference in New Issue
Block a user