Commit Graph
19 Commits
Author SHA1 Message Date
7418andClaude Opus 4.6 111fa8c92e feat: refactor composer to two-layer layout, add session permission & context indicator
Composer restructure:
- MessageInput internal footer now only contains: file button, model
  selector, slash command button, and send button
- Removed mode toggle (Code/Plan) from composer area (logic preserved)
- New ChatComposerActionBar below input with three slots: left (Design
  Agent), center (permission selector), right (context indicator)

New components:
- SlashCommandButton: inserts `/` at cursor, reuses existing slash
  command detection and popover logic
- ChatPermissionSelector: session-level permission toggle (default /
  full_access) with danger confirmation dialog
- ContextUsageIndicator: SVG donut chart showing context usage ratio
  with HoverCard displaying model, tokens, cache stats
- ChatComposerActionBar: slot-based container for action bar items

Session permission (permission_profile):
- DB: new column on chat_sessions, default 'default'
- API: PATCH sessions supports updating permission_profile
- Chat API: bypassPermissions = global setting OR session full_access
- PermissionPrompt: auto-approves and hides when full_access
- Claude client: combines global and per-session bypass flags

Bridge integration:
- permission-broker: auto-approves permissions for full_access sessions
  without sending IM notifications
- conversation-engine: passes bypassPermissions to SDK for full_access
- PATCH route: auto-approves pending bridge permissions on switch to
  full_access

Other:
- ImageGenToggle relabeled to "Design Agent" / "设计 Agent"
- New model-context.ts with MODEL_CONTEXT_WINDOWS lookup
- Provider models API returns optional contextWindow field
- New useContextUsage hook for token usage calculation
- i18n: added composer.*, permission.*, context.* keys (en + zh)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 14:55:38 +08:00
7418andClaude Opus 4.6 c07a604271 fix: address code review findings — snapshot dedup, workspace hooks, permission params, Windows health check
ChatView.tsx (P2): snapshot consumption now calls detectAssistantCompletion()
so assistant workspace onboarding/check-in state updates even when the
stream completes while ChatView is unmounted.

ChatView.tsx (P2): replace local temp-message append with a DB re-fetch
on snapshot consumption to avoid duplicate messages (backend already
persists the reply in route.ts).

page.tsx (P3): add updatedInput and denyMessage parameters to
handlePermissionResponse so AskUserQuestion answers and ExitPlanMode
custom deny messages are forwarded to the permission API on the
first-message page.

electron/main.ts: fix Windows startup timeout by forcing IPv4 (family:4)
in health check HTTP request and improving error diagnostics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 14:24:19 +08:00
7418andClaude Opus 4.6 5eedb5bacc fix: resolve 7 bugs — idle output, permission scroll, file selector, drag area, input reset, markdown tables, panel collapse
- ChatView: consume unconsumed stream snapshots on mount so output
  appears after idle without needing a conversation switch
- PermissionPrompt: extract permission UI from StreamingMessage into a
  fixed-position component at the bottom of ChatView, preventing
  permission popups from scrolling off-screen during long responses
- StreamingMessage/MessageList: remove ~409 lines of permission UI code
  now handled by PermissionPrompt
- AssistantWorkspaceSection: fix macOS Electron file selector by using
  the correct IPC API (window.electronAPI.dialog.openFolder)
