mirror of
https://github.com/tiennm99/claude-code-usage-bubble.git
synced 2026-10-23 14:14:47 +00:00
fix(ui): reserve the weekly percent column at its semibold weight
The tail percent is drawn at weight 600 but its column was measured at 400, so "100%" was clipped at larger bubble sizes. The layout now asks the measure callback for the weight the text is drawn in.
This commit is contained in:
1 parent
452ba60ea5
commit
c4ad23236e
4 files changed
+27
-5
No files matched your search
@@ -15,6 +15,7 @@ import {
|
||||
remainingFraction,
|
||||
ringDash,
|
||||
scale,
|
||||
TAIL_PCT_WEIGHT,
|
||||
tailLayout,
|
||||
timeFillWidth,
|
||||
usageFillWidth,
|
||||
@@ -130,6 +131,21 @@ describe('tailLayout', () => {
|
||||
assert.equal(tail.usageBarRect.r - tail.usageBarRect.l, 8);
|
||||
});
|
||||
|
||||
it('reserves the percent column at the weight it is drawn in', () => {
|
||||
const regular = 40;
|
||||
const semibold = 61.3;
|
||||
/** @type {Array<number | undefined>} */
|
||||
const pctWeights = [];
|
||||
const tail = tailLayout({ canvasW: 360, canvasH: 138, dpi: 96 }, (text, _px, weight) => {
|
||||
if (text === '100%') {
|
||||
pctWeights.push(weight);
|
||||
}
|
||||
return weight === TAIL_PCT_WEIGHT ? semibold : regular;
|
||||
});
|
||||
assert.deepEqual(pctWeights, [600]);
|
||||
assert.ok(tail.usagePctRect.r - tail.usagePctRect.l >= Math.ceil(semibold));
|
||||
});
|
||||
|
||||
it('rounds fractional measurements up', () => {
|
||||
const exact = tailLayout({ canvasW: 360, canvasH: 138, dpi: 96 }, () => 40);
|
||||
const fractional = tailLayout({ canvasW: 360, canvasH: 138, dpi: 96 }, () => 39.2);
|
||||
|
||||
@@ -101,6 +101,7 @@ body {
|
||||
}
|
||||
|
||||
.tail-pct {
|
||||
/* The layout reserves this column measured at TAIL_PCT_WEIGHT in layout.js; change both together. */
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@ const DRAG_THRESHOLD_PX = 3;
|
||||
const PULSE_INTERVAL_MS = 80;
|
||||
const TIME_PROGRESS_INTERVAL_MS = 60_000;
|
||||
const FONT_STACK = '"Segoe UI", system-ui, sans-serif';
|
||||
/** Weight the layout's reserved text widths are measured at. */
|
||||
/** Weight text is measured at when the caller names none (regular). */
|
||||
const MEASURE_WEIGHT = 400;
|
||||
const HEAD_LABEL_WEIGHT = 600;
|
||||
|
||||
@@ -212,7 +212,7 @@ function relayout(p) {
|
||||
}
|
||||
const canvas = { canvasW: p.canvasW, canvasH: p.canvasH, dpi: p.dpi };
|
||||
head = headMetrics(canvas);
|
||||
tail = tailLayout(canvas, (text, px) => measure(text, px));
|
||||
tail = tailLayout(canvas, (text, px, weight) => measure(text, px, weight));
|
||||
layoutKey = key;
|
||||
|
||||
rings.setAttribute('viewBox', `0 0 ${p.canvasW} ${p.canvasH}`);
|
||||
|
||||
+8
-3
@@ -15,6 +15,9 @@ const COUNTDOWN_TEMPLATE = '999d';
|
||||
/** Widest percent text the tail reserves room for. */
|
||||
const PCT_TEMPLATE = '100%';
|
||||
|
||||
/** Font weight of the tail percent; keep in step with `.tail-pct` in bubble.css. */
|
||||
export const TAIL_PCT_WEIGHT = 600;
|
||||
|
||||
/** Usage at or above this percent pulses. */
|
||||
export const PULSE_THRESHOLD_PCT = 95;
|
||||
|
||||
@@ -67,10 +70,11 @@ const PULSE_STRENGTH = 0.3;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Measures `text` set at `fontPx` (regular weight) in physical px.
|
||||
* Measures `text` set at `fontPx` and `weight` in physical px.
|
||||
* @callback MeasureText
|
||||
* @param {string} text Text to measure.
|
||||
* @param {number} fontPx Font size in physical px.
|
||||
* @param {number} [weight] CSS font weight; regular (400) when omitted.
|
||||
* @returns {number} Width in physical px.
|
||||
*/
|
||||
|
||||
@@ -174,7 +178,7 @@ export function headMetrics(canvas) {
|
||||
* their right-aligned texts. When the tail is too narrow, the text column
|
||||
* shrinks (possibly to zero) before the bars drop below their minimum.
|
||||
* @param {Canvas} canvas Window canvas.
|
||||
* @param {MeasureText} measure Regular-weight text measurement.
|
||||
* @param {MeasureText} measure Text measurement.
|
||||
* @returns {TailLayout} Tail geometry in physical px.
|
||||
*/
|
||||
export function tailLayout(canvas, measure) {
|
||||
@@ -186,7 +190,7 @@ export function tailLayout(canvas, measure) {
|
||||
const barTextGap = scale(8, dpi);
|
||||
|
||||
const countdownW = Math.ceil(measure(COUNTDOWN_TEMPLATE, fonts.main));
|
||||
const pctReserveW = Math.ceil(measure(PCT_TEMPLATE, fonts.small)) + scale(2, dpi);
|
||||
const pctReserveW = Math.ceil(measure(PCT_TEMPLATE, fonts.small, TAIL_PCT_WEIGHT)) + scale(2, dpi);
|
||||
|
||||
const usageBarH = clamp(idiv(h * 10, 100), scale(6, dpi), scale(12, dpi));
|
||||
const timeBarH = clamp(idiv(h * 4, 100), scale(3, dpi), scale(6, dpi));
|
||||
@@ -341,3 +345,4 @@ export function timeFillWidth(barW, fraction) {
|
||||
export function headLabelText(label, labelW, rectW) {
|
||||
return label === '' || labelW > rectW ? HEAD_LABEL_FALLBACK : label;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user