generated from nhcarrigan/template
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a8816f6a0 | |||
| d5485e616f | |||
| d41b37d9e8 | |||
| 4b684bcd63 | |||
| 4c67380859 | |||
| f5bc9a5016 | |||
| cf20540a49 | |||
| 70af6a6b72 | |||
| 3194a3cca5 | |||
| 7fecb20ba9 | |||
| 9b3c242333 | |||
| 5d4fa75278 | |||
| edd8fa5b55 | |||
| 7781b2caab | |||
| e397435dbe | |||
| 89e99b1524 | |||
| e40ae989f7 | |||
| 32a74235c0 |
@@ -141,37 +141,6 @@ When developing new features, always add corresponding tests:
|
||||
|
||||
The goal is to maintain our near-100% coverage as the codebase grows, so future refactoring and changes can be made with confidence!
|
||||
|
||||
## Quality Assurance
|
||||
|
||||
Before committing any changes, **always run the full test suite**:
|
||||
|
||||
```bash
|
||||
./check-all.sh
|
||||
```
|
||||
|
||||
This script runs all checks in the correct order:
|
||||
|
||||
1. Frontend linting (ESLint)
|
||||
2. Frontend formatting (Prettier)
|
||||
3. Frontend type checking (svelte-check)
|
||||
4. Frontend tests with coverage (Vitest)
|
||||
5. Backend linting (Clippy with strict rules)
|
||||
6. Backend tests with coverage (cargo test + llvm-cov)
|
||||
|
||||
**Important**: The script requires Node.js and Rust toolchains to be available:
|
||||
|
||||
- **Node.js tools** (pnpm, npm): Source nvm first if needed: `source ~/.nvm/nvm.sh`
|
||||
- **Rust tools** (cargo, clippy): Should be in PATH via `~/.cargo/bin/`
|
||||
|
||||
If `check-all.sh` reports any failures:
|
||||
|
||||
1. Read the error messages carefully - they usually explain what needs fixing
|
||||
2. Fix the issues (linting errors, test failures, etc.)
|
||||
3. Run `check-all.sh` again to verify the fixes
|
||||
4. Only commit once all checks pass ✨
|
||||
|
||||
**Never commit code that doesn't pass `check-all.sh`** - this ensures code quality and prevents broken builds!
|
||||
|
||||
## Project Context
|
||||
|
||||
Hikari Desktop is a Tauri-based desktop application that wraps Claude Code with a visual anime character (Hikari) who appears on screen. This is a personal project where Hikari can sign her work and act as herself!
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hikari-desktop",
|
||||
"version": "1.5.1",
|
||||
"version": "1.4.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -51,7 +51,7 @@
|
||||
"@codemirror/legacy-modes": "^6.5.2",
|
||||
"@codemirror/state": "^6.5.4",
|
||||
"@codemirror/theme-one-dark": "^6.1.3",
|
||||
"@codemirror/view": "6.39.13",
|
||||
"@codemirror/view": "^6.39.11",
|
||||
"@lezer/highlight": "^1.2.3",
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
|
||||
|
||||
Generated
+16
-16
@@ -81,8 +81,8 @@ importers:
|
||||
specifier: ^6.1.3
|
||||
version: 6.1.3
|
||||
'@codemirror/view':
|
||||
specifier: 6.39.13
|
||||
version: 6.39.13
|
||||
specifier: ^6.39.11
|
||||
version: 6.39.11
|
||||
'@lezer/highlight':
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.3
|
||||
@@ -323,8 +323,8 @@ packages:
|
||||
'@codemirror/theme-one-dark@6.1.3':
|
||||
resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==}
|
||||
|
||||
'@codemirror/view@6.39.13':
|
||||
resolution: {integrity: sha512-QBO8ZsgJLCbI28KdY0/oDy5NQLqOQVZCozBknxc2/7L98V+TVYFHnfaCsnGh1U+alpd2LOkStVwYY7nW2R1xbw==}
|
||||
'@codemirror/view@6.39.11':
|
||||
resolution: {integrity: sha512-bWdeR8gWM87l4DB/kYSF9A+dVackzDb/V56Tq7QVrQ7rn86W0rgZFtlL3g3pem6AeGcb9NQNoy3ao4WpW4h5tQ==}
|
||||
|
||||
'@csstools/color-helpers@5.1.0':
|
||||
resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==}
|
||||
@@ -2192,14 +2192,14 @@ snapshots:
|
||||
dependencies:
|
||||
'@codemirror/language': 6.12.1
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
'@lezer/common': 1.5.0
|
||||
|
||||
'@codemirror/commands@6.8.1':
|
||||
dependencies:
|
||||
'@codemirror/language': 6.12.1
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
'@lezer/common': 1.5.0
|
||||
|
||||
'@codemirror/lang-angular@0.1.4':
|
||||
@@ -2239,7 +2239,7 @@ snapshots:
|
||||
'@codemirror/lang-javascript': 6.2.4
|
||||
'@codemirror/language': 6.12.1
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
'@lezer/common': 1.5.0
|
||||
'@lezer/css': 1.3.0
|
||||
'@lezer/html': 1.3.13
|
||||
@@ -2255,7 +2255,7 @@ snapshots:
|
||||
'@codemirror/language': 6.12.1
|
||||
'@codemirror/lint': 6.9.3
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
'@lezer/common': 1.5.0
|
||||
'@lezer/javascript': 1.5.4
|
||||
|
||||
@@ -2278,7 +2278,7 @@ snapshots:
|
||||
'@codemirror/lang-html': 6.4.11
|
||||
'@codemirror/language': 6.12.1
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
'@lezer/common': 1.5.0
|
||||
'@lezer/markdown': 1.6.3
|
||||
|
||||
@@ -2341,7 +2341,7 @@ snapshots:
|
||||
'@codemirror/autocomplete': 6.20.0
|
||||
'@codemirror/language': 6.12.1
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
'@lezer/common': 1.5.0
|
||||
'@lezer/xml': 1.0.6
|
||||
|
||||
@@ -2358,7 +2358,7 @@ snapshots:
|
||||
'@codemirror/language@6.12.1':
|
||||
dependencies:
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
'@lezer/common': 1.5.0
|
||||
'@lezer/highlight': 1.2.3
|
||||
'@lezer/lr': 1.4.8
|
||||
@@ -2371,13 +2371,13 @@ snapshots:
|
||||
'@codemirror/lint@6.9.3':
|
||||
dependencies:
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
crelt: 1.0.6
|
||||
|
||||
'@codemirror/search@6.6.0':
|
||||
dependencies:
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
crelt: 1.0.6
|
||||
|
||||
'@codemirror/state@6.5.4':
|
||||
@@ -2388,10 +2388,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@codemirror/language': 6.12.1
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
'@lezer/highlight': 1.2.3
|
||||
|
||||
'@codemirror/view@6.39.13':
|
||||
'@codemirror/view@6.39.11':
|
||||
dependencies:
|
||||
'@codemirror/state': 6.5.4
|
||||
crelt: 1.0.6
|
||||
@@ -3230,7 +3230,7 @@ snapshots:
|
||||
'@codemirror/lint': 6.9.3
|
||||
'@codemirror/search': 6.6.0
|
||||
'@codemirror/state': 6.5.4
|
||||
'@codemirror/view': 6.39.13
|
||||
'@codemirror/view': 6.39.11
|
||||
|
||||
color-convert@2.0.1:
|
||||
dependencies:
|
||||
|
||||
Generated
+1
-1
@@ -1636,7 +1636,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hikari-desktop"
|
||||
version = "1.5.1"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dirs 5.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "hikari-desktop"
|
||||
version = "1.5.1"
|
||||
version = "1.4.0"
|
||||
description = "Hikari - Claude Code Visual Assistant"
|
||||
authors = ["Naomi Carrigan"]
|
||||
edition = "2021"
|
||||
|
||||
+14
-159
@@ -49,59 +49,6 @@ fn wsl_path_to_windows(wsl_path: &str) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Command instance for executing Claude CLI commands
|
||||
/// On Windows, this will use WSL to execute the command
|
||||
/// On other platforms, it executes directly
|
||||
fn create_claude_command() -> std::process::Command {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Use `which` inside WSL to find the claude binary dynamically
|
||||
// Non-login shells launched by `wsl` don't inherit the full user PATH,
|
||||
// so we need to use a login shell to get the correct PATH
|
||||
let which_output = std::process::Command::new("wsl")
|
||||
.args(["-e", "bash", "-l", "-c", "which claude"])
|
||||
.output();
|
||||
|
||||
match which_output {
|
||||
Ok(output) if output.status.success() => {
|
||||
let claude_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
let mut cmd = std::process::Command::new("wsl");
|
||||
cmd.arg(claude_path);
|
||||
cmd
|
||||
}
|
||||
_ => {
|
||||
// Fallback to just "claude" if which fails
|
||||
// This maintains backwards compatibility
|
||||
let mut cmd = std::process::Command::new("wsl");
|
||||
cmd.arg("claude");
|
||||
cmd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
// Use `which` to find the claude binary dynamically
|
||||
// This works regardless of how Claude Code was installed (standalone, npm, etc.)
|
||||
// and avoids hardcoding paths
|
||||
let which_output = std::process::Command::new("which")
|
||||
.arg("claude")
|
||||
.output();
|
||||
|
||||
match which_output {
|
||||
Ok(output) if output.status.success() => {
|
||||
let claude_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
std::process::Command::new(claude_path)
|
||||
}
|
||||
_ => {
|
||||
// Fallback to just "claude" if which fails
|
||||
// This maintains backwards compatibility
|
||||
std::process::Command::new("claude")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn start_claude(
|
||||
bridge_manager: State<'_, SharedBridgeManager>,
|
||||
@@ -1219,55 +1166,6 @@ pub struct MemoryFilesResponse {
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_memory_files() -> Result<MemoryFilesResponse, String> {
|
||||
// On Windows, we need to look in the WSL home directory
|
||||
// On Linux/Mac, use the native home directory
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
list_memory_files_via_wsl().await
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
list_memory_files_native().await
|
||||
}
|
||||
}
|
||||
|
||||
/// List memory files via WSL (for Windows)
|
||||
#[cfg(target_os = "windows")]
|
||||
async fn list_memory_files_via_wsl() -> Result<MemoryFilesResponse, String> {
|
||||
use std::process::Command;
|
||||
|
||||
// Use WSL to find all memory files in the WSL home directory
|
||||
// This script finds all "memory" directories and lists their files
|
||||
let script = r#"
|
||||
find ~/.claude/projects -type d -name memory 2>/dev/null | while read dir; do
|
||||
find "$dir" -maxdepth 1 -type f 2>/dev/null
|
||||
done | sort
|
||||
"#;
|
||||
|
||||
let output = Command::new("wsl")
|
||||
.args(["-e", "bash", "-l", "-c", script])
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(format!("Failed to list memory files: {}", stderr));
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let files: Vec<String> = stdout
|
||||
.lines()
|
||||
.filter(|line| !line.trim().is_empty())
|
||||
.map(|line| line.trim().to_string())
|
||||
.collect();
|
||||
|
||||
Ok(MemoryFilesResponse { files })
|
||||
}
|
||||
|
||||
/// List memory files using native filesystem (for Linux/Mac)
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
async fn list_memory_files_native() -> Result<MemoryFilesResponse, String> {
|
||||
use std::fs;
|
||||
|
||||
// Get the .claude directory in the user's home
|
||||
@@ -1335,7 +1233,7 @@ async fn list_memory_files_native() -> Result<MemoryFilesResponse, String> {
|
||||
pub async fn get_claude_version() -> Result<String, String> {
|
||||
tracing::debug!("Getting Claude CLI version");
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("--version")
|
||||
.output();
|
||||
|
||||
@@ -1425,7 +1323,7 @@ fn parse_plugin_list(stdout: &str) -> Vec<PluginInfo> {
|
||||
pub async fn list_plugins() -> Result<Vec<PluginInfo>, String> {
|
||||
tracing::debug!("Listing Claude Code plugins");
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("plugin")
|
||||
.arg("list")
|
||||
.output();
|
||||
@@ -1454,7 +1352,7 @@ pub async fn list_plugins() -> Result<Vec<PluginInfo>, String> {
|
||||
pub async fn install_plugin(plugin_name: String) -> Result<String, String> {
|
||||
tracing::debug!("Installing plugin: {}", plugin_name);
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("plugin")
|
||||
.arg("install")
|
||||
.arg(&plugin_name)
|
||||
@@ -1483,7 +1381,7 @@ pub async fn install_plugin(plugin_name: String) -> Result<String, String> {
|
||||
pub async fn uninstall_plugin(plugin_name: String) -> Result<String, String> {
|
||||
tracing::debug!("Uninstalling plugin: {}", plugin_name);
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("plugin")
|
||||
.arg("uninstall")
|
||||
.arg(&plugin_name)
|
||||
@@ -1512,7 +1410,7 @@ pub async fn uninstall_plugin(plugin_name: String) -> Result<String, String> {
|
||||
pub async fn enable_plugin(plugin_name: String) -> Result<String, String> {
|
||||
tracing::debug!("Enabling plugin: {}", plugin_name);
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("plugin")
|
||||
.arg("enable")
|
||||
.arg(&plugin_name)
|
||||
@@ -1541,7 +1439,7 @@ pub async fn enable_plugin(plugin_name: String) -> Result<String, String> {
|
||||
pub async fn disable_plugin(plugin_name: String) -> Result<String, String> {
|
||||
tracing::debug!("Disabling plugin: {}", plugin_name);
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("plugin")
|
||||
.arg("disable")
|
||||
.arg(&plugin_name)
|
||||
@@ -1570,7 +1468,7 @@ pub async fn disable_plugin(plugin_name: String) -> Result<String, String> {
|
||||
pub async fn update_plugin(plugin_name: String) -> Result<String, String> {
|
||||
tracing::debug!("Updating plugin: {}", plugin_name);
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("plugin")
|
||||
.arg("update")
|
||||
.arg(&plugin_name)
|
||||
@@ -1642,7 +1540,7 @@ fn parse_marketplace_list(stdout: &str) -> Vec<MarketplaceInfo> {
|
||||
pub async fn list_marketplaces() -> Result<Vec<MarketplaceInfo>, String> {
|
||||
tracing::debug!("Listing plugin marketplaces");
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("plugin")
|
||||
.arg("marketplace")
|
||||
.arg("list")
|
||||
@@ -1675,7 +1573,7 @@ pub async fn list_marketplaces() -> Result<Vec<MarketplaceInfo>, String> {
|
||||
pub async fn add_marketplace(source: String) -> Result<String, String> {
|
||||
tracing::debug!("Adding marketplace: {}", source);
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("plugin")
|
||||
.arg("marketplace")
|
||||
.arg("add")
|
||||
@@ -1708,7 +1606,7 @@ pub async fn add_marketplace(source: String) -> Result<String, String> {
|
||||
pub async fn remove_marketplace(name: String) -> Result<String, String> {
|
||||
tracing::debug!("Removing marketplace: {}", name);
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("plugin")
|
||||
.arg("marketplace")
|
||||
.arg("remove")
|
||||
@@ -1848,7 +1746,7 @@ fn parse_mcp_server_list(stdout: &str) -> Vec<McpServerInfo> {
|
||||
pub async fn list_mcp_servers() -> Result<Vec<McpServerInfo>, String> {
|
||||
tracing::debug!("Listing MCP servers");
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("mcp")
|
||||
.arg("list")
|
||||
.output();
|
||||
@@ -1890,7 +1788,7 @@ pub async fn get_mcp_server(name: String) -> Result<McpServerInfo, String> {
|
||||
pub async fn remove_mcp_server(name: String) -> Result<String, String> {
|
||||
tracing::debug!("Removing MCP server: {}", name);
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("mcp")
|
||||
.arg("remove")
|
||||
.arg(&name)
|
||||
@@ -1925,7 +1823,7 @@ pub async fn add_mcp_server(
|
||||
) -> Result<String, String> {
|
||||
tracing::debug!("Adding MCP server: {} with transport {}", name, transport);
|
||||
|
||||
let mut cmd = create_claude_command();
|
||||
let mut cmd = std::process::Command::new("claude");
|
||||
cmd.arg("mcp").arg("add");
|
||||
|
||||
// Add transport flag
|
||||
@@ -1973,7 +1871,7 @@ pub async fn add_mcp_server(
|
||||
pub async fn get_mcp_server_details(name: String) -> Result<String, String> {
|
||||
tracing::debug!("Getting detailed info for MCP server: {}", name);
|
||||
|
||||
let output = create_claude_command()
|
||||
let output = std::process::Command::new("claude")
|
||||
.arg("mcp")
|
||||
.arg("get")
|
||||
.arg(&name)
|
||||
@@ -2010,49 +1908,6 @@ mod tests {
|
||||
tokio::runtime::Runtime::new().unwrap().block_on(f)
|
||||
}
|
||||
|
||||
// ==================== create_claude_command tests ====================
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "windows")]
|
||||
fn test_create_claude_command_windows() {
|
||||
// On Windows, should create a command that uses wsl with full path to claude
|
||||
// The path is resolved dynamically via `which` in a login shell
|
||||
let cmd = create_claude_command();
|
||||
let program = cmd.get_program();
|
||||
|
||||
assert_eq!(program, "wsl");
|
||||
|
||||
// Verify the first argument is a path to claude (full path from `which`)
|
||||
// or fallback to just "claude" if which fails
|
||||
let args: Vec<&std::ffi::OsStr> = cmd.get_args().collect();
|
||||
assert_eq!(args.len(), 1);
|
||||
|
||||
let arg_str = args[0].to_string_lossy();
|
||||
assert!(
|
||||
arg_str.contains("claude"),
|
||||
"Expected argument to contain 'claude', got: {}",
|
||||
arg_str
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn test_create_claude_command_linux() {
|
||||
// On Linux/Mac, should create a command that uses the full path to claude
|
||||
// (resolved via `which` command)
|
||||
let cmd = create_claude_command();
|
||||
let program = cmd.get_program();
|
||||
|
||||
// The program should be the full path to claude (from `which`)
|
||||
// or fallback to "claude" if which fails
|
||||
let program_str = program.to_string_lossy();
|
||||
assert!(
|
||||
program_str.ends_with("claude"),
|
||||
"Expected program to end with 'claude', got: {}",
|
||||
program_str
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== validate_directory tests ====================
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -129,11 +129,6 @@ impl WslBridge {
|
||||
return Err("Process already running".to_string());
|
||||
}
|
||||
|
||||
// Check if Claude binary is installed before attempting to start
|
||||
if Command::new("which").arg("claude").output().ok().is_none_or(|output| !output.status.success()) {
|
||||
return Err("Claude Code is not installed. Please install it using:\n\ncurl -fsSL https://claude.ai/install.sh | bash".to_string());
|
||||
}
|
||||
|
||||
// Load saved achievements and stats when starting a new session
|
||||
let app_clone = app.clone();
|
||||
let stats = self.stats.clone();
|
||||
@@ -1873,22 +1868,6 @@ mod tests {
|
||||
assert!(!bridge.is_running());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_claude_binary_check_command_structure() {
|
||||
// Test that we're using the correct command to check for Claude binary
|
||||
let output = Command::new("which").arg("claude").output();
|
||||
|
||||
// The command should execute successfully (even if claude is not found)
|
||||
// We're just verifying the command structure is valid
|
||||
assert!(output.is_ok(), "which command should execute without error");
|
||||
|
||||
// Verify the check logic returns a boolean
|
||||
// This is the same logic used in start() to check if claude is installed
|
||||
let _result = output.ok().is_none_or(|o| !o.status.success());
|
||||
// If claude is not installed, _result will be true (show error)
|
||||
// If claude is installed, _result will be false (proceed with connection)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_shared_bridge_manager() {
|
||||
use crate::bridge_manager::create_shared_bridge_manager;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "hikari-desktop",
|
||||
"version": "1.5.1",
|
||||
"version": "1.4.0",
|
||||
"identifier": "com.naomi.hikari-desktop",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
|
||||
@@ -1071,7 +1071,6 @@ User: ${formattedMessage}`;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.control-button {
|
||||
@@ -1088,18 +1087,6 @@ User: ${formattedMessage}`;
|
||||
transition: all 0.2s;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Hide button text on smaller screens, show icons only */
|
||||
@media (max-width: 640px) {
|
||||
.control-button span {
|
||||
display: none;
|
||||
}
|
||||
.control-button {
|
||||
padding: 10px;
|
||||
min-width: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.control-button:hover {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { readTextFile } from "@tauri-apps/plugin-fs";
|
||||
import Markdown from "./Markdown.svelte";
|
||||
|
||||
let memoryFiles: string[] = $state([]);
|
||||
@@ -32,8 +33,7 @@
|
||||
isLoading = true;
|
||||
error = null;
|
||||
try {
|
||||
// Use our backend command instead of Tauri plugin to handle WSL paths
|
||||
const content = await invoke<string>("read_file_content", { path: filePath });
|
||||
const content = await readTextFile(filePath);
|
||||
fileContent = content;
|
||||
selectedFile = filePath;
|
||||
} catch (e) {
|
||||
|
||||
@@ -381,16 +381,16 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 flex-wrap min-w-0">
|
||||
<div class="flex items-center gap-3">
|
||||
{#if streamerModeActive}
|
||||
<div
|
||||
class="w-2.5 h-2.5 rounded-full bg-red-500 animate-pulse shrink-0"
|
||||
class="w-2.5 h-2.5 rounded-full bg-red-500 animate-pulse"
|
||||
title="Streamer mode active (Ctrl+Shift+S to toggle)"
|
||||
></div>
|
||||
{/if}
|
||||
<button
|
||||
onclick={() => (showProfile = true)}
|
||||
class="p-1 text-gray-500 icon-trans-hover shrink-0"
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="Profile"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -696,7 +696,7 @@
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div class="fixed inset-0 z-40" onclick={() => (showStats = false)}></div>
|
||||
<div class="fixed top-14 right-4 z-50 max-h-[calc(100vh-4rem)] overflow-y-auto">
|
||||
<div class="fixed top-14 right-4 z-50">
|
||||
<StatsDisplay
|
||||
onRequestSummary={handleCompactConversation}
|
||||
onStartFreshWithContext={handleStartFreshWithContext}
|
||||
@@ -744,32 +744,3 @@
|
||||
{#if showMcpPanel}
|
||||
<McpManagementPanel onClose={() => (showMcpPanel = false)} />
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
/* Responsive status bar styling */
|
||||
.status-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Make all icon buttons shrink but not grow */
|
||||
.status-bar button {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Hide version text on very small screens */
|
||||
@media (max-width: 640px) {
|
||||
.status-bar button span:last-of-type {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stack left and right sections on very small screens */
|
||||
@media (max-width: 768px) {
|
||||
.status-bar {
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user