Files
aiturk-hermes-ide/ui-tui/src/components/petSprite.tsx
T

94 lines
2.7 KiB
TypeScript

import { Box, Text } from '@hermes/ink'
import { memo } from 'react'
// A cell is [tr,tg,tb,ta, br,bg,bb,ba] — the top + bottom pixel of one
// half-block, as produced by the `pet.cells` gateway RPC.
export type PetCell = number[]
export type PetGrid = PetCell[][]
const UPPER_HALF = '▀'
const LOWER_HALF = '▄'
const hex = (r: number, g: number, b: number) =>
`#${[r, g, b]
.map(v =>
Math.max(0, Math.min(255, v | 0))
.toString(16)
.padStart(2, '0')
)
.join('')}`
/**
* Renders one petdex frame as truecolor half-blocks using native Ink color
* props (no raw ANSI, so width measurement stays correct). The engine
* (`agent/pet/render.py`) does the decode + downscale; this is a thin painter.
*/
export const PetSprite = memo(function PetSprite({ grid }: { grid: PetGrid }) {
if (!grid.length) {
return null
}
return (
<Box flexDirection="column">
{grid.map((row, y) => (
<Box key={y}>
{row.map((cell, x) => {
const [tr, tg, tb, ta, br, bg, bb, ba] = cell
const top = (ta ?? 0) >= 32
const bot = (ba ?? 0) >= 32
if (!top && !bot) {
return <Text key={x}> </Text>
}
// Both halves opaque → fg=top over bg=bottom. One half opaque →
// draw it fg-only so the other stays the terminal bg (no black
// boxes bleeding around transparent sprite edges).
if (top && bot) {
return (
<Text backgroundColor={hex(br, bg, bb)} color={hex(tr, tg, tb)} key={x}>
{UPPER_HALF}
</Text>
)
}
return top ? (
<Text color={hex(tr, tg, tb)} key={x}>
{UPPER_HALF}
</Text>
) : (
<Text color={hex(br, bg, bb)} key={x}>
{LOWER_HALF}
</Text>
)
})}
</Box>
))}
</Box>
)
})
/**
* Renders a kitty Unicode-placeholder grid: each line is a row of U+10EEEE
* cells whose foreground color encodes the image id. The actual pixels are
* drawn by the terminal (the frame image is transmitted out-of-band by
* `usePet`); this only emits the placeholder text Ink can measure as width-1
* cells. Truecolor-only — the color must reach the terminal verbatim for the
* id to decode, which Ghostty/kitty support.
*/
export const PetKitty = memo(function PetKitty({ color, placeholder }: { color: string; placeholder: string[] }) {
if (!placeholder.length) {
return null
}
return (
<Box flexDirection="column">
{placeholder.map((row, y) => (
<Text color={color} key={y}>
{row}
</Text>
))}
</Box>
)
})