Skip to content
Closed
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
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"name": "claude-code-best",
"name": "@rainhole/rain-code",
"version": "1.1.0",
"description": "Reverse-engineered Anthropic Claude Code CLI — interactive AI coding assistant in the terminal",
"type": "module",
"author": "claude-code-best <claude-code-best@proton.me>",
"author": "rainhole",
"repository": {
"type": "git",
"url": "git+https://github.com/claude-code-best/claude-code.git"
"url": "git+https://github.com/rainhotel/claude-code-runable.git"
},
"homepage": "https://github.com/claude-code-best/claude-code#readme",
"homepage": "https://github.com/rainhotel/claude-code-runable#readme",
"bugs": {
"url": "https://github.com/claude-code-best/claude-code/issues"
"url": "https://github.com/rainhotel/claude-code-runable/issues"
},
"keywords": [
"claude",
Expand All @@ -25,9 +25,13 @@
"bun": ">=1.2.0"
},
"bin": {
"rain-code": "dist/cli.js",
"ccb": "dist/cli.js",
"claude-code-best": "dist/cli.js"
},
"publishConfig": {
"access": "public"
},
"workspaces": [
"packages/*",
"packages/@ant/*"
Expand Down
107 changes: 25 additions & 82 deletions src/components/LogoV2/AnimatedClawd.tsx
Original file line number Diff line number Diff line change
@@ -1,96 +1,39 @@
import * as React from 'react'
import { useEffect, useRef, useState } from 'react'
import { Box } from '@anthropic/ink'
import { getInitialSettings } from '../../utils/settings/settings.js'
import { Clawd, type ClawdPose } from './Clawd.js'
import * as React from 'react';
import { useEffect, useState } from 'react';
import { Box } from '@anthropic/ink';
import { getInitialSettings } from '../../utils/settings/settings.js';
import { Clawd, RAINCODE_SCENE_HEIGHT, type ClawdPose } from './Clawd.js';

type Frame = { pose: ClawdPose; offset: number }

/** Hold a pose for n frames (60ms each). */
function hold(pose: ClawdPose, offset: number, frames: number): Frame[] {
return Array.from({ length: frames }, () => ({ pose, offset }))
}

// Offset semantics: marginTop in a fixed-height-3 container. 0 = normal,
// 1 = crouched. Container height stays 3 so the layout never shifts; during
// a crouch (offset=1) Clawd's feet row dips below the container and gets
// clipped — reads as "ducking below the frame" before springing back up.

// Click animation: crouch, then spring up with both arms raised. Twice.
const JUMP_WAVE: readonly Frame[] = [
...hold('default', 1, 2), // crouch
...hold('arms-up', 0, 3), // spring!
...hold('default', 0, 1),
...hold('default', 1, 2), // crouch again
...hold('arms-up', 0, 3), // spring!
...hold('default', 0, 1),
]

// Click animation: glance right, then left, then back.
const LOOK_AROUND: readonly Frame[] = [
...hold('look-right', 0, 5),
...hold('look-left', 0, 5),
...hold('default', 0, 1),
]

const CLICK_ANIMATIONS: readonly (readonly Frame[])[] = [JUMP_WAVE, LOOK_AROUND]

const IDLE: Frame = { pose: 'default', offset: 0 }
const FRAME_MS = 60
const incrementFrame = (i: number) => i + 1
const CLAWD_HEIGHT = 3
const FRAMES: readonly ClawdPose[] = ['default', 'look-left', 'default', 'look-right', 'default', 'arms-up'];
const FRAME_MS = 280;

/**
* Clawd with click-triggered animations (crouch-jump with arms up, or
* look-around). Container height is fixed at CLAWD_HEIGHT — same footprint
* as a bare `<Clawd />` — so the surrounding layout never shifts. During a
* crouch only the feet row clips (see comment above). Click only fires when
* mouse tracking is enabled (i.e. inside `<AlternateScreen>` / fullscreen);
* elsewhere this renders and behaves identically to plain `<Clawd />`.
* A lightweight ambient animation for the startup scene. The scene gently
* cycles rain positions and a soft solar pulse so the header feels alive
* without needing a click target.
*/
export function AnimatedClawd(): React.ReactNode {
const { pose, bounceOffset, onClick } = useClawdAnimation()
const pose = useClawdAnimation();
return (
<Box height={CLAWD_HEIGHT} flexDirection="column" onClick={onClick}>
<Box marginTop={bounceOffset} flexShrink={0}>
<Box height={RAINCODE_SCENE_HEIGHT} flexDirection="column">
<Box flexShrink={0}>
<Clawd pose={pose} />
</Box>
</Box>
)
);
}

function useClawdAnimation(): {
pose: ClawdPose
bounceOffset: number
onClick: () => void
} {
// Read once at mount — no useSettings() subscription, since that would
// re-render on any settings change.
const [reducedMotion] = useState(
() => getInitialSettings().prefersReducedMotion ?? false,
)
const [frameIndex, setFrameIndex] = useState(-1)
const sequenceRef = useRef<readonly Frame[]>(JUMP_WAVE)

const onClick = () => {
if (reducedMotion || frameIndex !== -1) return
sequenceRef.current =
CLICK_ANIMATIONS[Math.floor(Math.random() * CLICK_ANIMATIONS.length)]!
setFrameIndex(0)
}
function useClawdAnimation(): ClawdPose {
const [reducedMotion] = useState(() => getInitialSettings().prefersReducedMotion ?? false);
const [frameIndex, setFrameIndex] = useState(0);

useEffect(() => {
if (frameIndex === -1) return
if (frameIndex >= sequenceRef.current.length) {
setFrameIndex(-1)
return
}
const timer = setTimeout(setFrameIndex, FRAME_MS, incrementFrame)
return () => clearTimeout(timer)
}, [frameIndex])

const seq = sequenceRef.current
const current =
frameIndex >= 0 && frameIndex < seq.length ? seq[frameIndex]! : IDLE
return { pose: current.pose, bounceOffset: current.offset, onClick }
if (reducedMotion) return;
const timer = setInterval(() => {
setFrameIndex(current => (current + 1) % FRAMES.length);
}, FRAME_MS);
return () => clearInterval(timer);
}, [reducedMotion]);

return reducedMotion ? 'default' : (FRAMES[frameIndex] ?? 'default');
}
Loading
Loading