mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
The chip prefixes every row with the provider name, so a machine that only has Claude limits renders "CLAUDE 5H 60% 7D 23%" — a 46px label naming the only thing it could possibly be. The name exists to tell two rows apart, so it should only appear when there are two. updatePlanUsageChip() now checks whether both Claude and Codex actually have windows before building the rows, and emits the .pu-provider span only in that case. The tooltip keeps naming the provider in both cases: it has the room, and the chip no longer does. Verified in a browser on an isolated beta instance: Claude-only renders bare windows with no .pu-provider in the DOM, Codex-only the same, and the two-provider chip is byte-identical to before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
115 lines
4.4 KiB
TypeScript
115 lines
4.4 KiB
TypeScript
/** @fileoverview Header plan-usage chip provider rows (Claude above Codex). */
|
|
|
|
import { readFileSync } from 'node:fs';
|
|
import { performance } from 'node:perf_hooks';
|
|
import { resolve } from 'node:path';
|
|
import vm from 'node:vm';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
|
|
function loadCodemanAppClass() {
|
|
const constants = readFileSync(resolve(import.meta.dirname, '../src/web/public/constants.js'), 'utf8');
|
|
const source = readFileSync(resolve(import.meta.dirname, '../src/web/public/app.js'), 'utf8');
|
|
const chip = { innerHTML: '', title: '' };
|
|
const context = vm.createContext({
|
|
console,
|
|
performance,
|
|
setInterval: vi.fn(),
|
|
clearInterval: vi.fn(),
|
|
setTimeout,
|
|
clearTimeout,
|
|
requestAnimationFrame: vi.fn(),
|
|
HTMLCanvasElement: class HTMLCanvasElement {},
|
|
fetch: (...args: Parameters<typeof fetch>) => global.fetch(...args),
|
|
document: {
|
|
addEventListener: vi.fn(),
|
|
getElementById: (id: string) => (id === 'planUsageChip' ? chip : null),
|
|
},
|
|
localStorage: {
|
|
length: 0,
|
|
key: vi.fn(),
|
|
getItem: vi.fn(),
|
|
setItem: vi.fn(),
|
|
removeItem: vi.fn(),
|
|
},
|
|
window: { addEventListener: vi.fn(), removeEventListener: vi.fn() },
|
|
MobileDetection: {},
|
|
});
|
|
vm.runInContext(`${constants}\n${source}\nglobalThis.__CodemanApp = CodemanApp;`, context);
|
|
return {
|
|
CodemanApp: (context as { __CodemanApp: new () => unknown }).__CodemanApp,
|
|
chip,
|
|
};
|
|
}
|
|
|
|
type UsageApp = { updatePlanUsageChip: (data: unknown) => void };
|
|
|
|
describe('header plan usage chip', () => {
|
|
it('renders Claude first and the main Codex limits underneath', () => {
|
|
const { CodemanApp, chip } = loadCodemanAppClass();
|
|
const app = Object.create((CodemanApp as { prototype: object }).prototype) as UsageApp;
|
|
|
|
app.updatePlanUsageChip({
|
|
fiveHour: { usedPercentage: 97, resetAt: 1000 },
|
|
sevenDay: { usedPercentage: 44, resetAt: 2000 },
|
|
codex: { sevenDay: { usedPercentage: 40, resetAt: 3000 } },
|
|
});
|
|
|
|
expect(chip.innerHTML).toContain('class="pu-row"');
|
|
expect(chip.innerHTML).toContain('class="pu-provider">Claude</span>');
|
|
expect(chip.innerHTML).toContain('class="pu-provider">Codex</span>');
|
|
expect(chip.innerHTML.indexOf('Claude')).toBeLessThan(chip.innerHTML.indexOf('Codex'));
|
|
expect(chip.innerHTML).toContain('97%');
|
|
expect(chip.innerHTML).toContain('44%');
|
|
expect(chip.innerHTML).toContain('40%');
|
|
expect(chip.title).toContain('Claude plan usage');
|
|
expect(chip.title).toContain('Codex plan usage');
|
|
});
|
|
|
|
it('omits unavailable Codex windows instead of inventing zero usage', () => {
|
|
const { CodemanApp, chip } = loadCodemanAppClass();
|
|
const app = Object.create((CodemanApp as { prototype: object }).prototype) as UsageApp;
|
|
|
|
app.updatePlanUsageChip({
|
|
fiveHour: { usedPercentage: 10, resetAt: 1000 },
|
|
sevenDay: { usedPercentage: 20, resetAt: 2000 },
|
|
codex: { sevenDay: { usedPercentage: 40, resetAt: 3000 } },
|
|
});
|
|
|
|
const codexRow = chip.innerHTML.slice(chip.innerHTML.indexOf('Codex'));
|
|
expect(codexRow).not.toContain('5h');
|
|
expect(codexRow).toContain('7d');
|
|
});
|
|
|
|
it('drops the provider label when Claude is the only provider with limits', () => {
|
|
const { CodemanApp, chip } = loadCodemanAppClass();
|
|
const app = Object.create((CodemanApp as { prototype: object }).prototype) as UsageApp;
|
|
|
|
app.updatePlanUsageChip({
|
|
fiveHour: { usedPercentage: 60, resetAt: 1000 },
|
|
sevenDay: { usedPercentage: 23, resetAt: 2000 },
|
|
});
|
|
|
|
expect(chip.innerHTML).toContain('class="pu-row"');
|
|
expect(chip.innerHTML).not.toContain('pu-provider');
|
|
expect(chip.innerHTML).not.toContain('Claude');
|
|
expect(chip.innerHTML).toContain('60%');
|
|
expect(chip.innerHTML).toContain('23%');
|
|
// The tooltip still names the provider — it has room, and the chip no longer does.
|
|
expect(chip.title).toContain('Claude plan usage');
|
|
});
|
|
|
|
it('drops the provider label when Codex is the only provider with limits', () => {
|
|
const { CodemanApp, chip } = loadCodemanAppClass();
|
|
const app = Object.create((CodemanApp as { prototype: object }).prototype) as UsageApp;
|
|
|
|
app.updatePlanUsageChip({
|
|
codex: { fiveHour: { usedPercentage: 12, resetAt: 3000 } },
|
|
});
|
|
|
|
expect(chip.innerHTML).toContain('class="pu-row"');
|
|
expect(chip.innerHTML).not.toContain('pu-provider');
|
|
expect(chip.innerHTML).toContain('12%');
|
|
expect(chip.title).toContain('Codex plan usage');
|
|
});
|
|
});
|