refactor: statusbar、companion、herdr 外掛改名加上 jsc- 前綴
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
f10b168c28
commit
58e2b2d26a
@@ -0,0 +1,114 @@
|
||||
import { describe, expect, test } from 'claude-code/testing'
|
||||
|
||||
import type { HudData } from './format'
|
||||
import { bar, day, clock, fit, lineWidth, parseCpu, cpuPercent, parseDf, parseGit, parseMem, parseOffset, prettyModel, shortDir, tokens } from './format'
|
||||
|
||||
const RESET_5H = '2026-10-12T07:40:00Z'
|
||||
const RESET_7D = '2026-10-16T01:00:00Z'
|
||||
|
||||
const DATA: HudData = {
|
||||
model: 'claude-opus-5-5[1m]',
|
||||
cwd: '/home/dev/work/luopan',
|
||||
home: '/home/dev',
|
||||
git: { branch: 'issue-278', dirty: 3, ahead: 1, behind: 0 },
|
||||
usage: {
|
||||
fiveHour: { percent: 42, resetsAt: RESET_5H },
|
||||
sevenDay: { percent: 71, resetsAt: RESET_7D },
|
||||
ctxTokens: 312_000,
|
||||
ctxWindow: 1_000_000,
|
||||
ctxPercent: 31,
|
||||
},
|
||||
sys: { cpu: 23, memUsed: 63, memTotal: 100, disk: 78 },
|
||||
tzOffsetMin: 480,
|
||||
effort: 'high',
|
||||
}
|
||||
|
||||
const text = (d: HudData, columns: number, compact = false) => fit(d, columns, compact).map(s => s.text).join('')
|
||||
|
||||
describe('format', () => {
|
||||
test('wide line shows every field on one line', async () => {
|
||||
const line = text(DATA, 200)
|
||||
expect(line).toBe(
|
||||
'◆ Opus 5.5 ·high │ ~/work/luopan │ ⎇ issue-278 ±3 ↑1 │ 5h ▰▰▱▱▱ 42% ↻15:40 │ 7d ▰▰▰▰▱ 71% ↻10/16 │ ctx 31% 312k/1.0M │ CPU 23% MEM 63% DSK 78%',
|
||||
)
|
||||
expect(line.includes('\n')).toBe(false)
|
||||
})
|
||||
|
||||
test('narrow widths collapse and always fit', async () => {
|
||||
for (const cols of [140, 120, 100, 90, 80, 60]) {
|
||||
const segs = fit(DATA, cols)
|
||||
expect(lineWidth(segs) <= cols || cols < 50).toBe(true)
|
||||
}
|
||||
expect(text(DATA, 40)).toBe('◆ Opus ⎇issue-278 5h42 7d71 ctx31 cpu23')
|
||||
expect(text(DATA, 200, true)).toBe('◆ Opus ⎇issue-278 5h42 7d71 ctx31 cpu23')
|
||||
})
|
||||
|
||||
test('missing figures show a dash', async () => {
|
||||
const line = text({ ...DATA, git: null, usage: {}, sys: {}, effort: null }, 200)
|
||||
expect(line).toBe('◆ Opus 5.5 │ ~/work/luopan │ 5h — │ 7d — │ ctx —')
|
||||
})
|
||||
|
||||
test('helpers', async () => {
|
||||
expect(bar(0)).toBe('▱▱▱▱▱')
|
||||
expect(bar(100)).toBe('▰▰▰▰▰')
|
||||
expect(prettyModel('claude-sonnet-5-5')).toBe('Sonnet 5.5')
|
||||
expect(prettyModel('Opus 5.5')).toBe('Opus 5.5')
|
||||
expect(tokens(950)).toBe('950')
|
||||
expect(tokens(200_000)).toBe('200k')
|
||||
expect(clock(RESET_5H, 480)).toBe('15:40')
|
||||
expect(day(RESET_7D, 480)).toBe('10/16')
|
||||
expect(day('2026-10-15T20:00:00Z', 480)).toBe('10/16')
|
||||
expect(parseOffset('+0800\n')).toBe(480)
|
||||
expect(parseOffset('-0530')).toBe(-330)
|
||||
expect(shortDir('/srv/x/y', '/home/dev', true)).toBe('…/y')
|
||||
expect(shortDir('/home/dev', '/home/dev', false)).toBe('~')
|
||||
})
|
||||
|
||||
test('model tiers', async () => {
|
||||
const head = (model: string) => fit({ ...DATA, model }, 200)[0]
|
||||
expect(head('claude-haiku-5-5')).toEqual({ text: '○ Haiku 5.5', color: '#7f9bb3', bold: true })
|
||||
expect(head('claude-sonnet-5-5')).toEqual({ text: '◇ Sonnet 5.5', color: '#4f9ee8', bold: true })
|
||||
expect(head('claude-opus-5-5')).toEqual({ text: '◆ Opus 5.5', color: 'claude', bold: true })
|
||||
expect(head('claude-fable-5-1')).toEqual({ text: '✦ Fable 5.1', color: '#a77bf0', bold: true })
|
||||
expect(head('some-other-model')).toEqual({ text: '◆ some-other-model', color: undefined, bold: true })
|
||||
expect(text({ ...DATA, model: 'claude-fable-5-1' }, 40)).toBe('✦ Fable ⎇issue-278 5h42 7d71 ctx31 cpu23')
|
||||
})
|
||||
|
||||
test('parsers', async () => {
|
||||
expect(parseGit('# branch.oid abc1234ff\n# branch.head main\n# branch.ab +2 -1\n1 .M N... a\n? new.txt\n')).toEqual({
|
||||
branch: 'main',
|
||||
dirty: 2,
|
||||
ahead: 2,
|
||||
behind: 1,
|
||||
})
|
||||
expect(parseGit('# branch.oid abc1234ff\n# branch.head (detached)\n')?.branch).toBe('abc1234')
|
||||
const a = parseCpu('cpu 100 0 100 700 100 0 0 0 0 0\ncpu0 1')
|
||||
const b = parseCpu('cpu 150 0 150 750 150 0 0 0 0 0\n')
|
||||
expect(a && b && cpuPercent(a, b)).toBe(50)
|
||||
expect(parseMem('MemTotal: 1000 kB\nMemFree: 1 kB\nMemAvailable: 250 kB\n')).toEqual({ used: 768000, total: 1024000 })
|
||||
expect(parseDf('Filesystem 1024-blocks Used Available Capacity Mounted on\n/dev/sdc 100 78 22 78% /\n')).toBe(78)
|
||||
})
|
||||
})
|
||||
|
||||
describe('band', () => {
|
||||
test('draws one line above the prompt and hides with /hud off', async ($, on) => {
|
||||
on('session.model', () => ({ value: 'claude-opus-5-5' }))
|
||||
on('session.cwd', () => ({ value: '/home/dev/work/luopan' }))
|
||||
const props = {
|
||||
hasSurvey: false,
|
||||
isWorking: false,
|
||||
maxRows: 10,
|
||||
bodyColumns: 200,
|
||||
scroll: { offset: 0, bodyRows: 10 },
|
||||
view: {},
|
||||
}
|
||||
for (const surface of ['terminal', 'desktop'] as const) {
|
||||
const ui = await $.ui.mount({ plugin: 'hud', surface, component: 'AbovePrompt', props })
|
||||
expect(await ui.find({ type: 'Text', text: /◆ Opus 5\.5/ })).toBeDefined()
|
||||
expect(await ui.find({ type: 'Text', text: /5h —/ })).toBeDefined()
|
||||
await ui.unmount()
|
||||
}
|
||||
const ran = await $.command.run({ command: 'hud', args: 'off' } as Parameters<typeof $.command.run>[0])
|
||||
expect(JSON.stringify(ran)).toContain('隱藏')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user