Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@ All notable changes to DeepCode are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.6] — 2026-05-28

### 🐛 Critical fix — Bash tool calls were always reporting "error"
The Rust output structs (`ReadOk`, `EditOk`, `BashOk`) returned fields
in snake_case (`exit_code`, `lines_total`, `diff_preview`) while the
TS wrappers read them in camelCase. Result: `r.exitCode` was always
`undefined`, so `undefined !== 0` made every Bash tool result render
with a red `✕ error` badge — even when the underlying command had
exit code 0. Read + Edit silently dropped diff previews + line totals
for the same reason.

Fixed by adding `#[serde(rename_all = "camelCase")]` on the three
output structs. Glob and Grep were already single-word fields, no
change needed.

### Polish carry-over
- **Keyboard shortcuts**: ⌘N starts a new session, ⌘, opens Settings,
⌘/ opens About. New `src/lib/keyboard.ts` helper.
- **Switching project now clears chat history** so the next message
runs against the fresh cwd (was: old conversation lingered with
new project context).

## [0.1.5] — 2026-05-28

### Polish + dead-code removal
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "deepcode-cli",
"version": "0.1.5",
"version": "0.1.6",
"description": "DeepCode CLI — DeepSeek-powered AI coding agent, parity with Claude Code",
"license": "MIT",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@deepcode/desktop",
"version": "0.1.5",
"version": "0.1.6",
"private": true,
"description": "DeepCode Mac desktop client — Tauri + React",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "deepcode_desktop"
version = "0.1.5"
version = "0.1.6"
description = "DeepCode Mac desktop client"
authors = ["oratis"]
edition = "2021"
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src-tauri/src/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use tokio::process::Command;
// ──────────────────────────────────────────────────────────────────────────

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadOk {
pub content: String,
pub lines_total: usize,
Expand Down Expand Up @@ -101,6 +102,7 @@ pub struct EditInput {
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EditOk {
pub replaced: usize,
pub diff_preview: String,
Expand Down Expand Up @@ -155,6 +157,7 @@ pub struct BashInput {
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BashOk {
pub stdout: String,
pub stderr: String,
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "DeepCode",
"version": "0.1.5",
"version": "0.1.6",
"identifier": "dev.deepcode.desktop",
"build": {
"frontendDist": "../dist",
Expand Down
22 changes: 21 additions & 1 deletion apps/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { InspectorRail } from './components/InspectorRail.js';
import { ProjectPickerOverlay } from './components/ProjectPickerOverlay.js';
import { Sidebar } from './components/Sidebar.js';
import { UpdateBanner } from './components/UpdateBanner.js';
import { registerShortcut } from './lib/keyboard.js';
import { clearHistory as clearAgentHistory } from './lib/mac-agent.js';
import { loadProjectPath, saveProjectPath } from './lib/project.js';
import { onUpdateDownloaded, startUpdaterPolling } from './lib/updater.js';
Expand Down Expand Up @@ -36,9 +37,23 @@ export function App(): JSX.Element {
const offShim = window.deepcode.onUpdateDownloaded((info) => setUpdate(info));
const offReal = onUpdateDownloaded((info) => setUpdate(info));
startUpdaterPolling();

// Global keyboard shortcuts that mirror the sidebar hints.
const offN = registerShortcut('meta+n', () => {
clearAgentHistory();
setActiveSessionId(null);
setScreen('repl');
setSessionEpoch((k) => k + 1);
});
const offComma = registerShortcut('meta+,', () => setScreen('settings'));
const offSlash = registerShortcut('meta+/', () => setScreen('about'));

return () => {
offShim();
offReal();
offN();
offComma();
offSlash();
};
}, []);

Expand Down Expand Up @@ -95,8 +110,13 @@ export function App(): JSX.Element {
setSessionEpoch((k) => k + 1);
}}
onSwitchProject={async () => {
// Force-show the picker again by clearing state.
// Force-show the picker again by clearing state. Also clear
// the in-memory conversation so the next session starts
// fresh in the new project's cwd.
clearAgentHistory();
setProjectPath(null);
setActiveSessionId(null);
setSessionEpoch((k) => k + 1);
}}
/>
<main className="chat-main" key={`main-${sessionEpoch}`}>
Expand Down
48 changes: 48 additions & 0 deletions apps/desktop/src/lib/keyboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Global keyboard shortcut helper.
// React per-component listeners would have to all coordinate; instead we
// expose a single function that hooks the window once at App-mount and
// dispatches by chord. Chords use lowercase letters with modifiers
// prefixed: 'meta+n', 'meta+,', 'meta+.', 'meta+\\'.

type Handler = (e: KeyboardEvent) => void;

const handlers = new Map<string, Handler>();

function eventChord(e: KeyboardEvent): string {
const parts: string[] = [];
if (e.metaKey) parts.push('meta');
if (e.ctrlKey) parts.push('ctrl');
if (e.shiftKey) parts.push('shift');
if (e.altKey) parts.push('alt');
parts.push(e.key.toLowerCase());
return parts.join('+');
}

let installed = false;
function install(): void {
if (installed) return;
installed = true;
window.addEventListener('keydown', (e) => {
const chord = eventChord(e);
const h = handlers.get(chord);
if (h) {
// Don't preempt the target — composer textarea handles its own keys.
// We only fire global shortcuts when the event target isn't a focused
// input that wants the key. ⌘+letter is rarely consumed by inputs
// (browsers route them out), so a simple metaKey gate is enough.
if (e.metaKey || e.ctrlKey) {
e.preventDefault();
h(e);
}
}
});
}

/** Register a keyboard shortcut. Returns an unbind function. */
export function registerShortcut(chord: string, handler: Handler): () => void {
install();
handlers.set(chord, handler);
return () => {
if (handlers.get(chord) === handler) handlers.delete(chord);
};
}
Loading