- Extract ChatView from web chat-page into packages/ui as a prop-driven
component (accepts UseChatReturn shape, no transport dependency)
- Move DevicePairing from apps/web to packages/ui with locally defined
ConnectionIdentity type (no @multica/hooks dependency)
- Create @multica/hooks package with useGatewayConnection and useChat
(moved from apps/web/hooks)
- Add isLoadingHistory state to useChat with skeleton loading in ChatView
- Add MulticaIcon (pure CSS asterisk via clip-path, adapts to theme)
- Slim web chat-page.tsx from 188 to 65 lines (just wires hooks to UI)
Desktop can now reuse ChatView and DevicePairing directly.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The original icon PNGs had pre-baked rounded corners and transparent
backgrounds. When iOS applies its own superellipse mask on Add to Home
Screen, the transparent corners were filled with white, creating a
visible white border around the icon.
Changes:
- Regenerate all icon PNGs as full-bleed (no rounded corners, opaque
background filling entire canvas) from the existing logo SVG
- Add dedicated apple-touch-icon.png at 180x180 (iOS standard size)
- Add favicon.ico for universal browser fallback
- Replace oversized app/icon.png (540x540) with 192x192 full-bleed
- Update layout.tsx to reference apple-touch-icon.png
- Add explicit purpose: "any" to 192x192 manifest entry
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace Zustand global stores with hook-local state for the web app.
useGatewayConnection handles client lifecycle, identity persistence,
reconnection, and keyed reset. useChat handles message history, streaming
events, tool execution, Hub error action, and exec approval requests.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add logo.svg as the new brand logo
- Generate logo-192x192.png and logo-512x512.png from SVG for PWA
- Update app-header, manifest, and layout to reference new logo files
- Remove old icon.png, icon-192x192.png, icon-512x512.png
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- desktop layout.tsx: remove connection logic, keep header + tabs + outlet
- web app-header.tsx: remove connection logic, keep logo + theme toggle
Connection lifecycle is now owned by the shared Chat component.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove all props from Chat (showHeader, headerActions) making it a
zero-config pure chat component with only connection input, messages,
and send functionality
- Create AppHeader client component for web app with brand, theme
toggle, disconnect button, and useHubInit
- Add disconnect button to desktop layout header
- Add reset() action to hub store to eliminate duplicated state reset
- Remove unused token field from gateway store
- Remove dead code: connection-bar.tsx
- Guard handleConnect against empty deviceId race condition
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Chat component no longer depends on next-themes, making it safe to use
in the desktop app. Theme toggle is now a fixed button in the web layout.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add connection code parsing (JSON + base64) with localStorage persistence
- Add connectWithCode() to gateway store for code-driven connections
- Replace Sidebar with simple ConnectionBar (paste code / status / disconnect)
- Simplify Chat component by removing header clutter
- Auto-restore connection on page refresh from saved code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Next.js MetadataRoute.Manifest types only accept single-value purpose
fields, not space-separated combinations like "any maskable".
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enable the web app to be installed as a standalone PWA on mobile and
desktop. Uses Next.js built-in Metadata API for manifest generation,
a lightweight service worker for navigation offline fallback, and
proper Apple Web App metadata for iOS support.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move gatewayUrl into gateway store state with setGatewayUrl action
- layout.tsx uses useGatewayStore.getState().setGatewayUrl() directly
- Remove @multica/fetch from store and web package.json dependencies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Document thin-shell architecture, package responsibilities, Zustand
best practices, import conventions, and new feature checklist.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move setConfig() after imports in layout.tsx
- Add gateway disconnect cleanup in hub-init useEffect
- Replace silent catches with toast error notifications in hub store
- Optimize chat selectors with useMemo and useCallback/getState()
- Remove unused getMessagesByAgent from messages store
- Add clipboard try/catch with error toast
- Add skeleton loading states for hub sidebar and chat header
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create gateway store in @multica/store (WS connection independent of components)
- Gateway auto-connects when hub is ready, messages handled internally
- Move scroll-fade hook to @multica/ui/hooks
- Move Chat component to @multica/ui/components
- Add setConfig() call in web layout for URL injection
- Delete all web-local hooks, components, and lib/config
- Web app is now a pure shell: layout.tsx + page.tsx
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move useMessages from apps/web to packages/store/src/messages.ts
- Convert useState to Zustand store for global message persistence
- Add reserved interfaces: updateMessage, loadMessages, getMessagesByAgent
- Update chat.tsx imports to use @multica/store
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move useDeviceId from apps/web to packages/store/src/device-id.ts
- Update imports in chat.tsx and use-gateway.ts to use @multica/store
- Add uuid dependency to @multica/store
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create @multica/fetch package for HTTP client and URL config
- Migrate hub store and hub-init hook to @multica/store
- Move HubSidebar component to @multica/ui for web/desktop reuse
- Update web app imports to use shared packages
- Remove counter store example and its component-example usage
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Simplified handleSend function in Chat component by removing unnecessary useCallback.
- Refactored useDeviceId hook to utilize useSyncExternalStore for better state management and removed useState and useEffect for device ID retrieval.
- Updated useGateway hook to ensure onMessageRef is set correctly on options change.
- Enhanced useScrollFade hook to properly cancel animation frames on cleanup.
Replace useState-based useHub hook and separate useActiveAgent store
with a single useHubStore (Zustand). This fixes the bug where
HubSidebar and Chat held independent state copies, causing stale data
and duplicate 30s polling. Agent create/delete logic now lives in the
store with automatic activeAgentId management.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Re-add deviceId (full UUID) with copy-to-clipboard button next to the
SidebarTrigger, matching the original behavior from commit 0975510.
Both deviceId and agentId now have independent copy states.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Re-add the copy-to-clipboard button that was lost when chat.tsx was
rewritten for hub/agent support. Clicking copies the full agentId,
shows a checkmark icon for 2s, and triggers a toast confirmation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Align with sidebar component library convention: use Tailwind v4 native
data-active: instead of data-[active]:, and add missing
text-sidebar-accent-foreground for active state.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace SidebarMenuButton with a custom flex row div that handles hover
and active states on the entire item, not just the text area. Delete
button uses stopPropagation to avoid triggering selection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
SidebarMenuButton applies truncate via [&>span:last-child]:truncate,
so the text must be in a <span> rather than a bare text node to get
overflow-hidden + text-ellipsis + whitespace-nowrap.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Drop SidebarMenuAction (absolute positioning) in favor of a flex row:
left side (SidebarMenuButton) gets flex-1 min-w-0 for natural truncation,
right side (delete button) sits as a normal flex child with hover reveal.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add cursor-not-allowed and reduced opacity to ChatInput when disabled
- Show user icon in empty state when no agent is selected
- Pass contextual placeholder text based on agent selection
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use CSS truncation for agent/hub IDs instead of JS slicing, show delete
button only on hover via showOnHover prop, and reduce contrast on status
dots, labels, and placeholder text for a more subtle appearance.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- hub-sidebar: displays Hub connection status, agent list with create/delete
- chat: sends messages via Gateway to Hub, receives agent responses, filters by active agent
- layout: passes HubSidebar as children to AppSidebar, removes static NAV_ITEMS
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- use-gateway: GatewayClient WebSocket lifecycle with auto-connect/disconnect
- use-hub: REST polling for Hub status and agent CRUD operations
- use-active-agent: Zustand store for cross-component selected agent state
- use-messages: replace mock data with real addUser/addAssistant/clear API
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds gateway and console URL configuration in app/lib/config.ts,
centralizing endpoint URLs with env var overrides.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Display Multica icon alongside brand name in sidebar header
- Use Playfair Display serif font for an artistic brand feel
- Expose --font-brand CSS variable from layout
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Copy brand icon from gateway to web app/icon.png and public/icon.png
- Remove default favicon.ico and placeholder SVGs
- Update metadata title and description
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
sonner is a dependency of @multica/ui, not web. Re-export toast from
the ui sonner module and update the import path in chat.tsx.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Display full UUID instead of truncated 8-char slice. Add ghost button
with copy icon that writes deviceId to clipboard, switches to checkmark
for 2s, and shows a sonner toast confirmation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move device ID logic from @multica/store (Zustand persist) into a
simple useDeviceId hook in the web app. SSR returns empty string,
client reads/writes localStorage directly — no hydration mismatch,
no suppressHydrationWarning needed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use CSS mask-image gradients to hint at scrollable overflow in the chat
area. Adds useScrollFade hook that dynamically applies top/bottom fade
based on scroll position via scroll events and ResizeObserver.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Zustand persist generates a new deviceId on the server (no localStorage),
then hydrates a different value from localStorage on the client. Add
suppressHydrationWarning to the deviceId span since this mismatch is expected.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>