feat: batch of fixes and features (#56)
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 57s
CI / Lint & Test (push) Successful in 14m14s
CI / Build Linux (push) Successful in 16m45s
CI / Build Windows (cross-compile) (push) Successful in 26m50s

## Summary

This PR includes a batch of bug fixes and new features:

### Bug Fixes
- **Links in chat history now open in default browser** instead of navigating within the app
  - Closes #54
- **Allow spaces in tab names** - space key no longer acts like enter when renaming tabs
  - Closes #52

### New Features
- **`/cd` command** - Change the working directory of an active tab with context preservation
  - Closes #55
- **`/search` command** - Search and highlight matches within the conversation
  - Closes #32

## Test Plan
- [ ] Click a link in chat history and verify it opens in the default browser
- [ ] Rename a tab and verify spaces can be typed
- [ ] Use `/cd <path>` and verify the directory changes while preserving conversation context
- [ ] Use `/search <query>` and verify matches are highlighted in yellow
- [ ] Use `/search` with no args to clear the search highlighting

 This PR was created with help from Hikari~ 🌸

Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Reviewed-on: #56
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #56.
This commit is contained in:
2026-01-23 11:59:21 -08:00
committed by Naomi Carrigan
parent 947e56ef41
commit 94991796be
10 changed files with 369 additions and 5 deletions
+86
View File
@@ -3,6 +3,7 @@ import { invoke } from "@tauri-apps/api/core";
import { claudeStore } from "$lib/stores/claude";
import { characterState } from "$lib/stores/character";
import { setSkipNextGreeting } from "$lib/tauri";
import { searchState } from "$lib/stores/search";
export interface SlashCommand {
name: string;
@@ -11,6 +12,71 @@ export interface SlashCommand {
execute: (args: string) => Promise<void> | void;
}
async function changeDirectory(path: string): Promise<void> {
const conversationId = get(claudeStore.activeConversationId);
if (!conversationId) {
claudeStore.addLine("error", "No active conversation");
return;
}
if (!path.trim()) {
const currentDir = get(claudeStore.currentWorkingDirectory);
claudeStore.addLine("system", `Current directory: ${currentDir}`);
return;
}
try {
characterState.setState("thinking");
claudeStore.addLine("system", `Changing directory to: ${path}`);
const currentDir = get(claudeStore.currentWorkingDirectory);
const validatedPath = await invoke<string>("validate_directory", { path, currentDir });
// Capture conversation history before disconnecting
const conversationHistory = claudeStore.getConversationHistory();
await invoke("stop_claude", { conversationId });
// Wait for clean shutdown
await new Promise((resolve) => setTimeout(resolve, 500));
claudeStore.setWorkingDirectory(validatedPath);
setSkipNextGreeting(true);
await invoke("start_claude", {
conversationId,
options: {
working_dir: validatedPath,
},
});
// Wait for connection to establish
await new Promise((resolve) => setTimeout(resolve, 1000));
// Restore context if there was conversation history
if (conversationHistory) {
const contextMessage = `[CONTEXT RESTORATION]
I just changed the working directory from ${currentDir} to ${validatedPath}. Here's our conversation so far:
${conversationHistory}
Please continue where we left off. You are now operating in the new directory.`;
await invoke("send_prompt", {
conversationId,
message: contextMessage,
});
}
claudeStore.addLine("system", `Changed directory to: ${validatedPath}`);
characterState.setState("idle");
} catch (error) {
claudeStore.addLine("error", `Failed to change directory: ${error}`);
characterState.setTemporaryState("error", 3000);
}
}
async function startNewConversation(): Promise<void> {
const conversationId = get(claudeStore.activeConversationId);
if (!conversationId) {
@@ -48,6 +114,12 @@ async function startNewConversation(): Promise<void> {
}
export const slashCommands: SlashCommand[] = [
{
name: "cd",
description: "Change the working directory",
usage: "/cd <path>",
execute: changeDirectory,
},
{
name: "clear",
description: "Clear the terminal display (keeps conversation context)",
@@ -74,6 +146,20 @@ export const slashCommands: SlashCommand[] = [
claudeStore.addLine("system", `Available commands:\n${helpText}`);
},
},
{
name: "search",
description: "Search within the conversation (use /search to clear)",
usage: "/search [query]",
execute: (args: string) => {
if (!args.trim()) {
searchState.clear();
claudeStore.addLine("system", "Search cleared");
return;
}
searchState.setQuery(args.trim());
claudeStore.addLine("system", `Searching for: "${args.trim()}"`);
},
},
{
name: "summarise",
description: "Get a summary of the entire conversation",