evOo-local/packages/core/src/project-look.ts
cynarAI 6ace14c3c8
Projects: one sidebar row with icon and color, coordinator overview (#190)
Each project is now a single sidebar row with its own icon and color that
opens the project's coordinator conversation. The whole row drags to
reorder. The row menu gains Customize for name, icon and color. Inside
the coordinator conversation, an overview lists the project's agent
threads that need the user, are running, or recently finished.


Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-10 03:32:40 +02:00

107 lines
5.6 KiB
TypeScript

import { OO_PALETTE } from "./oo-palette.js";
/**
* The icons and colors a project can wear in the sidebar. Ids are stored on the space; each
* platform maps an id to its own symbol (web: lucide, iOS: SF Symbols, Android: Ionicons).
*/
export const PROJECT_ICONS = [
{ id: "folder", ios: "folder", android: "folder-outline" },
{ id: "briefcase", ios: "briefcase", android: "briefcase-outline" },
{ id: "house", ios: "house", android: "home-outline" },
{ id: "heart", ios: "heart", android: "heart-outline" },
{ id: "star", ios: "star", android: "star-outline" },
{ id: "book", ios: "book", android: "book-outline" },
{ id: "code", ios: "chevron.left.forwardslash.chevron.right", android: "code-slash-outline" },
{ id: "terminal", ios: "terminal", android: "terminal-outline" },
{ id: "globe", ios: "globe", android: "globe-outline" },
{ id: "camera", ios: "camera", android: "camera-outline" },
{ id: "film", ios: "film", android: "film-outline" },
{ id: "music", ios: "music.note", android: "musical-notes-outline" },
{ id: "mic", ios: "mic", android: "mic-outline" },
{ id: "image", ios: "photo", android: "image-outline" },
{ id: "paintbrush", ios: "paintbrush", android: "brush-outline" },
{ id: "pencil", ios: "pencil", android: "pencil-outline" },
{ id: "cart", ios: "cart", android: "cart-outline" },
{ id: "bag", ios: "bag", android: "bag-outline" },
{ id: "money", ios: "banknote", android: "cash-outline" },
{ id: "chart", ios: "chart.bar", android: "bar-chart-outline" },
{ id: "trending", ios: "chart.line.uptrend.xyaxis", android: "trending-up-outline" },
{ id: "calendar", ios: "calendar", android: "calendar-outline" },
{ id: "clock", ios: "clock", android: "time-outline" },
{ id: "mail", ios: "envelope", android: "mail-outline" },
{ id: "chat", ios: "bubble.left", android: "chatbubble-outline" },
{ id: "people", ios: "person.2", android: "people-outline" },
{ id: "person", ios: "person", android: "person-outline" },
{ id: "school", ios: "graduationcap", android: "school-outline" },
{ id: "flask", ios: "flask", android: "flask-outline" },
{ id: "bulb", ios: "lightbulb", android: "bulb-outline" },
{ id: "bolt", ios: "bolt", android: "flash-outline" },
{ id: "flame", ios: "flame", android: "flame-outline" },
{ id: "leaf", ios: "leaf", android: "leaf-outline" },
{ id: "paw", ios: "pawprint", android: "paw-outline" },
{ id: "airplane", ios: "airplane", android: "airplane-outline" },
{ id: "car", ios: "car", android: "car-outline" },
{ id: "bicycle", ios: "bicycle", android: "bicycle-outline" },
{ id: "map", ios: "map", android: "map-outline" },
{ id: "pin", ios: "mappin", android: "location-outline" },
{ id: "fitness", ios: "dumbbell", android: "barbell-outline" },
{ id: "medical", ios: "cross.case", android: "medkit-outline" },
{ id: "food", ios: "fork.knife", android: "restaurant-outline" },
{ id: "coffee", ios: "cup.and.saucer", android: "cafe-outline" },
{ id: "game", ios: "gamecontroller", android: "game-controller-outline" },
{ id: "gift", ios: "gift", android: "gift-outline" },
{ id: "trophy", ios: "trophy", android: "trophy-outline" },
{ id: "flag", ios: "flag", android: "flag-outline" },
{ id: "key", ios: "key", android: "key-outline" },
{ id: "lock", ios: "lock", android: "lock-closed-outline" },
{ id: "shield", ios: "shield", android: "shield-outline" },
{ id: "gear", ios: "gearshape", android: "settings-outline" },
{ id: "hammer", ios: "hammer", android: "hammer-outline" },
{ id: "tools", ios: "wrench.and.screwdriver", android: "construct-outline" },
{ id: "cloud", ios: "cloud", android: "cloud-outline" },
{ id: "sun", ios: "sun.max", android: "sunny-outline" },
{ id: "moon", ios: "moon", android: "moon-outline" },
{ id: "box", ios: "cube", android: "cube-outline" },
{ id: "layers", ios: "square.stack.3d.up", android: "layers-outline" },
{ id: "server", ios: "server.rack", android: "server-outline" },
{ id: "phone", ios: "iphone", android: "phone-portrait-outline" },
{ id: "desktop", ios: "desktopcomputer", android: "desktop-outline" },
{ id: "news", ios: "newspaper", android: "newspaper-outline" },
{ id: "document", ios: "doc.text", android: "document-text-outline" },
{ id: "library", ios: "books.vertical", android: "library-outline" },
{ id: "sparkles", ios: "sparkles", android: "sparkles-outline" },
{ id: "target", ios: "target", android: "locate-outline" },
{ id: "infinity", ios: "infinity", android: "infinite-outline" },
] as const;
export type ProjectIconId = (typeof PROJECT_ICONS)[number]["id"];
export const DEFAULT_PROJECT_ICON: ProjectIconId = "folder";
/** Project colors are the Oo identity palette, so projects and bots read as one family. */
export const PROJECT_COLORS = OO_PALETTE.map((color) => ({
id: color.id,
hex: color.hex,
/** The icon drawn on this color, light or dark so it stays legible. */
ink: color.eyeColor,
}));
export const DEFAULT_PROJECT_COLOR = "violet";
export function isProjectIcon(value: string): value is ProjectIconId {
return PROJECT_ICONS.some((icon) => icon.id === value);
}
export function isProjectColor(value: string): boolean {
return PROJECT_COLORS.some((color) => color.id === value);
}
/** The look to draw, with defaults for projects that never chose one. */
export function projectLook(space: { icon?: string | null; color?: string | null }) {
const icon =
PROJECT_ICONS.find((item) => item.id === space.icon) ??
PROJECT_ICONS.find((item) => item.id === DEFAULT_PROJECT_ICON)!;
const color =
PROJECT_COLORS.find((item) => item.id === space.color) ??
PROJECT_COLORS.find((item) => item.id === DEFAULT_PROJECT_COLOR)!;
return { icon, color };
}