Replace the legacy monolithic ChatInterface.jsx implementation with a modular TypeScript architecture centered around a small orchestration component (ChatInterface.tsx).
Core architecture changes:
- Remove src/components/ChatInterface.jsx and add src/components/ChatInterface.tsx as a thin coordinator that wires provider state, session state, realtime WebSocket handlers, and composer behavior via dedicated hooks.
- Update src/components/MainContent.tsx to use typed ChatInterface directly (remove AnyChatInterface cast).
State ownership and hook extraction:
- Add src/hooks/chat/useChatProviderState.ts to centralize provider/model/permission-mode state, provider/session synchronization, cursor model bootstrap from backend config, and pending permission request scoping.
- Add src/hooks/chat/useChatSessionState.ts to own chat/session lifecycle state: session loading, cursor/claude/codex history loading, pagination, scroll restoration, visible-window slicing, token budget loading, persisted chat hydration, and processing-state restoration.
- Add src/hooks/chat/useChatRealtimeHandlers.ts to isolate WebSocket event processing for Claude/Cursor/Codex, including session filtering, streaming chunk buffering, session-created/pending-session transitions, permission request queueing/cancellation, completion/error handling, and session status updates.
- Add src/hooks/chat/useChatComposerState.ts to own composer-local state and interactions: input/draft persistence, textarea sizing and keyboard behavior, slash command execution, file mentions, image attachment/drop/paste workflow, submit/abort flows, permission decision responses, and transcript insertion.
UI modularization under src/components/chat:
- Add view/ChatMessagesPane.tsx for message list rendering, loading/empty states, pagination affordances, and thinking indicator.
- Add view/ChatComposer.tsx for composer shell layout and input area composition.
- Add view/ChatInputControls.tsx for mode toggles, token display, command launcher, clear-input, and scroll-to-bottom controls.
- Add view/PermissionRequestsBanner.tsx for explicit tool-permission review actions (allow once / allow & remember / deny).
- Add view/ProviderSelectionEmptyState.tsx for provider and model selection UX plus task starter integration.
- Add messages/MessageComponent.tsx and markdown/Markdown.tsx to isolate message rendering concerns, markdown/code rendering, and rich tool-output presentation.
- Add input/ImageAttachment.tsx for attachment previews/removal/progress/error overlay rendering.
Shared chat typing and utilities:
- Add src/components/chat/types.ts with shared types for providers, permission mode, message/tool payloads, pending permission requests, and ChatInterfaceProps.
- Add src/components/chat/utils/chatFormatting.ts for html decoding, code fence normalization, regex escaping, math-safe unescaping, and usage-limit text formatting.
- Add src/components/chat/utils/chatPermissions.ts for permission rule derivation, suggestion generation, and grant flow.
- Add src/components/chat/utils/chatStorage.ts for resilient localStorage access, quota handling, and normalized Claude settings retrieval.
- Add src/components/chat/utils/messageTransforms.ts for session message normalization (Claude/Codex/Cursor) and cached diff computation utilities.
Command/file input ergonomics:
- Add src/hooks/chat/useSlashCommands.ts for slash command fetching, usage-based ranking, fuzzy filtering, keyboard navigation, and command history persistence.
- Add src/hooks/chat/useFileMentions.tsx for project file flattening, @mention suggestions, mention highlighting, and keyboard/file insertion behavior.
TypeScript support additions:
- Add src/types/react-syntax-highlighter.d.ts module declarations to type-check markdown code highlighting imports.
Behavioral intent:
- Preserve existing chat behavior and provider flows while improving readability, separation of concerns, and future refactorability.
- Move state closer to the components/hooks that own it, reducing cross-cutting concerns in the top-level chat component.
Replace the previous monolithic MainContent.jsx with a typed one and
extract focused subcomponents/hooks to improve readability, local state ownership,
and maintainability while keeping runtime behavior unchanged.
Key changes:
- Replace `src/components/MainContent.jsx` with `src/components/MainContent.tsx`.
- Add typed contracts for main-content domain in `src/components/main-content/types.ts`.
- Extract header composition into:
- `MainContentHeader.tsx`
- `MainContentTitle.tsx`
- `MainContentTabSwitcher.tsx`
- `MobileMenuButton.tsx`
- Extract loading/empty project views into `MainContentStateView.tsx`.
- Extract editor presentation into `EditorSidebar.tsx`.
- Move editor file-open + resize behavior into `useEditorSidebar.ts`.
- Move mobile menu touch/click suppression logic into `useMobileMenuHandlers.ts`.
- Extract TaskMaster-specific concerns into `TaskMasterPanel.tsx`:
- task detail modal state
- PRD editor modal state
- PRD list loading/refresh
- PRD save notification lifecycle
Behavior/compatibility notes:
- Preserve existing tab behavior, session passthrough props, and Chat/Git/File flows.
- Keep interop with existing JS components via boundary `as any` casts where needed.
- No intentional functional changes; this commit is structural/type-oriented refactor.
Validation:
- `npm run typecheck` passes.
- `npm run build` passes (existing unrelated CSS minify warnings remain).
The mobile sidebar menu button redirects users to `cloudcli.ai/dashboard` when a
session was active. The redirect happened because
the menu was opened on `touchstart`, which mounted the sidebar before the touch
sequence completed; the follow-up tap/click then landed on the sidebar header
anchor.
This change rewrites mobile menu interaction handling in `MainContent.jsx` to
eliminate touch/click event leakage and ghost-click behavior.
Key changes:
- Added `suppressNextMenuClickRef` to guard against synthetic click events that
fire after a touch interaction.
- Added `openMobileMenu(event)` helper to centralize `preventDefault`,
`stopPropagation`, and `onMenuClick()` invocation.
- Added `handleMobileMenuTouchEnd(event)`:
- opens the menu on `touchend` instead of `touchstart`
- sets a short suppression window (350ms) for the next click.
- Added `handleMobileMenuClick(event)`:
- ignores/suppresses click events during the suppression window
- otherwise opens the menu normally.
- Updated all mobile menu button instances in `MainContent.jsx` (loading state,
no-project state, active header state) to use:
- `onTouchEnd={handleMobileMenuTouchEnd}`
- `onClick={handleMobileMenuClick}`
- Removed the previous `onTouchStart` path that caused premature DOM mutation.
Behavioral impact:
- Mobile sidebar still opens reliably with one tap.
- Tap no longer leaks to newly-mounted sidebar header links.
- Prevents accidental redirects while preserving existing menu UX.
- Replace `src/App.jsx` with `src/App.tsx` and move route-level UI orchestration into `src/components/app/AppContent.tsx`.
This separates provider/bootstrap concerns from runtime app layout logic, keeps route definitions minimal, and improves readability of the root app entry.
- Introduce `src/hooks/useProjectsState.ts` to centralize project/session/sidebar state management previously embedded in `App.jsx`.
This keeps the existing behavior for:
project loading,
Cursor session hydration,
WebSocket `loading_progress` handling,
additive-update protection for active sessions,
URL-based session selection,
sidebar refresh/delete/new-session flows.
The hook now exposes a typed `sidebarSharedProps` contract and typed handlers used by `AppContent`.
- Introduce `src/hooks/useSessionProtection.ts` for active/processing session lifecycle logic.
This preserves session-protection behavior while isolating `activeSessions`, `processingSessions`, and temporary-session replacement into a dedicated reusable hook.
- Replace monolithic `src/components/Sidebar.jsx` with typed `src/components/Sidebar.tsx` as a thin orchestrator.
`Sidebar.tsx` now focuses on wiring controller state/actions, modal visibility, collapsed mode, and version modal behavior instead of rendering every UI branch inline.
- Add `src/hooks/useSidebarController.ts` to encapsulate sidebar interaction/state logic.
This includes expand/collapse state, inline project/session editing state, project starring/sorting/filtering, lazy session pagination, delete confirmations, rename/delete actions, refresh state, and mobile touch click handling.
- Add strongly typed sidebar domain models in `src/components/sidebar/types.ts` and move sidebar-derived helpers into `src/components/sidebar/utils.ts`.
Utility coverage now includes:
session provider normalization,
session view-model creation (name/time/activity/message count),
project sorting/filtering,
task indicator status derivation,
starred-project persistence and readbacks.
- Split sidebar rendering into focused components under `src/components/sidebar/`:
`SidebarContent.tsx` for top-level sidebar layout composition.
`SidebarProjectList.tsx` for project-state branching and project iteration.
`SidebarProjectsState.tsx` for loading/empty/no-search-result placeholders.
`SidebarProjectItem.tsx` for per-project desktop/mobile header rendering and actions.
`SidebarProjectSessions.tsx` for expanded session area, skeletons, pagination, and new-session controls.
`SidebarSessionItem.tsx` for per-session desktop/mobile item rendering and session actions.
`SessionProviderIcon.tsx` for provider icon normalization.
`SidebarHeader.tsx`, `SidebarFooter.tsx`, `SidebarCollapsed.tsx`, and `SidebarModals.tsx` as dedicated typed UI surfaces.
This keeps rendering responsibilities local and significantly improves traceability.
- Convert shared UI primitives from JSX to TSX:
`src/components/ui/button.tsx`,
`src/components/ui/input.tsx`,
`src/components/ui/badge.tsx`,
`src/components/ui/scroll-area.tsx`.
These now provide typed props/variants (`forwardRef` where appropriate) while preserving existing class/behavior.
- Add shared app typings in `src/types/app.ts` for projects/sessions/websocket/loading contracts used by new hooks/components.
- Add global window declarations in `src/types/global.d.ts` for `__ROUTER_BASENAME__`, `refreshProjects`, and `openSettings`, removing implicit `any` usage for global integration points.
- Update `src/main.jsx` to import `App.tsx` and keep app bootstrap consistent with the TS migration.
- Update `src/components/QuickSettingsPanel.jsx` to self-resolve mobile state via `useDeviceSettings` (remove `isMobile` prop dependency), and update `src/components/ChatInterface.jsx` to render `QuickSettingsPanel` directly.
This reduces prop drilling and keeps quick settings colocated with chat UI concerns.
- Add new `useDeviceSettings` hook (`src/hooks/useDeviceSettings.ts`) to centralize
device-related state:
- exposes `isMobile` and `isPWA`
- supports options: `mobileBreakpoint`, `trackMobile`, `trackPWA`
- listens to window resize for mobile updates
- listens to `display-mode: standalone` changes for PWA updates
- includes `matchMedia.addListener/removeListener` fallback for older environments
- Update `AppContent` (`src/App.jsx`) to consume `isMobile` from
`useDeviceSettings({ trackPWA: false })`:
- remove local `isMobile` state/effect
- remove local `isPWA` state/effect
- keep existing `isMobile` behavior for layout and mobile sidebar flow
- stop passing `isPWA` into `Sidebar` props
- Update `Sidebar` (`src/components/Sidebar.jsx`) to own PWA detection:
- consume `isPWA` from `useDeviceSettings({ trackMobile: false })`
- add effect to toggle `pwa-mode` class on `document.documentElement` and `document.body`
- retain use of `isMobile` prop from `App` for sidebar/mobile rendering decisions
Why:
- removes duplicated device-detection logic from `AppContent`
- makes device-state logic reusable and easier to maintain
- keeps PWA-specific behavior where it is actually used (`Sidebar`)
* fix: claude code login issues
1. Now, the browser opens in a new tab automatically
2. Clicking "C" to copy works
3. I have removed the "x-term" link selector since it didn't select the whole link
* fix: remove unnecessary terminal hyperlink for auth URL
* fix(shell): resolve clipboard handling for copy and paste events
* feat(shell): add authentication URL display and copy functionality - allows copy for mobile users
* revert: Update login command for unauthenticated users to use '/exit'
---------
Co-authored-by: Haileyesus <something@gmail.com>
* feat(i18n): add Korean language support
- Add Korean (ko) translation files for all namespaces:
- common.json, auth.json, settings.json, sidebar.json, chat.json, codeEditor.json
- Register Korean locale in config.js and languages.js
- Follow translation guidelines:
- Keep technical terms in English (UI, API, Shell, Git, etc.)
- Use Korean phonetic for some terms (TaskMaster → 테스크마스터)
- Maintain concise translations to match English length where possible
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(i18n): keep technical term "UI" in Korean translation
- Change "인터페이스" back to "UI" in sidebar subtitle
- Keep technical terms in English as per translation guidelines
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: remove unnecessary websocket.js file and replace its usage directly in `WebSocketContext`
* fix: connect() doesn't need to be async
* fix: update WebSocket context import to use useWebSocket hook
* fix: use `useRef` for WebSocketContext
The main issue with using states was, previously the websocket never closed
properly on unmount, so multiple connections could be opened.
This was because the useEffect cleanup function was closing an old websocket
(that was initialized to null) instead of the current one.
We could have fixed this by adding `ws` to the useEffect dependency array, but
this was unnecessary since `ws` doesn't affect rendering so we shouldn't use a state.
* fix: replace `WebSocketContext` default value with null and add type definitions
* fix: add type definition for WebSocket URL and remove redundant protocol declaration
* fix: Prevent WebSocket reconnection attempts after unmount
Right now, when the WebSocketContext component unmounts,
there is still a pending reconnection attempt that tries
to reconnect the WebSocket after 3 seconds.
* refactor: Extract WebSocket URL construction into a separate function
* refactor: Centralize platform mode detection using IS_PLATFORM constant; use `token` from Auth context in WebSocket connection
* refactor: Use IS_PLATFORM constant for platform detection in authenticatedFetch function (backend)
* refactor: move IS_PLATFORM to config file for both frontend and backend
The reason we couldn't place it in shared/modelConstants.js is that the
frontend uses Vite which requires import.meta.env for environment variables,
while the backend uses process.env. Therefore, we created separate config files
for the frontend (src/constants/config.ts) and backend (server/constants/config.js).
* refactor: update import path for IS_PLATFORM constant to use config file
* refactor: replace `messages` with `latestMessage` in WebSocket context and related components
Why?
Because, messages was only being used to access the latest message in the components it's used in.
* refactor: optimize WebSocket connection handling with useCallback and useMemo
* refactor: comment out debug log for render count in AppContent component
* refactor(backend): update environment variable handling and replace VITE_IS_PLATFORM with IS_PLATFORM constant
* refactor: update WebSocket connection effect to depend on token changes for reconnection
---------
The regex only replaced forward slashes, causing Windows paths like
C:\Users\Eric\my_project to remain unchanged instead of being encoded
to C--Users-Eric-my-project. This caused API routes to fail.