--- name: hermes-ui-kit description: Shared React chat UI package for Astral Hermes and Hermes Spawn. Use whenever changing either app's Hermes chat interface, voice-note UI, composer, knowledge sidebar, or shared chat shell. version: 1.0.0 author: Hermes Agent license: MIT metadata: hermes: tags: [react, chat-ui, astral-hermes, hermes-spawn, shared-components] related_skills: [coding-quality-workflow, vps-app-deployment] --- # Hermes UI Kit Use this skill when changing the Astral Hermes or Hermes Spawn chat interface. ## Source of truth - Repo/path: `/home/avalon/apps/hermes-ui-kit` - GitHub: `firemountain/hermes-ui-kit` private repo - Package: `@hermes/ui-kit` ## Consumers - Astral Hermes web: `/home/avalon/apps/astral-hermes-platform/web` - Hermes Spawn: `/home/avalon/apps/hermes-spawn` - Hermes Mobile: `/home/avalon/apps/hermes-mobile` ## Architecture rule Generic chat-shell work belongs in `@hermes/ui-kit`: - responsive chat shell - mobile sidebar/drawer - knowledge navigation and preview slots - message bubbles - voice note recording/playback chrome - composer behavior - typing/thinking indicators App-specific behavior stays in the consuming app and is passed via props/slots: - Astral timewaves/transits/astrology copy and mock landing behavior - Spawn provisioning/provider/storage/ops and tenant API calls - Transcription/chat API adapters Do not duplicate shared chat UI directly in Astral or Spawn unless it is a temporary spike that gets moved back to the kit before finishing. ## Local package wiring pattern Consumers currently depend on the kit via local file dependencies: - Astral web: `"@hermes/ui-kit": "file:../../hermes-ui-kit"` - Spawn: `"@hermes/ui-kit": "file:../hermes-ui-kit"` - Hermes Mobile: `"@hermes/ui-kit": "file:../hermes-ui-kit"` When wiring a Vite app to this sibling local React package, add/keep a consumer-side `vite.config.js` that dedupes and aliases React back to the consumer's own `node_modules`: ```js resolve: { dedupe: ['react', 'react-dom'], alias: { react: path.resolve(__dirname, 'node_modules/react'), 'react-dom': path.resolve(__dirname, 'node_modules/react-dom'), }, } ``` This avoids duplicate-React/runtime-hook problems and also prevents local package builds from failing to resolve `react` from the sibling package path. ## Hermes app styling preference - When restyling Alex's VPS apps, prefer the Hermes Creative / Hermes Social cream-panel language over dark dashboards unless explicitly requested otherwise. See `references/hermes-app-visual-language.md` for tokens, layout cues, and verification notes. - Start from the user's mental model, not the backend schema. Primary navigation and cards should name recognizable things and actions; internal IDs, hashes, tenant controls, confidence thresholds, graph levels, queue mechanics, and provenance handles belong in optional technical disclosures. - Translate operations language into consequences. Prefer “one source at a time so the server stays responsive” over “backpressure”; never invent labels such as “trust fan-out” without a user-facing need and explanation. - A visualization must remain legible without clicking random shapes: use visible labels, a plain-language legend, meaningful descriptions, source attribution, and a card/list alternative. Separate demo/test/system layers from real user knowledge by default. - Reject generic AI control-room styling: no dark/cyber shell, glow-heavy metric cards, unexplained percentages, oversized hero copy, or faux-technical chrome. Use compact branding, warm cream surfaces, navy structure, restrained gold accents, plain labels, and clear hierarchy. ## Pitfalls - Keep `@hermes/ui-kit` generic. Do not move tenant API calls, Astral transit/timewave logic, Spawn provider/provisioning logic into the kit. - For voice notes, the kit should only own browser recording/playback/chrome and pass `{ audioBase64, mimeType, waveform, audioUrl }` to the consumer. Transcription credentials and API calls stay server-side in the consuming app. - For Hermes-style tool visibility, keep the kit generic by normalizing/displaying message metadata such as `progress: string[]`, optional `toolCalls`, and optional `images`; consuming apps translate their actual SSE/tool events into those fields. Add/update shape tests whenever normalized message fields change. - For knowledge previews, expose slots/props (`sidebarSections`, `sidebarPreview`, `onSidebarItemClick`) rather than hard-coding file APIs. - For Desktop-like mobile chat, preserve the information architecture: thread/session drawer, current conversation, and knowledge/wiki panel. Local thread grouping is acceptable for an MVP, but resumed Hermes sessions should carry their `sessionId` through the app-server adapter. - When embedding `SharedHermesChat` inside a mobile/PWA app shell, let the shared chat own the chat header and drawer on the chat route. Avoid stacking an app-level header/hamburger on top of the kit header; it creates duplicate menu buttons and makes the chat feel like a nested widget. If the kit's floating and topbar drawer toggles both appear on mobile, hide one explicitly or add a generic kit prop rather than adding another consumer-side control. - For iOS PWA chat routes, guard against horizontal rubber-band drift while vertically scrolling: constrain route/chat wrappers to viewport width and use `overflow-x: hidden`, `overscroll-behavior-x: none`, and `touch-action: pan-y` on the full-screen chat shell/panel. - Label knowledge/wiki vault selectors clearly and hide them when only one vault is available; an unexplained dropdown in chat chrome reads as broken UI. If the wiki affordance is not part of the current user goal, omit it from the primary mobile drawer until there is a clear search/browse interaction. - Keep mobile hamburger/menu buttons visually identical across app routes and shared-chat routes. Normalize to the same hit target, centering, border/background, icon geometry, and safe-area/top-left rhythm; otherwise a technically single hamburger can still look broken if it is offset or styled differently from the rest of the app. - For Hermes-session consumers, distinguish local UI threads from persisted Hermes backend sessions. A "new thread" may start as a local blank wrapper, but after first send it should store the returned `sessionId`. Opening an existing session should fetch real transcript messages in pages (latest chunk first, with "load older" pagination) rather than showing only a placeholder, so users can scroll back without overloading the app. If adding a history pager/thread header above the shared message list, verify the grid rows and force the message list to remain the scroll container; otherwise loaded sessions can become non-scrollable with strange mobile margins. See `references/hermes-mobile-chat-integration-pitfalls-2026-06.md`. - When fixing mobile chat overflow, zoom out before stacking CSS overrides: preserve a coherent model where the route shell is one viewport, the thread is the only vertical scroll container, topbar/composer stay outside it, assistant bubbles can stretch, and user bubbles remain natural-width/right-aligned with an ~80–85% cap. Do not "fix" horizontal scroll by making every bubble `width:100%` or by applying broad `overflow:hidden`; that clips long imported messages and collapses chat semantics. Instead use `min-width:0`, shell/thread `overflow-x:hidden`, `overscroll-behavior-x:none`, `touch-action:pan-y`, and wrapping rules for `p`, `pre`, and `code`. Test with old/resumed sessions containing long copied text, not just new chat. See `references/hermes-mobile-loaded-session-chat-debugging-2026-06.md`. - Render Hermes markdown inside shared chat bubbles rather than exposing raw syntax. At minimum handle headings, bold, italics, inline code, and bullet lists; keep markdown rendering generic in the UI kit so Astral/Spawn/Mobile all benefit. - If the chat surface also shows VPS/app status, do not filter PM2 to Hermes-named processes; enrich all PM2 processes with launcher metadata where possible. - If Vite/Rolldown reports it cannot resolve `react` from `/home/avalon/apps/hermes-ui-kit/src/...`, check the consumer `vite.config.js` dedupe/alias and the kit `peerDependencies`/local `devDependencies` before changing component code. ## References - `references/astral-spawn-chat-extraction-2026-06.md` — session notes for the initial extraction into a private sibling repo and the Vite/local-package workaround. - `references/mobile-pwa-chat-shell-and-vps-console.md` — mobile PWA pattern for Desktop-like thread drawers, Hermes session resume, LLM wiki browsing, and all-PM2 VPS status links. - `references/hermes-mobile-sidebar-chat-redesign-2026-06.md` — session notes for making Hermes Mobile a `@hermes/ui-kit` consumer, adding progress/tool metadata, voice-note adapter flow, full app sidebar navigation, and multi-consumer verification. - `references/hermes-mobile-chat-integration-pitfalls-2026-06.md` — duplicate-hamburger, iOS horizontal rubber-band, and unexplained wiki-vault dropdown lessons from integrating the shared chat into Hermes Mobile. - `references/hermes-mobile-loaded-session-chat-debugging-2026-06.md` — resumed-session mobile chat lessons: horizontal overflow, clipped bubbles, markdown rendering, history pager grid rows, and verification checklist. ## Verification Run all of these after shared UI changes: ```bash npm --prefix /home/avalon/apps/hermes-ui-kit test npm --prefix /home/avalon/apps/hermes-spawn test npm --prefix /home/avalon/apps/astral-hermes-platform/web run build npm --prefix /home/avalon/apps/hermes-spawn run build ``` For deployment changes, also restart/verify PM2 apps per the `vps-app-deployment` skill.