- AppShell: increase title bar drag region from h-5 to h-10 (#152)
- ChatView: add key={sessionId} on MessageInput to reset input state
  when switching conversations (#176)
- UpdateDialog: add remark-gfm plugin and table component mappings for
  proper GFM markdown rendering in release notes (#117)
- RightPanel: add border and top margin to collapsed panel toggle for
  visibility (#116)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 14:24:19 +08:00
7418andClaude Opus 4.6 c9f1b62809 fix: 模型切换串台 + 发送消息后滚动不到底部
1. page.tsx — loadSession 加 cancelled 竞态守卫,快速切换会话时
   丢弃过期的 fetch 结果,防止旧会话的 model/provider 覆盖新会话
2. ChatView.tsx — 去掉 if(modelName)/if(providerId) 守卫,改为
   无条件同步 props,空值走 localStorage → default fallback 链
3. ChatListPanel.tsx + types + route.ts + db.ts — 三个创建会话入口
   (handleFolderSelect/handleNewChat/handleCreateSessionInProject)
   统一从 localStorage 读取并传递 model + provider_id,DB 层写入
4. MessageList.tsx — ScrollOnStream 增加 messageCount prop,
   消息数增加时立即 scrollToBottom,覆盖乐观插入和 assistant 落地

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 12:01:10 +08:00
7418andClaude Opus 4.6 132fdeb255 perf: P0 performance optimizations — memo, base64 cleanup, startup speed, bump v0.17.5
Six independent fixes targeting startup lag, streaming jank, and excessive payloads:

Step 0 — MessageItem React.memo + useMemo (MessageItem.tsx)
- Wrap MessageItem with React.memo so historical messages skip re-render
  when `message` ref is stable during streaming
- Add useMemo for parseToolBlocks + pairTools (deps: message.content),
  parseMessageFiles (deps: text), token_usage JSON.parse (deps: message.token_usage)
- Extract assistant content rendering into separate memo'd AssistantContent component
  to cache parseBatchPlan / parseImageGen chain (deps: displayText, messageId)
- Root cause: 50 msgs × 10 SSE events/s = 500 full parses/s causing UI jank

Step 1 — Strip base64 from new messages (ChatView.tsx, route.ts)
- ChatView: remove `data: f.data` from fileMeta in display content HTML comment
- route.ts: skip base64 data in fileAttachments when filePath exists (file already on disk)
- Root cause: base64 image data inflated message content to MB-level

Step 2 — Sanitize old messages in API (messages/route.ts)
- Add stripFileData() to strip base64 `data` fields from <!--files:...--> comments
  before returning messages from GET /api/chat/sessions/:id/messages
- Handles legacy data already stored with base64

Step 3 — Reduce initial fetch limit 100 → 30 (page.tsx, messages/route.ts)
- Default limit changed from 100 to 30 on both frontend and backend
- "Load more" functionality unchanged

Step 4 — ChatListPanel dedup requests (ChatListPanel.tsx)
- Remove duplicate pathname-dependent useEffect (was causing 2 fetches per navigation)
- Add AbortController to cancel in-flight requests on new fetch
- Add 300ms debounce for event-driven fetches (session-created/session-updated)

Step 5 — Electron startup: show window before server ready (electron/main.ts)
- createWindow() now accepts optional URL, defaults to inline loading HTML
- Loading page: dark theme, centered spinner, "Starting CodePilot..." text
- Startup: startServer() → createWindow(loading) → waitForServer() → loadURL(real)
- Also updated activate handler for same early-window pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 15:00:03 +08:00
7418 00d3fef5a4 feat: refactor image generation to bypass model dependency, fix UI issues
Image Generation Refactor:
- MessageInput: add onImageGenerate callback, remove skill fetch dependency
- ChatView: implement handleImageGenerate that creates image-gen-request
  message for ImageGenConfirmation UI, then user selects params and generates
- New API route POST /api/chat/messages for DB-only message persistence
- image-gen-completed event now only persists notice to DB, no longer triggers model
- Reference images passed via module-level Map (imageGenRefStore) to avoid
  DB bloat from base64 data; user message DB content stores metadata only

Bug Fixes:
- Fix useCallback dependency missing imageGen causing stale closure reads
- Fix React Hooks order violation in MessageItem (early return before hooks)
- Fix localStorage cache collision across sessions (key now includes sessionId)
- Fix empty src attribute in FileAttachmentDisplay for DB-restored messages
- Add JSON parse fallback for malformed image-gen-request blocks

Gallery Reference Images:
- /api/media/generate saves reference images to disk, stores paths in metadata
- /api/media/gallery extracts referenceImages from metadata in mapRow
- GalleryDetail displays reference image thumbnails
- ImageGenCard supports referenceImages prop for chat display

UI Changes:
- ImageGenToggle: replace paint brush icon with text label (en: Image, zh: 生图)
- Model selector button: remove provider name display to save space
- ImageGenConfirmation: support referenceImagesData prop for base64 preview

i18n:
- Add imageGen.toggleLabel key (en: Image, zh: 生图)
2026-02-26 21:07:48 +08:00
7418andClaude Opus 4.6 c6ed594800 feat: use native OS folder picker dialog in Electron mode
Electron 环境下改用系统原生文件夹选择对话框替代自定义 FolderPicker 组件,
浏览器环境保留原有 FolderPicker 作为 fallback(浏览器沙箱无法获取绝对路径)。

- electron/main.ts: 新增 `dialog:open-folder` IPC handler,调用
  `dialog.showOpenDialog` 并设置 openDirectory + createDirectory 属性,
  macOS 下支持在对话框内新建文件夹,对话框模态绑定 mainWindow
- electron/preload.ts: 在 electronAPI 中暴露 `dialog.openFolder()` 方法
- src/types/electron.d.ts: 新建全局类型声明,统一定义 window.electronAPI
  的完整类型,消除代码中散落的 `(window as any)` 类型断言
- src/hooks/useNativeFolderPicker.ts: 新建 hook 封装平台检测和原生对话框
  调用逻辑,返回 isElectron 标志和 openNativePicker 方法
- src/components/layout/ChatListPanel.tsx: 新增 openFolderPicker() 统一入口,
  Electron 走原生对话框、浏览器走 setFolderPickerOpen(true);替换
  handleNewChat 中 4 处及文件夹按钮 onClick 共 5 处调用点;
  handleFolderSelect 改为 useCallback 并调整声明顺序避免引用前使用;
  清理 electronAPI 类型断言
- src/app/chat/[id]/page.tsx: 移除 `window as unknown as ...` 类型断言
- src/components/layout/ConnectionStatus.tsx: 移除 `(window as any)` 断言
- src/components/layout/InstallWizard.tsx: getInstallAPI() 简化为直接返回
  window.electronAPI?.install,移除 10 行手动类型定义

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:47:08 +08:00
7418andClaude Opus 4.6 448738d4f3 feat: provider/model system refactor — switch provider from chat input
Refactors the provider/model selection system so users can switch providers
directly from the chat input model selector, instead of having to go to
Settings to activate/deactivate providers.

types/index.ts:
- Add ProviderModelGroup interface for grouped provider+model data
- Extend SendMessageRequest, ClaudeStreamOptions, ChatSession with provider_id

db.ts:
- Migration: add provider_id column to chat_sessions
- Migration: convert is_active=1 provider to default_provider_id setting
- New helpers: updateSessionProviderId, getDefaultProviderId, setDefaultProviderId

providers/models/route.ts:
- Rewrite to return { groups, default_provider_id } instead of flat model list
- Each configured provider becomes a group; env vars become "Environment" group
- Deduplicate models within a group (e.g. GLM sonnet/opus both → "GLM-4.7" once)

chat/route.ts:
- Accept provider_id from request body
- Resolution chain: request → session → default_provider_id → env vars
- provider_id='env' is sentinel for environment variable fallback
- Pass resolved provider object to streamClaude()

claude-client.ts:
- Use options.provider ?? getActiveProvider() so explicit provider takes priority

ChatView.tsx:
- Track currentProviderId state, pass provider_id in fetch body
- New handleProviderModelChange callback

MessageInput.tsx:
- New props: providerId, onProviderModelChange
- Replace dynamicModels/activeProviderName with providerGroups/defaultProviderId
- Model selector UI: grouped by provider with section headers, ✓ for active item
- Persist both model and provider ID to localStorage

chat/page.tsx, chat/[id]/page.tsx:
- Track and pass provider_id through the component chain

ProviderManager.tsx:
- Remove handleActivate/handleDeactivate, activatingId, Apply/Disable buttons
- Remove Active badge and green border styling
- Simplify env variable banner
- Add provider-changed event dispatch to save/delete handlers

providers/route.ts:
- GET response now includes default_provider_id field

i18n (en.ts, zh.ts):
- Add provider.default, provider.setDefault, provider.environment translations

Backward compatible: old sessions without provider_id default to '' which
triggers the fallback chain. is_active column preserved but no longer written.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 00:22:18 +08:00
7418 c7c3ba5302 feat: v0.13.0 — add i18n support with Chinese/English translations
Implements a zero-dependency i18n framework with TypeScript type safety.
Thanks to @gy212 for the original i18n infrastructure design (PR #89)
and comprehensive translation files (PR #90-96) that inspired this implementation.

## New files (5)

- src/i18n/en.ts: English translations (source of truth, ~270 keys),
  exports TranslationKey type for compile-time key completeness
- src/i18n/zh.ts: Chinese translations, typed as Record<TranslationKey, string>
- src/i18n/index.ts: translate() function with {param} interpolation,
  Locale type, SUPPORTED_LOCALES array
- src/components/layout/I18nProvider.tsx: React Context provider,
  loads/persists locale preference via /api/settings/app
- src/hooks/useTranslation.ts: useTranslation() hook returning
  { locale, setLocale, t }

## Infrastructure wiring

- src/app/layout.tsx: I18nProvider wrapped inside ThemeProvider
- src/app/api/settings/app/route.ts: added "locale" to ALLOWED_KEYS
  for persistence

## Language picker

- src/components/settings/GeneralSection.tsx: added language selection
  dropdown (Select component) in Settings > General, using
  SUPPORTED_LOCALES and setLocale()

## String replacements (33 component files)

All hardcoded user-facing strings replaced with t() calls:

High priority (chat):
- ChatListPanel.tsx: session list, search, empty state, relative time
- NavRail.tsx: nav labels, theme toggle, auto-approve indicator
- MessageInput.tsx: command descriptions, mode labels, attach tooltip
- MessageList.tsx: empty state, load earlier, loading indicator
- StreamingMessage.tsx: thinking, permission actions
- chat/[id]/page.tsx: session title fallback

Settings:
- GeneralSection.tsx: update card, auto-approve section, warning dialog
- SettingsLayout.tsx: title, description, sidebar labels
- CliSettingsSection.tsx: form/json tabs, buttons, dynamic field labels
  (skipDangerousModePermissionPrompt etc.), Enabled/Disabled states
- ProviderForm.tsx: dialog title, form labels, buttons
- ProviderManager.tsx: status badges, delete dialog, section headers

Extensions:
- extensions/page.tsx: title, tab labels
- SkillsManager.tsx: empty state, loading, search
- SkillEditor.tsx: toolbar buttons, placeholder
- SkillListItem.tsx: delete confirmation
- CreateSkillDialog.tsx: form labels, scope, templates, validation

Plugins:
- McpManager.tsx: header, tabs, loading state
- McpServerList.tsx: empty state, badges, field labels
- McpServerEditor.tsx: dialog title, form labels, buttons
- ConfigEditor.tsx: save/format buttons

Layout:
- RightPanel.tsx: panel header, open/close tooltips
- FileTree.tsx: search placeholder, empty states
- FilePreview.tsx: back button, line count, copy path
- DocPreview.tsx: iframe title
- ConnectionStatus.tsx: install prompts, version display
- InstallWizard.tsx: phase descriptions, button labels
- ImportSessionDialog.tsx: search, relative time, import buttons
- FolderPicker.tsx: dialog title, loading, buttons
- TaskList.tsx: filter tabs, input placeholder, empty states
2026-02-24 16:50:57 +08:00
7418 85940dfde3 fix: align title bar headers across panels, fix macOS Dock icon size
Title bar alignment (AppShell.tsx, page.tsx, DocPreview.tsx):
- Reduce AppShell draggable spacer from h-11 to h-5 to match side panels' mt-5
- Chat title bar uses normal flow h-12 instead of absolute positioning hack
- DocPreview header changed from h-10 to h-12 mt-5
- All three columns now align at the same vertical position

macOS Dock icon (build/icon.png, build/icon.icns):
- Add 100px transparent padding on all sides (content 824x824 in 1024x1024 canvas)
- Follows Apple Human Interface Guidelines for macOS app icons
- Regenerated icon.icns with all required sizes
- Original icon backed up as icon-original.png

Remove start execution button (ChatView.tsx):
- Remove showStartExecution logic and handleStartExecution callback
- Mode switching is now handled automatically by SDK
2026-02-24 15:30:42 +08:00
7418 9e7ccfefaa feat: v0.12.0 — fix streaming tool UI persistence, file tree UX improvements, unify icon library
Streaming tool UI fix (ChatView.tsx):
- Add toolUsesRef/toolResultsRef to reliably track tool data across closures
- Serialize tool_use/tool_result into JSON content-blocks array when building
  assistant message after stream ends, matching backend storage format
- MessageItem.parseToolBlocks() now renders tool UI from history messages
- Clean up refs in finally block and timeout auto-retry path

File tree UX (FileTree.tsx, file-tree.tsx):
- Remove folder path header from right panel, merge search + refresh into one row
- Remove file icon spacer (size-5) so file icons align flush with search box
- Change folder icon color from blue to muted-foreground

Chat list panel (ChatListPanel.tsx):
- Folder header: expanded uses FolderOpenIcon, collapsed uses Folder01Icon
- Add tooltip showing full working directory path on folder hover
- Double-click folder header opens in system file manager

Title bar folder name (chat/[id]/page.tsx):
- Add tooltip with full path on project name hover
- Click opens folder in Finder/Explorer via Electron IPC or API fallback

Open folder support:
- electron/main.ts: add shell:open-path IPC handler via shell.openPath
- electron/preload.ts: expose electronAPI.shell.openPath to renderer
- api/files/open/route.ts: fallback API route using open/explorer/xdg-open

Unify icon library — migrate app-level components from Lucide to Hugeicons:
- file-tree.tsx: folder, file, chevron, plus icons
- tool-actions-group.tsx: chevron expand icon
- MessageItem.tsx: copy, check, expand/collapse icons
- MessageInput.tsx: stop button (SquareIcon → StopIcon)
- FileCard.tsx: file icon
- ImageLightbox.tsx: left/right navigation arrows
- InstallWizard.tsx: status icons (check, x, minus, loader, circle, copy, download)
- Keep Lucide in ui/ and ai-elements/ base layers (shadcn standard)
2026-02-24 12:32:03 +08:00
7418 9c13f043d0 feat: v0.11.0 — SDK settingSources, nested session fix, dynamic models, UX improvements
Core fixes:
- Add settingSources to SDK options so CodePilot loads user/project settings
  like the CLI (fixes WebFetch timeouts, tool permission issues)
- Delete CLAUDECODE env var to prevent nested session detection when
  CodePilot runs alongside Claude Code CLI (fixes 'exited with code 1')
- Remove manual MCP config reading (SDK handles it via settingSources)
- Remove hardcoded timeout env vars (SDK loads from user settings)

UX improvements:
- Remember last selected model across sessions (localStorage)
- Same-project new chat skips directory picker
- File tree auto-refreshes on project switch and after each tool completes
- Left sidebar projects default collapsed (only most recent expanded)
- Right-side file tree defaults collapsed
- Fix file tree icon alignment (spacer size-4 → size-5)
- Dynamic model list from /api/providers/models endpoint
- Provider change dispatches event to refresh model selector
- Improved error messages with provider name and troubleshooting hints
- Fix context button file type matching for extension patterns
- Default tool timeout 120s → 300s
2026-02-24 03:08:17 +08:00
7418andClaude Opus 4.6 916b8889b7 fix: require valid project directory for session creation
- Backend: POST /api/chat/sessions now validates working_directory
  exists (400 MISSING_DIRECTORY / INVALID_DIRECTORY)
- db.ts: remove process.cwd() fallback in createSession
- ChatListPanel: validate last-working-directory before use,
  open FolderPicker when invalid; add SSR guard for localStorage
- NewChatPage: block sending without a project directory selected

Bump version to 0.10.5.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:37:51 +08:00
7418andClaude Opus 4.6 ae8a905581 feat: inline title editing, simplify right panel, polish input toolbar
- Add inline title editing in chat header (hover to reveal pencil icon,
  click to edit, Enter/Blur to save, Escape to cancel)
- Simplify right panel: remove Name section, rename header to "Files"
- Align top headers across all three columns (left/center/right)
- Remove folder picker from message input (project already set via sidebar)
- Change attachment icon from paperclip to plus sign
- Remove icon from mode selector, move model selector next to mode selector
- Fix FileTree search icon alignment and left padding consistency
- Add background to chat list delete button to prevent text overlap

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 15:23:36 +08:00
7418andClaude Opus 4.6 5a57098ffd feat: group chat sidebar by project folder with collapsible sections
Restructure the chat list panel to group sessions by working directory
(project folder), similar to Codex App. Each project folder is shown as
a collapsible section with a folder icon and project name. Features:

- Sessions grouped by working_directory, sorted by most recent activity
- Collapsible folder sections with persistent state in localStorage
- Per-folder "+" button to create new chat in that project
- "Open project folder" button in sidebar header with FolderPicker dialog
- NavRail new-chat button creates sessions in most recent project
- Chat header shows "projectName / sessionTitle" breadcrumb
- Search filters both folder names and session titles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 14:46:50 +08:00
7418andClaude Opus 4.6 af2830db9b feat: add cursor-based message pagination (P0-3)
Backend returns paginated messages with hasMore flag. Frontend shows
"Load earlier messages" button with scroll anchor preservation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:24:27 +08:00
7418andClaude Opus 4.6 a42c0e312d fix: resolve session switching stuck on loading spinner
Race condition when quickly switching chat sessions caused the UI to get
stuck on loading. Added cancellation flags and key-based remounting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:19:25 +08:00
7418andClaude Opus 4.6 454c7b0e6c fix: show detailed error messages for session creation failures
Return full error stack traces from the sessions API and display
the actual server error in the frontend, instead of a generic
"Failed to create session" message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 22:29:44 +08:00
7418andClaude Opus 4.6 ff1fb38735 feat: CodePilot - desktop GUI for Claude Code
Full-featured desktop application built with Next.js + Electron:
- Streaming chat with Claude Code via Agent SDK
- Session management with SQLite persistence
- Project file tree and file preview
- MCP server and custom skills management
- Permission controls with Code/Plan/Ask modes
- Dark/light theme, token tracking
- macOS DMG packaging with electron-builder

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 21:04:05 +08:00