Compare commits
5 Commits
v0.14.0-de
...
v0.14.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35b171764e | ||
|
|
6b53ab2d73 | ||
|
|
1b829094ef | ||
|
|
e28e9f5879 | ||
|
|
cb84547c88 |
@@ -20,24 +20,10 @@ function getDefaultShellPath(): string {
|
|||||||
return "/bin/bash"
|
return "/bin/bash"
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapCommandForShell(command: string, shellPath: string): string {
|
|
||||||
const shellName = path.basename(shellPath)
|
|
||||||
|
|
||||||
if (shellName.includes("bash")) {
|
|
||||||
return 'if [ -f ~/.bashrc ]; then source ~/.bashrc >/dev/null 2>&1; fi; ' + command
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellName.includes("zsh")) {
|
|
||||||
return 'if [ -f ~/.zshrc ]; then source ~/.zshrc >/dev/null 2>&1; fi; ' + command
|
|
||||||
}
|
|
||||||
|
|
||||||
return command
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildShellArgs(shellPath: string): string[] {
|
function buildShellArgs(shellPath: string): string[] {
|
||||||
const shellName = path.basename(shellPath)
|
const shellName = path.basename(shellPath)
|
||||||
if (shellName.includes("zsh")) {
|
if (shellName.includes("zsh") || shellName.includes("bash")) {
|
||||||
return ["-l", "-i", "-c"]
|
return ["-i", "-l", "-c"]
|
||||||
}
|
}
|
||||||
return ["-l", "-c"]
|
return ["-l", "-c"]
|
||||||
}
|
}
|
||||||
@@ -59,12 +45,11 @@ export function buildUserShellCommand(userCommand: string): ShellCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const shellPath = getDefaultShellPath()
|
const shellPath = getDefaultShellPath()
|
||||||
const script = wrapCommandForShell(userCommand, shellPath)
|
|
||||||
const args = buildShellArgs(shellPath)
|
const args = buildShellArgs(shellPath)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
command: shellPath,
|
command: shellPath,
|
||||||
args: [...args, script],
|
args: [...args, userCommand],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
"vite-plugin-solid": "^2.10.0"
|
"vite-plugin-solid": "^2.10.0"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "ai.opencode.client",
|
"appId": "ai.neuralnomads.codenomad.client",
|
||||||
"productName": "CodeNomad",
|
"productName": "CodeNomad",
|
||||||
"directories": {
|
"directories": {
|
||||||
"output": "release",
|
"output": "release",
|
||||||
|
|||||||
BIN
packages/tauri-app/src-tauri/icons/linux/128x128.png
Normal file
BIN
packages/tauri-app/src-tauri/icons/linux/128x128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
packages/tauri-app/src-tauri/icons/linux/256x256.png
Normal file
BIN
packages/tauri-app/src-tauri/icons/linux/256x256.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
BIN
packages/tauri-app/src-tauri/icons/linux/32x32.png
Normal file
BIN
packages/tauri-app/src-tauri/icons/linux/32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
BIN
packages/tauri-app/src-tauri/icons/linux/48x48.png
Normal file
BIN
packages/tauri-app/src-tauri/icons/linux/48x48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
BIN
packages/tauri-app/src-tauri/icons/linux/512x512.png
Normal file
BIN
packages/tauri-app/src-tauri/icons/linux/512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 322 KiB |
BIN
packages/tauri-app/src-tauri/icons/linux/64x64.png
Normal file
BIN
packages/tauri-app/src-tauri/icons/linux/64x64.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
@@ -0,0 +1,9 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Categories=
|
||||||
|
Exec=codenomad-tauri
|
||||||
|
StartupWMClass=codenomad-tauri
|
||||||
|
Icon=codenomad-tauri
|
||||||
|
Name=CodeNomad
|
||||||
|
NoDisplay=true
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
@@ -38,6 +38,7 @@ use windows_sys::Win32::System::JobObjects::{
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||||
|
const MISSING_NODE_PREFIX: &str = "CODENOMAD_MISSING_NODE:";
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -630,6 +631,13 @@ impl CliProcessManager {
|
|||||||
|
|
||||||
let use_user_shell = supports_user_shell();
|
let use_user_shell = supports_user_shell();
|
||||||
|
|
||||||
|
if !use_user_shell && which::which(&resolution.node_binary).is_err() {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Node binary '{}' not found. CodeNomad desktop currently requires Node.js installed on the system, or set NODE_BINARY to a valid runtime path.",
|
||||||
|
resolution.node_binary
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let command_info = if use_user_shell {
|
let command_info = if use_user_shell {
|
||||||
log_line("spawning via user shell");
|
log_line("spawning via user shell");
|
||||||
ShellCommandType::UserShell(build_shell_command_string(&resolution, &args)?)
|
ShellCommandType::UserShell(build_shell_command_string(&resolution, &args)?)
|
||||||
@@ -641,14 +649,6 @@ impl CliProcessManager {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
if !use_user_shell {
|
|
||||||
if which::which(&resolution.node_binary).is_err() {
|
|
||||||
return Err(anyhow::anyhow!(
|
|
||||||
"Node binary not found. Make sure Node.js is installed."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = match &command_info {
|
let child = match &command_info {
|
||||||
ShellCommandType::UserShell(cmd) => {
|
ShellCommandType::UserShell(cmd) => {
|
||||||
log_line(&format!("spawn command: {} {:?}", cmd.shell, cmd.args));
|
log_line(&format!("spawn command: {} {:?}", cmd.shell, cmd.args));
|
||||||
@@ -920,6 +920,17 @@ impl CliProcessManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(node_binary) = line.strip_prefix(MISSING_NODE_PREFIX) {
|
||||||
|
let mut locked = status.lock();
|
||||||
|
if locked.error.is_none() {
|
||||||
|
locked.error = Some(format!(
|
||||||
|
"Node binary '{}' not found in the desktop shell environment. CodeNomad desktop currently requires Node.js installed on the system, or set NODE_BINARY to a valid runtime path.",
|
||||||
|
node_binary.trim()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(url) = local_url_regex
|
if let Some(url) = local_url_regex
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|re| re.captures(line).and_then(|c| c.get(1)))
|
.and_then(|re| re.captures(line).and_then(|c| c.get(1)))
|
||||||
@@ -1248,7 +1259,13 @@ fn build_shell_command_string(
|
|||||||
for arg in entry.runner_args(cli_args) {
|
for arg in entry.runner_args(cli_args) {
|
||||||
quoted.push(shell_escape(&arg));
|
quoted.push(shell_escape(&arg));
|
||||||
}
|
}
|
||||||
let command = format!("ELECTRON_RUN_AS_NODE=1 exec {}", quoted.join(" "));
|
let command = format!(
|
||||||
|
"if command -v {} >/dev/null 2>&1; then ELECTRON_RUN_AS_NODE=1 exec {}; else printf '%s%s\\n' '{}' {} >&2; exit 127; fi",
|
||||||
|
shell_escape(&entry.node_binary),
|
||||||
|
quoted.join(" "),
|
||||||
|
MISSING_NODE_PREFIX,
|
||||||
|
shell_escape(&entry.node_binary),
|
||||||
|
);
|
||||||
let args = build_shell_args(&shell, &command);
|
let args = build_shell_args(&shell, &command);
|
||||||
log_line(&format!("user shell command: {} {:?}", shell, args));
|
log_line(&format!("user shell command: {} {:?}", shell, args));
|
||||||
Ok(ShellCommand { shell, args })
|
Ok(ShellCommand { shell, args })
|
||||||
@@ -1288,8 +1305,11 @@ fn build_shell_args(shell: &str, command: &str) -> Vec<String> {
|
|||||||
.unwrap_or("")
|
.unwrap_or("")
|
||||||
.to_lowercase();
|
.to_lowercase();
|
||||||
|
|
||||||
let _ = shell_name;
|
if shell_name.contains("zsh") || shell_name.contains("bash") {
|
||||||
vec!["-l".into(), "-c".into(), command.into()]
|
vec!["-i".into(), "-l".into(), "-c".into(), command.into()]
|
||||||
|
} else {
|
||||||
|
vec!["-l".into(), "-c".into(), command.into()]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn first_existing(paths: Vec<Option<PathBuf>>) -> Option<String> {
|
fn first_existing(paths: Vec<Option<PathBuf>>) -> Option<String> {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"frontendDist": "resources/ui-loading"
|
"frontendDist": "resources/ui-loading"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
|
"enableGTKAppId": true,
|
||||||
"withGlobalTauri": true,
|
"withGlobalTauri": true,
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
@@ -41,6 +42,35 @@
|
|||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
"active": true,
|
"active": true,
|
||||||
|
"linux": {
|
||||||
|
"appimage": {
|
||||||
|
"files": {
|
||||||
|
"/usr/share/applications/ai.neuralnomads.codenomad.client.desktop": "icons/linux/ai.neuralnomads.codenomad.client.desktop"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deb": {
|
||||||
|
"files": {
|
||||||
|
"/usr/share/applications/ai.neuralnomads.codenomad.client.desktop": "icons/linux/ai.neuralnomads.codenomad.client.desktop",
|
||||||
|
"/usr/share/icons/hicolor/32x32/apps/codenomad-tauri.png": "icons/linux/32x32.png",
|
||||||
|
"/usr/share/icons/hicolor/48x48/apps/codenomad-tauri.png": "icons/linux/48x48.png",
|
||||||
|
"/usr/share/icons/hicolor/64x64/apps/codenomad-tauri.png": "icons/linux/64x64.png",
|
||||||
|
"/usr/share/icons/hicolor/128x128/apps/codenomad-tauri.png": "icons/linux/128x128.png",
|
||||||
|
"/usr/share/icons/hicolor/256x256/apps/codenomad-tauri.png": "icons/linux/256x256.png",
|
||||||
|
"/usr/share/icons/hicolor/512x512/apps/codenomad-tauri.png": "icons/linux/512x512.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rpm": {
|
||||||
|
"files": {
|
||||||
|
"/usr/share/applications/ai.neuralnomads.codenomad.client.desktop": "icons/linux/ai.neuralnomads.codenomad.client.desktop",
|
||||||
|
"/usr/share/icons/hicolor/32x32/apps/codenomad-tauri.png": "icons/linux/32x32.png",
|
||||||
|
"/usr/share/icons/hicolor/48x48/apps/codenomad-tauri.png": "icons/linux/48x48.png",
|
||||||
|
"/usr/share/icons/hicolor/64x64/apps/codenomad-tauri.png": "icons/linux/64x64.png",
|
||||||
|
"/usr/share/icons/hicolor/128x128/apps/codenomad-tauri.png": "icons/linux/128x128.png",
|
||||||
|
"/usr/share/icons/hicolor/256x256/apps/codenomad-tauri.png": "icons/linux/256x256.png",
|
||||||
|
"/usr/share/icons/hicolor/512x512/apps/codenomad-tauri.png": "icons/linux/512x512.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"resources": [
|
"resources": [
|
||||||
"resources/server",
|
"resources/server",
|
||||||
"resources/ui-loading"
|
"resources/ui-loading"
|
||||||
|
|||||||
@@ -357,7 +357,11 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
|
|||||||
const pill = activeSessionStatusPill()
|
const pill = activeSessionStatusPill()
|
||||||
if (!pill) return null
|
if (!pill) return null
|
||||||
return (
|
return (
|
||||||
<span class={`status-indicator session-status session-status-list ${pill.className}`} title={pill.title}>
|
<span
|
||||||
|
class={`status-indicator session-status session-status-list ${pill.className} notranslate`}
|
||||||
|
title={pill.title}
|
||||||
|
translate="no"
|
||||||
|
>
|
||||||
{pill.showAlertIcon ? <ShieldAlert class="w-3.5 h-3.5" aria-hidden="true" /> : <span class="status-dot" />}
|
{pill.showAlertIcon ? <ShieldAlert class="w-3.5 h-3.5" aria-hidden="true" /> : <span class="status-dot" />}
|
||||||
{pill.text}
|
{pill.text}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -520,7 +520,11 @@ const SessionList: Component<SessionListProps> = (props) => {
|
|||||||
<ChevronDown class={`w-3.5 h-3.5 transition-transform ${rowProps.expanded ? "" : "-rotate-90"}`} />
|
<ChevronDown class={`w-3.5 h-3.5 transition-transform ${rowProps.expanded ? "" : "-rotate-90"}`} />
|
||||||
</span>
|
</span>
|
||||||
</Show>
|
</Show>
|
||||||
<span class={`status-indicator session-status session-status-list ${statusClassName()}`} title={statusTooltip()}>
|
<span
|
||||||
|
class={`status-indicator session-status session-status-list ${statusClassName()} notranslate`}
|
||||||
|
title={statusTooltip()}
|
||||||
|
translate="no"
|
||||||
|
>
|
||||||
{needsInput() ? <ShieldAlert class="w-3.5 h-3.5" aria-hidden="true" /> : <span class="status-dot" />}
|
{needsInput() ? <ShieldAlert class="w-3.5 h-3.5" aria-hidden="true" /> : <span class="status-dot" />}
|
||||||
{statusText()}
|
{statusText()}
|
||||||
</span>
|
</span>
|
||||||
@@ -736,7 +740,9 @@ const SessionList: Component<SessionListProps> = (props) => {
|
|||||||
<div class="session-list-header p-3 border-b border-base">
|
<div class="session-list-header p-3 border-b border-base">
|
||||||
{props.headerContent ?? (
|
{props.headerContent ?? (
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<h3 class="text-sm font-semibold text-primary">{t("sessionList.header.title")}</h3>
|
<h3 class="text-sm font-semibold text-primary notranslate" translate="no">
|
||||||
|
{t("sessionList.header.title")}
|
||||||
|
</h3>
|
||||||
<KeyboardHint
|
<KeyboardHint
|
||||||
shortcuts={[keyboardRegistry.get("session-prev")!, keyboardRegistry.get("session-next")!].filter(Boolean)}
|
shortcuts={[keyboardRegistry.get("session-prev")!, keyboardRegistry.get("session-next")!].filter(Boolean)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user