Data-plane API
GET /api/v1/appearance/:key returns { "appearance": null } when no recipe exists, or a version 1 recipe. PUT takes { "appearance": recipe }; an explicit null deletes the saved preference. Missing appearance and invalid recipes return 400. The existing runtime authentication middleware supplies the user identity; callers cannot select another user in the request.
The URL-encoded key is a JSON array: ["project", mode, nativeId] or ["folder", scope, ownerId, path]. Project modes include chat, cowork, code, design, and bot. Node directories use scope node and the node id. Library folders use thread/<threadId> as their path and the containing project’s identity. Folder separators normalize to / and trailing separators are removed. Key length is capped at 2,048 bytes.
Recipes are stored in the existing runtime_settings table under appearance:<key>, whose primary key includes user_id. No schema migration or project-worker registration is required. Runtime settings GET/PUT ignore this namespace; reset deletes only execution-setting sections. Recipe writes replace a single preference atomically, and never touch coordinator metadata.
Recipe version 1
The frontend validates loaded and outgoing recipes. The runtime validates writes and caps recipe JSON size. Image import accepts PNG/JPEG/WebP, decodes to a bounded canvas, and re-encodes to PNG; no arbitrary SVG markup or remote URLs enter the recipe. Brand paths are vendored from installed Simple Icons v11.15.0 (CC0); other symbols use the existing Phosphor dependency.
UI and dependency relationships
The workspace source isallternit-ai. src/lib/appearance/model.ts owns keys, recipe validation, and image normalization. useAppearance.ts uses the authenticated runtimeApiUrl seam, including the paired-runtime relay. Reads in flight are deduplicated; no appearance data is persisted in a browser-wide cache. Account, session, organization, runtime, and entity identity changes clear the displayed recipe. Save failures do not optimistically update the icon.
AppearanceIcon renders the layered SVG. AppearanceEditor owns a cancellable draft, pointer dragging and keyboard-accessible sliders, image upload/paste, PNG export and clipboard errors. EntityIcon and CustomizeAppearance connect these to consumers:
- Shared
ProjectCard, including the phoneProjectsScreenreuse ofProjectView. ProjectCoordinatorWorkspaceheader, coordinator identity, settings, and Library.- Bot
ProjectHomeViewheader/settings andBotProjectsRail. ProjectLibrarylist and grid thread folders.- Code
ExplorerView, DesignProjectFileWorkspace, and nodeTerminalFileBrowserdirectories.
cmd/allternit-api/src/runtime_settings_routes.rs. Desktop packaging must include that runtime change as well as the UI. Web and PWA clients need a supporting connected runtime. The phone uses the same editor with a single-column layout; customizations do not invoke OS icon APIs.
The appearance HTTP relationship is recorded in docs/dependency-map/runtime-links.json. Impact was queried against both current source worktrees and the curated map was validated before landing. The editor reaches Desktop, ai.allternit.com, and the phone/PWA through the shared workspace UI.
Verification and release
Frontend tests cover recipe bounds, upload rejection, authenticated save/load/reset, account changes, failed saves, draft cancellation, text/transform editing, and symbol search. Runtime tests cover roundtrip persistence, user isolation, invalid input, and preserving appearances through execution-settings reset. Release the supporting runtime alongside the workspace UI. Desktop needs a new package; ai.allternit.com uses the workspace release path; m.allternit.com needs its separate approved deploy. Source changes alone do not update installed applications or deployed sites. See customization instructions. The client symbol catalog andvalidate_appearance runtime allowlist must change together. The current catalog has 48 Phosphor symbols and 71 Simple Icons brand paths. Image drop, paste and file selection all run through the same bounded raster import. Project header banners derive from the account’s saved appearance color.
Shell navigation material
allternit-ai/src/design/navigationGlass.css is imported by theme.css, so Desktop, web and the fabric-session PWA load the same navigation material. allternit-nav-control styles floating controls and composer mode segments; allternit-rail-tab styles shell tabs. Selection comes from aria-selected, aria-pressed or aria-current="page". Keep icon motion brief and interaction-driven. The stylesheet provides reduced-motion, reduced-transparency and increased-contrast fallbacks; do not replace these with opaque tan or gray hover utilities. Rail selection variables use --nav-glass-fill.
ProjectBanner.tsx shares the full-width identity band across BaseProjectView (including bot project homes) and ProjectCoordinatorWorkspace (Chat, Cowork, Code and Design). Both use the same mode/project recipe key as their icons and settings. The appearance editor bounds its scrolling control area, keeps its footer visible, and separates preview/import/transform controls from color/catalog controls.
Project settings pass mode-specific configuration into the shared AppearanceEditor shell. Appearance and Configuration share bounded dialog geometry; the latter is available during appearance errors. bot-config.css uses inline-size container queries rather than outer viewport breakpoints for bot phone Workspace & Config.
Bot avatar rendering
allternit-ai/src/views/bots/PackAvatarImage.tsx resolves pack portrait URLs to sheets or prepared expression assets, including avatars saved as image configs. AvatarPortraitCanvas.tsx and avatar-framing.ts measure visible art and fit it to an 88% footprint without changing aspect ratio. Sheet framing uses fixed neutral-frame bounds so animation does not resize every frame. Drawing uses device pixel ratio up to 2 and smooth scaling for clay and 2D artwork. Decoded images and measurements are cached; visible sprite instances share avatar-animation-clock.ts. Activity mappings live in the pure src/lib/agents/avatar-activity.ts module.
The pack manifest remains the catalog source of truth. Office Professionals uses Allternit Series expression PNGs; old portraits and sheets are retained for saved configs. Gizzi YouTube combines prepared expression PNGs with transparent SVG exports of the existing show rigs. The source and reproducible exporter are in scripts/avatar-cast/; asset provenance is recorded in its README. The pocket companion cutouts retain their established WebP URLs.
BotCategorySelect.tsx is shared across create/edit and harness forms. Its portalled popover stays outside modal clipping, uses themed glass, and supports selection labels, keyboard navigation, focus return and dismissal.