diff --git a/CHANGELOG.md b/CHANGELOG.md index 73a2a6bf..7a6e12c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to CloudCLI UI will be documented in this file. +## [1.21.0](https://github.com/siteboon/claudecodeui/compare/v1.20.1...v1.21.0) (2026-02-27) + +### New Features + +* add copy icon for user messages ([#449](https://github.com/siteboon/claudecodeui/issues/449)) ([b359c51](https://github.com/siteboon/claudecodeui/commit/b359c515277b4266fde2fb9a29b5356949c07c4f)) +* Google's gemini-cli integration ([#422](https://github.com/siteboon/claudecodeui/issues/422)) ([a367edd](https://github.com/siteboon/claudecodeui/commit/a367edd51578608b3281373cb4a95169dbf17f89)) +* persist active tab across reloads via localStorage ([#414](https://github.com/siteboon/claudecodeui/issues/414)) ([e3b6892](https://github.com/siteboon/claudecodeui/commit/e3b689214f11d549ffe1b3a347476d58f25c5aca)), closes [#387](https://github.com/siteboon/claudecodeui/issues/387) + +### Bug Fixes + +* add support for Codex in the shell ([#424](https://github.com/siteboon/claudecodeui/issues/424)) ([23801e9](https://github.com/siteboon/claudecodeui/commit/23801e9cc15d2b8d1bfc6e39aee2fae93226d1ad)) + +### Maintenance + +* upgrade @anthropic-ai/claude-agent-sdk to version 0.2.59 and add model usage logging ([#446](https://github.com/siteboon/claudecodeui/issues/446)) ([917c353](https://github.com/siteboon/claudecodeui/commit/917c353115653ee288bf97be01f62fad24123cbc)) +* upgrade better-sqlite to latest version to support node 25 ([#445](https://github.com/siteboon/claudecodeui/issues/445)) ([4ab94fc](https://github.com/siteboon/claudecodeui/commit/4ab94fce4257e1e20370fa83fa4c0f6fadbb8a2b)) + ## [1.20.1](https://github.com/siteboon/claudecodeui/compare/v1.19.1...v1.20.1) (2026-02-23) ### New Features diff --git a/README.md b/README.md index 7b5d5dda..209a74da 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ -A desktop and mobile UI for [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Cursor CLI](https://docs.cursor.com/en/cli/overview) and [Codex](https://developers.openai.com/codex). You can use it locally or remotely to view your active projects and sessions in Claude Code, Cursor, or Codex and make changes to them from everywhere (mobile or desktop). This gives you a proper interface that works everywhere. +A desktop and mobile UI for [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Cursor CLI](https://docs.cursor.com/en/cli/overview), [Codex](https://developers.openai.com/codex), and [Gemini-CLI](https://geminicli.com/). You can use it locally or remotely to view your active projects and sessions and make changes to them from everywhere (mobile or desktop). This gives you a proper interface that works everywhere. siteboon%2Fclaudecodeui | Trendshift
English · 한국어 · 中文 · 日本語
@@ -44,14 +44,14 @@ A desktop and mobile UI for [Claude Code](https://docs.anthropic.com/en/docs/cla ## Features -- **Responsive Design** - Works seamlessly across desktop, tablet, and mobile so you can also use Claude Code, Cursor, or Codex from mobile -- **Interactive Chat Interface** - Built-in chat interface for seamless communication with Claude Code, Cursor, or Codex -- **Integrated Shell Terminal** - Direct access to Claude Code, Cursor CLI, or Codex through built-in shell functionality +- **Responsive Design** - Works seamlessly across desktop, tablet, and mobile so you can also use Agents from mobile +- **Interactive Chat Interface** - Built-in chat interface for seamless communication with the Agents +- **Integrated Shell Terminal** - Direct access to the Agents CLI through built-in shell functionality - **File Explorer** - Interactive file tree with syntax highlighting and live editing - **Git Explorer** - View, stage and commit your changes. You can also switch branches - **Session Management** - Resume conversations, manage multiple sessions, and track history - **TaskMaster AI Integration** *(Optional)* - Advanced project management with AI-powered task planning, PRD parsing, and workflow automation -- **Model Compatibility** - Works with Claude Sonnet 4.5, Opus 4.5, and GPT-5.2 +- **Model Compatibility** - Works with Claude Sonnet 4.5, Opus 4.5, GPT-5.2, and Gemini. ## Quick Start @@ -61,7 +61,8 @@ A desktop and mobile UI for [Claude Code](https://docs.anthropic.com/en/docs/cla - [Node.js](https://nodejs.org/) v22 or higher - [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) installed and configured, and/or - [Cursor CLI](https://docs.cursor.com/en/cli/overview) installed and configured, and/or -- [Codex](https://developers.openai.com/codex) installed and configured +- [Codex](https://developers.openai.com/codex) installed and configured, and/or +- [Gemini-CLI](https://geminicli.com/) installed and configured ### One-click Operation (Recommended) @@ -279,7 +280,7 @@ session counts ### Backend (Node.js + Express) - **Express Server** - RESTful API with static file serving - **WebSocket Server** - Communication for chats and project refresh -- **Agent Integration (Claude Code / Cursor CLI / Codex)** - Process spawning and management +- **Agent Integration (Claude Code / Cursor CLI / Codex / Gemini CLI)** - Process spawning and management - **File System API** - Exposing file browser for projects ### Frontend (React + Vite) @@ -327,6 +328,7 @@ This project is open source and free to use, modify, and distribute under the GP - **[Claude Code](https://docs.anthropic.com/en/docs/claude-code)** - Anthropic's official CLI - **[Cursor CLI](https://docs.cursor.com/en/cli/overview)** - Cursor's official CLI - **[Codex](https://developers.openai.com/codex)** - OpenAI Codex +- **[Gemini-CLI](https://geminicli.com/)** - Google Gemini CLI - **[React](https://react.dev/)** - User interface library - **[Vite](https://vitejs.dev/)** - Fast build tool and dev server - **[Tailwind CSS](https://tailwindcss.com/)** - Utility-first CSS framework diff --git a/package-lock.json b/package-lock.json index f5be1b45..08e511cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@siteboon/claude-code-ui", - "version": "1.20.1", + "version": "1.21.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@siteboon/claude-code-ui", - "version": "1.20.1", + "version": "1.21.0", "hasInstallScript": true, "license": "GPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 30fa2cdf..368129b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@siteboon/claude-code-ui", - "version": "1.20.1", + "version": "1.21.0", "description": "A web-based UI for Claude Code CLI", "type": "module", "main": "server/index.js", @@ -119,4 +119,4 @@ "typescript": "^5.9.3", "vite": "^7.0.4" } -} \ No newline at end of file +} diff --git a/server/claude-sdk.js b/server/claude-sdk.js index 7cbb55f9..7b054dd6 100644 --- a/server/claude-sdk.js +++ b/server/claude-sdk.js @@ -660,9 +660,6 @@ async function queryClaudeSDK(command, options = {}, ws) { console.log('No session_id in message or already captured. message.session_id:', message.session_id, 'capturedSessionId:', capturedSessionId); } - // logs which model was used in the message - console.log("---> Model was sent using:", Object.keys(message.modelUsage || {})); - // Transform and send message to WebSocket const transformedMessage = transformMessage(message); ws.send({ @@ -673,6 +670,10 @@ async function queryClaudeSDK(command, options = {}, ws) { // Extract and send token budget updates from result messages if (message.type === 'result') { + const models = Object.keys(message.modelUsage || {}); + if (models.length > 0) { + console.log("---> Model was sent using:", models); + } const tokenBudget = extractTokenBudget(message); if (tokenBudget) { console.log('Token budget from modelUsage:', tokenBudget); diff --git a/server/index.js b/server/index.js index 1cfc91c7..a46c2755 100755 --- a/server/index.js +++ b/server/index.js @@ -1220,7 +1220,7 @@ function handleShellConnection(ws) { if (hasSession && sessionId) { try { // Gemini CLI enforces its own native session IDs, unlike other agents that accept arbitrary string names. - // The UI only knows about its internal generated `sessionId` (e.g. gemini_1234). + // The UI only knows about its internal generated `sessionId` (e.g. gemini_1234). // We must fetch the mapping from the backend session manager to pass the native `cliSessionId` to the shell. const sess = sessionManager.getSession(sessionId); if (sess && sess.cliSessionId) { @@ -1793,8 +1793,8 @@ app.get('/api/projects/:projectName/sessions/:sessionId/token-usage', authentica // Construct the JSONL file path // Claude stores session files in ~/.claude/projects/[encoded-project-path]/[session-id].jsonl - // The encoding replaces /, spaces, ~, and _ with - - const encodedPath = projectPath.replace(/[\\/:\s~_]/g, '-'); + // The encoding replaces any non-alphanumeric character (except -) with - + const encodedPath = projectPath.replace(/[^a-zA-Z0-9-]/g, '-'); const projectDir = path.join(homeDir, '.claude', 'projects', encodedPath); const jsonlPath = path.join(projectDir, `${safeSessionId}.jsonl`); diff --git a/src/components/chat/view/subcomponents/MessageComponent.tsx b/src/components/chat/view/subcomponents/MessageComponent.tsx index 2abc1700..fe12f427 100644 --- a/src/components/chat/view/subcomponents/MessageComponent.tsx +++ b/src/components/chat/view/subcomponents/MessageComponent.tsx @@ -10,6 +10,7 @@ import type { import { Markdown } from './Markdown'; import { formatUsageLimitText } from '../../utils/chatFormatting'; import { getClaudePermissionSuggestion } from '../../utils/chatPermissions'; +import { copyTextToClipboard } from '../../../../utils/clipboard'; import type { Project } from '../../../../types/app'; import { ToolRenderer, shouldHideToolResult } from '../../tools'; @@ -53,6 +54,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile const [isExpanded, setIsExpanded] = React.useState(false); const permissionSuggestion = getClaudePermissionSuggestion(message, provider); const [permissionGrantState, setPermissionGrantState] = React.useState('idle'); + const [messageCopied, setMessageCopied] = React.useState(false); React.useEffect(() => { @@ -100,7 +102,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile {message.type === 'user' ? ( /* User message bubble on the right */
-
+
{message.content}
@@ -117,8 +119,45 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile ))}
)} -
- {formattedTime} +
+ + {formattedTime}
{!isGrouped && ( diff --git a/src/components/sidebar/view/subcomponents/SidebarProjectList.tsx b/src/components/sidebar/view/subcomponents/SidebarProjectList.tsx index 55710638..1e1dbd55 100644 --- a/src/components/sidebar/view/subcomponents/SidebarProjectList.tsx +++ b/src/components/sidebar/view/subcomponents/SidebarProjectList.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react'; import type { TFunction } from 'i18next'; import type { LoadingProgress, Project, ProjectSession, SessionProvider } from '../../../../types/app'; import type { @@ -103,6 +104,15 @@ export default function SidebarProjectList({ /> ); + useEffect(() => { + let baseTitle = 'CloudCLI UI'; + const displayName = selectedProject?.displayName?.trim(); + if (displayName) { + baseTitle = `${displayName} - ${baseTitle}`; + } + document.title = baseTitle; + }, [selectedProject]); + const showProjects = !isLoading && projects.length > 0 && filteredProjects.length > 0; return ( diff --git a/src/i18n/locales/en/chat.json b/src/i18n/locales/en/chat.json index b907f180..dc2b3109 100644 --- a/src/i18n/locales/en/chat.json +++ b/src/i18n/locales/en/chat.json @@ -4,6 +4,10 @@ "copied": "Copied", "copyCode": "Copy code" }, + "copyMessage": { + "copy": "Copy message", + "copied": "Message copied" + }, "messageTypes": { "user": "U", "error": "Error", diff --git a/src/i18n/locales/ja/chat.json b/src/i18n/locales/ja/chat.json index 0d81a1ad..2c3dda4d 100644 --- a/src/i18n/locales/ja/chat.json +++ b/src/i18n/locales/ja/chat.json @@ -4,6 +4,10 @@ "copied": "コピーしました", "copyCode": "コードをコピー" }, + "copyMessage": { + "copy": "メッセージをコピー", + "copied": "メッセージをコピーしました" + }, "messageTypes": { "user": "U", "error": "エラー", diff --git a/src/i18n/locales/ko/chat.json b/src/i18n/locales/ko/chat.json index cddc89af..ca66e3a9 100644 --- a/src/i18n/locales/ko/chat.json +++ b/src/i18n/locales/ko/chat.json @@ -4,6 +4,10 @@ "copied": "복사됨", "copyCode": "코드 복사" }, + "copyMessage": { + "copy": "메시지 복사", + "copied": "메시지 복사됨" + }, "messageTypes": { "user": "U", "error": "오류", diff --git a/src/i18n/locales/zh-CN/chat.json b/src/i18n/locales/zh-CN/chat.json index 678ea171..1fdbc462 100644 --- a/src/i18n/locales/zh-CN/chat.json +++ b/src/i18n/locales/zh-CN/chat.json @@ -4,6 +4,10 @@ "copied": "已复制", "copyCode": "复制代码" }, + "copyMessage": { + "copy": "复制消息", + "copied": "消息已复制" + }, "messageTypes": { "user": "U", "error": "错误",