feat: M1 companion 資料層與 band 角色行
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,10 @@ import { atom, read, update } from 'claude-code'
|
||||
import type { EngineInterface, Register, SessionContextUsage, SessionRateLimit } from 'claude-code'
|
||||
|
||||
import type { Mode, Sys, Usage } from '../types'
|
||||
import { cpuPercent, fit, parseCpu, parseDf, parseGit, parseMem, parseOffset } from './format'
|
||||
import { fitCompanion } from './companion/band'
|
||||
import { createCompanion } from './companion/core'
|
||||
import { nodeFs } from './companion/node-fs'
|
||||
import { bandLines, cpuPercent, parseCpu, parseDf, parseGit, parseMem, parseOffset } from './format'
|
||||
import type { CpuSample } from './format'
|
||||
|
||||
const usageAtom = atom({ plugin: 'hud', key: 'usage' } as const, {})
|
||||
@@ -34,6 +37,7 @@ function toUsage(context: SessionContextUsage, rateLimits: SessionRateLimit[]):
|
||||
}
|
||||
|
||||
let home: string | undefined
|
||||
let companion: ReturnType<typeof createCompanion> | undefined
|
||||
let tzOffsetMin = 0
|
||||
let prevCpu: CpuSample | null = null
|
||||
let ticks = 0
|
||||
@@ -90,6 +94,14 @@ export const register: Register = on => {
|
||||
argumentHint: 'on | off | compact',
|
||||
})
|
||||
home = await $.env.get('HOME')
|
||||
companion = createCompanion({ fs: nodeFs, clock: { now: Date.now }, root: `${home ?? ''}/.cockpit` })
|
||||
await $.command.register({
|
||||
name: 'cockpit',
|
||||
description: '助理與儀表:status、switch、companion off、setup',
|
||||
argumentHint: 'status | switch <角色> | companion off | setup',
|
||||
})
|
||||
const first = await companion.ensureActivated()
|
||||
if (first.first) $.ui.toast(`已啟用 ${first.name}。用 /cockpit companion off 可關閉`)
|
||||
try {
|
||||
const r = await $.process.run(['date', '+%z'], { timeoutMs: 3000 })
|
||||
tzOffsetMin = parseOffset(r.stdout)
|
||||
@@ -137,36 +149,39 @@ export const register: Register = on => {
|
||||
return { text: said[mode] }
|
||||
})
|
||||
|
||||
on('command.run', { command: 'cockpit' }, async (_$, e) => ({ text: companion ? await companion.command(e.args) : '尚未就緒。' }))
|
||||
|
||||
on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
|
||||
const mode = await read($, modeAtom)
|
||||
if (e.props.hasSurvey || mode === 'off') return next(e)
|
||||
|
||||
const line = fit(
|
||||
{
|
||||
model: await $.session.model(),
|
||||
cwd: await $.session.cwd(),
|
||||
home,
|
||||
git: await read($, gitAtom),
|
||||
usage: await read($, usageAtom),
|
||||
sys: await read($, sysAtom),
|
||||
tzOffsetMin,
|
||||
effort: await read($, effortAtom),
|
||||
},
|
||||
e.props.bodyColumns,
|
||||
mode === 'compact',
|
||||
)
|
||||
const data = {
|
||||
model: await $.session.model(),
|
||||
cwd: await $.session.cwd(),
|
||||
home,
|
||||
git: await read($, gitAtom),
|
||||
usage: await read($, usageAtom),
|
||||
sys: await read($, sysAtom),
|
||||
tzOffsetMin,
|
||||
effort: await read($, effortAtom),
|
||||
}
|
||||
const view = await companion?.view()
|
||||
const cols = e.props.bodyColumns
|
||||
const lines = bandLines(data, cols, mode === 'compact', view ? fitCompanion(view, cols, mode === 'compact') : null)
|
||||
const { Box, Text } = $.ui.resolve(e)
|
||||
|
||||
// 引擎在 band 與輸入框之間已留一行空白,上方補一行讓上下間距相同
|
||||
return (
|
||||
<Box marginTop={1}>
|
||||
<Text wrap="truncate">
|
||||
{line.map(s => (
|
||||
<Text color={s.color} dimColor={s.dim} bold={s.bold}>
|
||||
{s.text}
|
||||
</Text>
|
||||
))}
|
||||
</Text>
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
{lines.map(line => (
|
||||
<Text wrap="truncate">
|
||||
{line.map(s => (
|
||||
<Text color={s.color} dimColor={s.dim} bold={s.bold}>
|
||||
{s.text}
|
||||
</Text>
|
||||
))}
|
||||
</Text>
|
||||
))}
|
||||
</Box>
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user