Skip to content
Merged
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
1 change: 1 addition & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ When reviewing code, use the Before/After markdown table format from the skill.
- [x] **Single-source `TOTAL_COMBOS`.** Before: 15 files hardcoded `9,856` → `10,752`; shipping spikes meant editing all of them. Now `TOTAL_COMBOS_LABEL` in `@/lib/constants` drives the in-code consumers; static files (MDX / READMEs / openapi.json) still hardcode but `pnpm check-combo-count` (CI step) fails if they drift from `totalCombinations()`. (PR #162)
- [x] **Browse 429s.** `/browse` fires ~60 animated renders on first paint. Animated rate limit was 30/min per IP — 30 succeed, the rest break. Added `isSameOrigin(request)` that checks `Sec-Fetch-Site: same-origin` to exempt our own UI. External consumers still capped, but caps bumped to 120/min animated + 60/min OG. (PR #170)
- [x] **Cache header revalidation.** `Cache-Control: immutable` was wrong once PR #156 made same-ID sprite art mutable. Swapped for 1-day fresh + 7-day stale-while-revalidate — art updates propagate within ~1 day without a manual dashboard purge. Manual purge still works for urgent rollout. (PR #166, #168, #169)
- [x] **Animated `cheeky-terminal` (16-frame sequence) + `terminal-round` + `glasses` (blink).** Upgraded three previously-static eye parts to animated. `cheeky-terminal` is the first 16-frame sequence (fills one full super-loop). `ANIM_VERSION` 1→2 to bust CDN caches. Parts.ts docstring updated to include N=16 as valid sequence length.

## Improve GIFs / Animated API

Expand Down
Binary file modified app/public/parts/eyes/cheeky-terminal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/parts/eyes/glasses.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/parts/eyes/terminal-round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added art/png/eyes/glasses/glasses-closed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/core/src/animation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type { PartOption } from './parts'
* output too and require a Vercel CDN purge on deploy, not a version
* bump here.
*/
export const ANIM_VERSION = 1
export const ANIM_VERSION = 2

export interface AnimFrame {
top: number
Expand Down
17 changes: 9 additions & 8 deletions packages/core/src/parts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ export type PartCategory = 'eyes' | 'heads' | 'body' | 'top'

/**
* Per-part sub-animation kind. Selects which frame to show on each tick
* of the 8-frame idle bounce.
* of the 16-tick idle super-loop.
*
* - `static`: always frame 0. Default when `frames` is 1 or omitted.
* - `blink`: 2-frame sprite sheet ordered [open, closed]. Tick schedule
* `[0,1,0,1,0,0,0,0]` — two blinks then hold-open.
* - `blink`: 2-frame sprite sheet ordered [open, closed]. Driven by
* `BLINK_SCHEDULE` in animation.ts.
* - `sequence`: N-frame sheet played in order. Advances one frame per tick
* and loops to fit the 8-tick bounce (N should divide 8: 1/2/4/8).
* and loops inside the 16-tick super-loop (N should divide
* LOOP_LENGTH: 1/2/4/8/16).
*
* Add new kinds here as new named patterns become useful.
*/
Expand All @@ -29,7 +30,7 @@ export interface PartOption {
/**
* Number of horizontal frames in the sprite sheet (sheet width = frames × 32).
* Defaults to 1 (single 32×32 sprite). For blink: 2. For sequence: N where
* N ∈ {1, 2, 4, 8} so it fits the 8-tick canonical bounce loop.
* N ∈ {1, 2, 4, 8, 16} so it fits the 16-tick super-loop.
*/
frames?: number
/** Sub-animation kind (see PartAnimKind). Defaults to 'static'. */
Expand Down Expand Up @@ -57,8 +58,8 @@ function makeParts(category: PartCategory, entries: PartInput[]): PartOption[] {
export const PARTS: Record<PartCategory, PartOption[]> = {
eyes: makeParts('eyes', [
'big-face',
'cheeky-terminal',
'glasses',
{ name: 'cheeky-terminal', frames: 16, kind: 'sequence' },
{ name: 'glasses', frames: 2, kind: 'blink' },
{ name: 'human', frames: 2, kind: 'blink' },
{ name: 'human-2', frames: 2, kind: 'blink' },
'monitor',
Expand All @@ -67,7 +68,7 @@ export const PARTS: Record<PartCategory, PartOption[]> = {
{ name: 'terminal', frames: 2, kind: 'blink' },
{ name: 'terminal-green', frames: 2, kind: 'blink' },
'terminal-light',
'terminal-round',
{ name: 'terminal-round', frames: 2, kind: 'blink' },
{ name: 'tight-visor', frames: 8, kind: 'sequence' },
{ name: 'visor', frames: 8, kind: 'sequence' },
{ name: 'wayfarer', frames: 4, kind: 'sequence' },
Expand Down
Loading