Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/widgets/BlockResetTimer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import {
isUsageDateMode,
isUsageInverted,
isUsageProgressMode,
isUsageSliderMode,
makeSliderBar,
toggleUsageCompact,
toggleUsageDateMode,
toggleUsageHourFormat,
Expand Down Expand Up @@ -68,7 +70,7 @@ export class BlockResetTimerWidget implements Widget {

handleEditorAction(action: string, item: WidgetItem): WidgetItem | null {
if (action === 'toggle-progress') {
return cycleUsageDisplayMode(item, ['compact', 'absolute']);
return cycleUsageDisplayMode(item, ['compact', 'absolute'], true);
}

if (action === 'toggle-invert') {
Expand Down Expand Up @@ -105,6 +107,14 @@ export class BlockResetTimerWidget implements Widget {
return formatRawOrLabeledValue(item, 'Reset ', `[${progressBar}] ${previewPercent.toFixed(1)}%`);
}

if (isUsageSliderMode(displayMode)) {
const slider = makeSliderBar(previewPercent);
const sliderDisplay = displayMode === 'slider'
? `${slider} ${previewPercent.toFixed(1)}%`
: slider;
return formatRawOrLabeledValue(item, 'Reset ', sliderDisplay);
}

if (dateMode) {
const resetAt = formatUsageResetAt(
BLOCK_RESET_PREVIEW_AT,
Expand Down Expand Up @@ -138,6 +148,15 @@ export class BlockResetTimerWidget implements Widget {
return formatRawOrLabeledValue(item, 'Reset ', `[${progressBar}] ${percentage}%`);
}

if (isUsageSliderMode(displayMode)) {
const percent = inverted ? window.remainingPercent : window.elapsedPercent;
const slider = makeSliderBar(percent);
const sliderDisplay = displayMode === 'slider'
? `${slider} ${percent.toFixed(1)}%`
: slider;
return formatRawOrLabeledValue(item, 'Reset ', sliderDisplay);
}

if (dateMode) {
const timezone = getUsageTimezone(item);
const locale = getUsageLocale(item);
Expand Down
29 changes: 28 additions & 1 deletion src/widgets/BlockTimer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
isUsageCompact,
isUsageInverted,
isUsageProgressMode,
isUsageSliderMode,
makeSliderBar,
toggleUsageCompact,
toggleUsageInverted
} from './shared/usage-display';
Expand All @@ -47,7 +49,7 @@ export class BlockTimerWidget implements Widget {

handleEditorAction(action: string, item: WidgetItem): WidgetItem | null {
if (action === 'toggle-progress') {
return cycleUsageDisplayMode(item, ['compact']);
return cycleUsageDisplayMode(item, ['compact'], true);
}

if (action === 'toggle-invert') {
Expand Down Expand Up @@ -75,6 +77,14 @@ export class BlockTimerWidget implements Widget {
return formatRawOrLabeledValue(item, 'Block ', `[${progressBar}] ${previewPercent.toFixed(1)}%`);
}

if (isUsageSliderMode(displayMode)) {
const slider = makeSliderBar(previewPercent);
const sliderDisplay = displayMode === 'slider'
? `${slider} ${previewPercent.toFixed(1)}%`
: slider;
return formatRawOrLabeledValue(item, 'Block ', sliderDisplay);
}

return formatRawOrLabeledValue(item, 'Block: ', compact ? '3h45m' : '3hr 45m');
}

Expand All @@ -88,6 +98,14 @@ export class BlockTimerWidget implements Widget {
return formatRawOrLabeledValue(item, 'Block ', `[${emptyBar}] 0.0%`);
}

if (isUsageSliderMode(displayMode)) {
const emptySlider = makeSliderBar(0);
const sliderDisplay = displayMode === 'slider'
? `${emptySlider} 0.0%`
: emptySlider;
return formatRawOrLabeledValue(item, 'Block ', sliderDisplay);
}

return formatRawOrLabeledValue(item, 'Block: ', compact ? '0h' : '0hr 0m');
}

Expand All @@ -99,6 +117,15 @@ export class BlockTimerWidget implements Widget {
return formatRawOrLabeledValue(item, 'Block ', `[${progressBar}] ${percentage}%`);
}

if (isUsageSliderMode(displayMode)) {
const percent = inverted ? window.remainingPercent : window.elapsedPercent;
const slider = makeSliderBar(percent);
const sliderDisplay = displayMode === 'slider'
? `${slider} ${percent.toFixed(1)}%`
: slider;
return formatRawOrLabeledValue(item, 'Block ', sliderDisplay);
}

const elapsedTime = formatUsageDuration(window.elapsedMs, compact);
return formatRawOrLabeledValue(item, 'Block: ', elapsedTime);
}
Expand Down
36 changes: 31 additions & 5 deletions src/widgets/WeeklyResetTimer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import {
isUsageDateMode,
isUsageInverted,
isUsageProgressMode,
isUsageSliderMode,
makeSliderBar,
toggleUsageCompact,
toggleUsageDateMode,
toggleUsageHourFormat,
Expand Down Expand Up @@ -71,19 +73,24 @@ function toggleWeeklyResetHoursOnly(item: WidgetItem): WidgetItem {
function getWeeklyResetModifierText(item: WidgetItem): string | undefined {
const displayMode = getUsageDisplayMode(item);
const dateMode = isUsageDateMode(item);
const isBarMode = isUsageProgressMode(displayMode) || isUsageSliderMode(displayMode);
const modifiers: string[] = [];

if (displayMode === 'progress') {
modifiers.push('long bar');
} else if (displayMode === 'progress-short') {
modifiers.push('medium bar');
} else if (displayMode === 'slider') {
modifiers.push('short bar');
} else if (displayMode === 'slider-only') {
modifiers.push('short bar only');
}

if (isUsageInverted(item)) {
modifiers.push('inverted');
}

if (!isUsageProgressMode(displayMode)) {
if (!isBarMode) {
if (isUsageCompact(item)) {
modifiers.push('compact');
}
Expand All @@ -100,12 +107,12 @@ function getWeeklyResetModifierText(item: WidgetItem): string | undefined {
}

const timezoneModifier = getUsageTimezoneModifier(item);
if (!isUsageProgressMode(displayMode) && dateMode && timezoneModifier) {
if (!isBarMode && dateMode && timezoneModifier) {
modifiers.push(timezoneModifier);
}

const localeModifier = getUsageLocaleModifier(item);
if (!isUsageProgressMode(displayMode) && dateMode && localeModifier) {
if (!isBarMode && dateMode && localeModifier) {
modifiers.push(localeModifier);
}

Expand All @@ -127,7 +134,7 @@ export class WeeklyResetTimerWidget implements Widget {

handleEditorAction(action: string, item: WidgetItem): WidgetItem | null {
if (action === 'toggle-progress') {
return cycleUsageDisplayMode(item, ['compact', 'hours', 'absolute']);
return cycleUsageDisplayMode(item, ['compact', 'hours', 'absolute'], true);
}

if (action === 'toggle-invert') {
Expand Down Expand Up @@ -169,6 +176,14 @@ export class WeeklyResetTimerWidget implements Widget {
return formatRawOrLabeledValue(item, 'Weekly Reset ', `[${progressBar}] ${previewPercent.toFixed(1)}%`);
}

if (isUsageSliderMode(displayMode)) {
const slider = makeSliderBar(previewPercent);
const sliderDisplay = displayMode === 'slider'
? `${slider} ${previewPercent.toFixed(1)}%`
: slider;
return formatRawOrLabeledValue(item, 'Weekly Reset ', sliderDisplay);
}

if (dateMode) {
const resetAt = formatUsageResetAt(
WEEKLY_RESET_PREVIEW_AT,
Expand Down Expand Up @@ -202,6 +217,15 @@ export class WeeklyResetTimerWidget implements Widget {
return formatRawOrLabeledValue(item, 'Weekly Reset ', `[${progressBar}] ${percentage}%`);
}

if (isUsageSliderMode(displayMode)) {
const percent = inverted ? window.remainingPercent : window.elapsedPercent;
const slider = makeSliderBar(percent);
const sliderDisplay = displayMode === 'slider'
? `${slider} ${percent.toFixed(1)}%`
: slider;
return formatRawOrLabeledValue(item, 'Weekly Reset ', sliderDisplay);
}

if (dateMode) {
const timezone = getUsageTimezone(item);
const locale = getUsageLocale(item);
Expand All @@ -223,7 +247,9 @@ export class WeeklyResetTimerWidget implements Widget {
includeTimezone: true
});

if (!item || (!isUsageProgressMode(getUsageDisplayMode(item)) && !isUsageDateMode(item))) {
const mode = item ? getUsageDisplayMode(item) : 'time';
const isBarMode = isUsageProgressMode(mode) || isUsageSliderMode(mode);
if (!item || (!isBarMode && !isUsageDateMode(item))) {
keybinds.push({ key: 'h', label: '(h)ours only', action: 'toggle-hours' });
}

Expand Down
86 changes: 86 additions & 0 deletions src/widgets/__tests__/BlockResetTimer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,91 @@ describe('BlockResetTimerWidget', () => {
expect(cleared?.metadata?.hour12).toBe('false');
});

it('renders slider bar with elapsed percentage', () => {
const widget = new BlockResetTimerWidget();
const item: WidgetItem = {
id: 'reset',
type: 'reset-timer',
metadata: { display: 'slider' }
};

mockResolveUsageWindowWithFallback.mockReturnValue({
sessionDurationMs: 18000000,
elapsedMs: 9000000,
remainingMs: 9000000,
elapsedPercent: 50,
remainingPercent: 50
});

expect(render(widget, item, { usageData: {} })).toBe('Reset ▓▓▓▓▓░░░░░ 50.0%');
});

it('renders slider-only bar without percentage', () => {
const widget = new BlockResetTimerWidget();
const item: WidgetItem = {
id: 'reset',
type: 'reset-timer',
metadata: { display: 'slider-only' }
};

mockResolveUsageWindowWithFallback.mockReturnValue({
sessionDurationMs: 18000000,
elapsedMs: 9000000,
remainingMs: 9000000,
elapsedPercent: 50,
remainingPercent: 50
});

expect(render(widget, item, { usageData: {} })).toBe('Reset ▓▓▓▓▓░░░░░');
});

it('renders inverted slider using remaining percent', () => {
const widget = new BlockResetTimerWidget();
const item: WidgetItem = {
id: 'reset',
type: 'reset-timer',
metadata: { display: 'slider', invert: 'true' }
};

mockResolveUsageWindowWithFallback.mockReturnValue({
sessionDurationMs: 18000000,
elapsedMs: 14400000,
remainingMs: 3600000,
elapsedPercent: 80,
remainingPercent: 20
});

expect(render(widget, item, { usageData: {} })).toBe('Reset ▓▓░░░░░░░░ 20.0%');
});

it('exposes invert keybind in slider mode', () => {
const widget = new BlockResetTimerWidget();

expect(widget.getCustomKeybinds({
id: 'reset',
type: 'reset-timer',
metadata: { display: 'slider' }
})).toEqual([
{ key: 'p', label: '(p)rogress toggle', action: 'toggle-progress' },
{ key: 'v', label: 'in(v)ert fill', action: 'toggle-invert' }
]);
});

it('shows short bar modifier text in slider modes', () => {
const widget = new BlockResetTimerWidget();

expect(widget.getEditorDisplay({
id: 'reset',
type: 'reset-timer',
metadata: { display: 'slider' }
}).modifierText).toBe('(short bar)');
expect(widget.getEditorDisplay({
id: 'reset',
type: 'reset-timer',
metadata: { display: 'slider-only' }
}).modifierText).toBe('(short bar only)');
});

runUsageTimerEditorSuite({
baseItem: { id: 'reset', type: 'reset-timer' },
createWidget: () => new BlockResetTimerWidget(),
Expand All @@ -189,6 +274,7 @@ describe('BlockResetTimerWidget', () => {
{ key: 't', label: '(t)imestamp', action: 'toggle-date' }
],
supportsDateMode: true,
supportsSliderMode: true,
expectedModifierText: '(medium bar, inverted)',
expectedProgressKeybinds: [
{ key: 'p', label: '(p)rogress toggle', action: 'toggle-progress' },
Expand Down
Loading