BMO: a living desktop buddy with a Claude brain

Standalone React + Vite app where BMO lives in a little garden:
- Redrawn avatar (src/avatar) with depth, cartoon controls and expressive
  faces; original bmo-preview avatar and scenery kept in src/bmo
- Idle life: wandering, chatter, boredom, sleep, pokes, weather and
  time-of-day reactions
- Chat via a local Node server streaming Claude replies (Haiku by default),
  with live scene awareness and tools to change color, outfit, sky,
  weather, move and perform
- Side menu for API key, appearance, environment and chat log

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 23:05:50 -04:00
commit 0f027e863b
44 changed files with 7369 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
// Names shared by the page and the brain server: what BMO can wear, how the
// world can look, and what BMO can do. The page owns the actual palettes and
// renders the scene; the server uses these lists for tool schemas and to
// validate what the model asks for.
export const COLOR_NAMES = [
"Classic",
"Ocean",
"Forest",
"Ember",
"Midnight",
"Sky",
"Grape",
"Honey",
"Bubblegum",
] as const;
export type ColorName = (typeof COLOR_NAMES)[number];
export const OUTFIT_NAMES = ["None", "Royal", "Hacker", "Inspector", "Dapper"] as const;
export type OutfitName = (typeof OUTFIT_NAMES)[number];
/** What each outfit looks like, so BMO can describe itself. */
export const OUTFIT_LOOKS: Record<OutfitName, string> = {
None: "no accessories",
Royal: "a gold crown and a cape",
Hacker: "big headphones and glasses",
Inspector: "a badge and cool sunglasses",
Dapper: "a top hat and a bow tie",
};
/** Plain-language color descriptions for the palettes in the page. */
export const COLOR_LOOKS: Record<ColorName, string> = {
Classic: "classic minty teal",
Ocean: "deep ocean teal",
Forest: "forest green",
Ember: "dark ember red",
Midnight: "midnight indigo",
Sky: "slate sky blue",
Grape: "grape purple",
Honey: "honey gold",
Bubblegum: "bubblegum pink",
};
export const TIME_MODES = ["auto", "day", "dusk", "night"] as const;
export type TimeModeName = (typeof TIME_MODES)[number];
export const WEATHER_MODES = ["auto", "none", "rain", "snow", "falling-leaves"] as const;
export type WeatherModeName = (typeof WEATHER_MODES)[number];
export const SPOTS = ["left", "center", "right"] as const;
export type Spot = (typeof SPOTS)[number];
export const PERFORMANCES = ["dance", "celebrate", "idea", "confused", "love", "dizzy", "look_around", "nap"] as const;
export type Performance = (typeof PERFORMANCES)[number];
/** Live state the page sends with every chat message. */
export interface SceneState {
color: ColorName;
outfit: OutfitName;
timeOfDay: "day" | "dusk" | "night";
timeMode: TimeModeName;
weather: "none" | "rain" | "snow" | "falling-leaves";
weatherMode: WeatherModeName;
/** Where BMO is standing on its little stage. */
spot: Spot;
/** The user's local time, e.g. "9:41 PM". */
localTime: string;
}
/** An action BMO decided to take, sent from the server to the page. */
export type BmoAction =
| { type: "set_color"; color: ColorName }
| { type: "set_outfit"; outfit: OutfitName }
| { type: "set_time_of_day"; time: TimeModeName }
| { type: "set_weather"; weather: WeatherModeName }
| { type: "move"; to: Spot }
| { type: "perform"; action: Performance };