The visualizer's PCM cache decoded the entire episode into RAM (22050 Hz mono s16 ~160 MB/hr of audio) and held it until stop() — a 3-hour episode pinned ~500 MB and long-form content hit 2.5 GB. The 4x decode also pulled the whole remote file even when only minutes were listened to. - audio-pcm-cache: sliding window around the playback position — the decode head caps at maxAheadSec (600s) ahead of the cursor, segments older than keepBehindSec (300s) are pruned, and the tail refills as playback advances. Steady state ~40 MB regardless of episode length; a backward seek past the window restarts a segment there (the existing seek-hole mechanism, no new failure mode). - feed: cap the full-parse episode cache at 1000 episodes/feed so archive-heavy subscriptions can't pin their entire history in RAM; the visible list stays bounded by the user's cache preference and fetch-more keeps working within the ceiling. - tests: pin the new head-cap and prune contracts (8/8 in audio-pcm-cache.test.ts; full suite 193 pass). Also includes the in-flight cleanup/refactor pass (cover-art resolve helper, page and comment tightening, ESLint config removal).
3.6 KiB
3.6 KiB
AGENTS.md
Build, Lint, and Test Commands
Development
bun start- Run the applicationbun run dev- Run with hot reload (watch mode)
Build
bun run build- Build JavaScript bundle todist/bun run build:native- Build native libraries (requiresscripts/build-cavacore.sh)
Testing
bun test- Run all testsbun tests/cavacore-smoke.ts- Run specific native library smoke test
Linting
bun run lint- Run the TypeScript typecheck (bun tsc --noEmit)
Code Style Guidelines
TypeScript Configuration
- Target: ESNext with bundler module resolution
- Strict mode enabled
- Path alias:
@/*maps tosrc/* - JSX: Use
@opentui/solidas import source
Import Organization
- Third-party framework imports (solid-js, @opentui/solid)
- Local utility imports
- Type imports (separate from value imports)
Naming Conventions
- Components: PascalCase (e.g.,
FeedPage,Player) - Hooks:
use*prefix (e.g.,useAudio,useAppKeyboard) - Stores:
create*factory +use*accessor (e.g.,createFeedStore,useFeedStore) - Utilities: camelCase (e.g.,
parseRSSFeed,detectPlayers) - Constants: UPPER_SNAKE_CASE (e.g.,
MAX_EPISODES_REFRESH) - Types/interfaces: PascalCase (e.g.,
Feed,AudioBackend) - Enums: PascalCase (e.g.,
FeedVisibility)
Code Structure
- Section Dividers: Use
// ── Section Name ────────────────────────────────────────────────────────────format - Helper Functions: Define before main logic
- Factory Functions: Use for store creation (return object with state, computed, actions)
- Singleton Pattern: Stores use module-level singleton with
use*accessor
Type Definitions
- Use
interfacefor object shapes - Use
typefor unions, intersections, and complex types - Use
enumfor constant sets - Export types from
src/types/directory - Include JSDoc comments for complex types
Error Handling
- Use
try/catchfor async operations - For expected failures, use
.catch(() => {})to suppress errors - Return default values on failure (e.g.,
return []orreturn null) - Use
catchblocks with descriptive comments for unexpected errors - For UI components, wrap in
ErrorBoundarywith clear fallback
Async Patterns
- Fire-and-forget async operations:
.catch(() => {})with comment - Async store initialization: IIFE
(async () => { ... })() - Promise handling: Use
.catch()to return defaults
Comments
- File headers: Brief description of file purpose
- Complex functions: JSDoc-style comments explaining behavior
- Section dividers: Visual separators for code organization
- Inline comments: Explain non-obvious logic, especially async patterns
Code Formatting
- 2-space indentation
- No semicolons (Bun style)
- Arrow functions with implicit return where appropriate
- Object shorthand where possible
- Prefer
constoverlet
Reactivity (Solid.js)
- Use
createSignalfor primitive state - Use
createMemofor computed values - Use
createEffectfor side effects - Component functions return JSX
- Store functions return plain objects with state, computed, and actions
Persistence
- Async persistence operations fire-and-forget
- Use
.catch(() => {})to suppress errors - Update state synchronously, persist asynchronously
- Use
setFeeds()pattern to update state and trigger save
Testing
- Test files in
tests/directory - Use Bun's test framework
- Include JSDoc comments explaining test purpose
- Native library tests use
bun:ffifor FFI calls