diff --git a/desktop/package.json b/desktop/package.json index 25557b6b..556b58fe 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,7 +1,7 @@ { "name": "claude-code-desktop", "private": true, - "version": "0.1.3", + "version": "0.1.4", "type": "module", "scripts": { "dev": "vite", diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index 956b476b..31b5b306 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -323,7 +323,7 @@ dependencies = [ [[package]] name = "claude-code-desktop" -version = "0.1.3" +version = "0.1.4" dependencies = [ "serde", "serde_json", diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index ef390f63..8ce03af1 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "claude-code-desktop" -version = "0.1.3" +version = "0.1.4" edition = "2021" [lib] diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index 861fb7fe..5cfd8062 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/nicegui/nicegui/main/nicegui/static/tauri-schema-v2.json", "productName": "Claude Code Haha", - "version": "0.1.3", + "version": "0.1.4", "identifier": "com.claude-code-haha.desktop", "build": { "frontendDist": "../dist", diff --git a/desktop/src/components/chat/CodeViewer.test.tsx b/desktop/src/components/chat/CodeViewer.test.tsx index 9f654f6b..90b602e4 100644 --- a/desktop/src/components/chat/CodeViewer.test.tsx +++ b/desktop/src/components/chat/CodeViewer.test.tsx @@ -2,18 +2,27 @@ import { render, screen, waitFor } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' import { CodeViewer } from './CodeViewer' +const mockShikiState = vi.hoisted(() => ({ + lastProps: null as Record | null, + engine: { kind: 'js-regex-engine' as const }, +})) + vi.mock('react-shiki', () => ({ - ShikiHighlighter: ({ children }: { children: string }) => ( -
- {children} -
- ), + createJavaScriptRegexEngine: () => mockShikiState.engine, + ShikiHighlighter: (props: { children: string } & Record) => { + mockShikiState.lastProps = props + return ( +
+ {props.children} +
+ ) + }, })) describe('CodeViewer', () => { it('keeps the same inner padding for highlighted code content', async () => { const { container } = render( - , + , ) await waitFor(() => { @@ -23,5 +32,9 @@ describe('CodeViewer', () => { const contentWrapper = container.querySelector('[data-code-viewer-content]') as HTMLElement | null expect(contentWrapper).toBeTruthy() expect(contentWrapper?.style.padding).toBe('0.5rem 12px') + + const codeArea = container.querySelector('.code-viewer-area') as HTMLElement | null + expect(codeArea?.getAttribute('data-has-line-numbers')).toBe('true') + expect(mockShikiState.lastProps?.engine).toBe(mockShikiState.engine) }) }) diff --git a/desktop/src/components/chat/CodeViewer.tsx b/desktop/src/components/chat/CodeViewer.tsx index 4b337cde..a6cb8726 100644 --- a/desktop/src/components/chat/CodeViewer.tsx +++ b/desktop/src/components/chat/CodeViewer.tsx @@ -1,5 +1,5 @@ import { useState, useRef, useEffect } from 'react' -import { ShikiHighlighter } from 'react-shiki' +import { ShikiHighlighter, createJavaScriptRegexEngine } from 'react-shiki' import 'react-shiki/css' import { CopyButton } from '../shared/CopyButton' @@ -46,6 +46,8 @@ const warmCodeTheme = { } const CODE_AREA_PADDING = '0.5rem 12px' +const CODE_LINE_HEIGHT = 1.3 +const shikiEngine = createJavaScriptRegexEngine({ forgiving: true }) /** * Wraps ShikiHighlighter with a plain-text fallback so the code area @@ -76,7 +78,11 @@ function CodeArea({ code, language, showLineNumbers }: { code: string; language? }, [code, language]) return ( -
+
{/* Plain-text fallback shown until Shiki finishes highlighting */} {!loaded && (
           {code}
@@ -128,7 +135,7 @@ function CodeArea({ code, language, showLineNumbers }: { code: string; language?
   )
 }
 
-export function CodeViewer({ code, language, maxLines = 20, showLineNumbers = true }: Props) {
+export function CodeViewer({ code, language, maxLines = 20, showLineNumbers = false }: Props) {
   const [expanded, setExpanded] = useState(false)
 
   const allLines = code.split('\n')
diff --git a/desktop/src/theme/globals.css b/desktop/src/theme/globals.css
index a24d6125..eeb72334 100644
--- a/desktop/src/theme/globals.css
+++ b/desktop/src/theme/globals.css
@@ -70,24 +70,27 @@
 
 /* ─── Shiki code viewer ───────────────────────────────────────── */
 .code-viewer-area .line {
-  display: inline-block;
-  width: 100%;
-  padding: 1px 12px;
+  display: block;
+  padding: 0 12px;
 }
 
 .code-viewer-area .has-line-numbers .line {
-  padding-left: 0;
+  padding-left: 12px;
 }
 
 .code-viewer-area .line:hover {
   background: rgba(0, 0, 0, 0.03);
 }
 
+.code-viewer-area .line-numbers::before {
+  margin-right: 1.5ch;
+}
+
 .code-viewer-area .has-line-numbers {
   --line-numbers-foreground: rgba(135, 115, 109, 0.5);
   --line-numbers-width: 2.5ch;
-  --line-numbers-padding-left: 1ch;
-  --line-numbers-padding-right: 1.5ch;
+  --line-numbers-padding-left: 0;
+  --line-numbers-padding-right: 0;
   --line-numbers-font-size: 11px;
 }