feat: Projekt-Wechsel mit File-Browser + Nix-Wrapper Audio-Fixes [appimage]
All checks were successful
Build AppImage / build (push) Successful in 9m35s

- File-Browser Dialog (tauri-plugin-dialog) für Projektverzeichnis-Auswahl
- Auto-Name aus Verzeichnisname beim Projekt-Hinzufügen
- Nix-Wrapper: GStreamer Core-Plugins (valve) + ffmpeg-headless im PATH
- Nix-Wrapper: Version 0.1.1 mit korrektem gstreamer.out Output

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eddy 2026-04-21 11:37:07 +02:00
parent 68d2500037
commit 6efbd5de5f
8 changed files with 141 additions and 10 deletions

View file

@ -52,15 +52,16 @@ let
]; ];
gstPluginPath = pkgs.lib.concatMapStringsSep ":" (p: "${p}/lib/gstreamer-1.0") gstPlugins gstPluginPath = pkgs.lib.concatMapStringsSep ":" (p: "${p}/lib/gstreamer-1.0") gstPlugins
+ ":${pkgs.gst_all_1.gstreamer.out}/lib/gstreamer-1.0"
+ ":${pkgs.pipewire}/lib/gstreamer-1.0"; + ":${pkgs.pipewire}/lib/gstreamer-1.0";
# Laufzeit-Binaries die die App spawnen muss (node fuer claude-bridge.js, # Laufzeit-Binaries die die App spawnen muss (node fuer claude-bridge.js,
# npm fuer apply_bundle_update, tar fuer Bundle-Extract) # npm fuer apply_bundle_update, tar fuer Bundle-Extract)
runtimeBins = [ pkgs.nodejs_22 pkgs.gnutar pkgs.gzip ]; runtimeBins = [ pkgs.nodejs_22 pkgs.gnutar pkgs.gzip pkgs.ffmpeg-headless ];
in in
pkgs.stdenv.mkDerivation { pkgs.stdenv.mkDerivation {
pname = "claude-desktop"; pname = "claude-desktop";
version = "0.1.0"; version = "0.1.1";
# Keine Quelldateien — Wir packen nur Wrapper + Desktop-Entry # Keine Quelldateien — Wir packen nur Wrapper + Desktop-Entry
dontUnpack = true; dontUnpack = true;

10
package-lock.json generated
View file

@ -12,6 +12,7 @@
"@anthropic-ai/claude-code": "^0.2.0", "@anthropic-ai/claude-code": "^0.2.0",
"@anthropic-ai/sdk": "^0.88.0", "@anthropic-ai/sdk": "^0.88.0",
"@tauri-apps/api": "^2.0.0", "@tauri-apps/api": "^2.0.0",
"@tauri-apps/plugin-dialog": "^2.7.0",
"@tauri-apps/plugin-shell": "^2.0.0", "@tauri-apps/plugin-shell": "^2.0.0",
"marked": "^18.0.0", "marked": "^18.0.0",
"mermaid": "^11.4.0", "mermaid": "^11.4.0",
@ -1821,6 +1822,15 @@
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/@tauri-apps/plugin-dialog": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.7.0.tgz",
"integrity": "sha512-4nS/hfGMGCXiAS3LtVjH9AgsSAPJeG/7R+q8agTFqytjnMa4Zq95Bq8WzVDkckpanX+yyRHXnRtrKXkANKDHvw==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@tauri-apps/api": "^2.10.1"
}
},
"node_modules/@tauri-apps/plugin-shell": { "node_modules/@tauri-apps/plugin-shell": {
"version": "2.3.5", "version": "2.3.5",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.5.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.5.tgz",

View file

@ -26,6 +26,7 @@
"@anthropic-ai/claude-code": "^0.2.0", "@anthropic-ai/claude-code": "^0.2.0",
"@anthropic-ai/sdk": "^0.88.0", "@anthropic-ai/sdk": "^0.88.0",
"@tauri-apps/api": "^2.0.0", "@tauri-apps/api": "^2.0.0",
"@tauri-apps/plugin-dialog": "^2.7.0",
"@tauri-apps/plugin-shell": "^2.0.0", "@tauri-apps/plugin-shell": "^2.0.0",
"marked": "^18.0.0", "marked": "^18.0.0",
"mermaid": "^11.4.0", "mermaid": "^11.4.0",

68
src-tauri/Cargo.lock generated
View file

@ -490,6 +490,7 @@ dependencies = [
"sha2", "sha2",
"tauri", "tauri",
"tauri-build", "tauri-build",
"tauri-plugin-dialog",
"tauri-plugin-shell", "tauri-plugin-shell",
"tokio", "tokio",
"tokio-tungstenite", "tokio-tungstenite",
@ -2743,6 +2744,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"block2", "block2",
"libc",
"objc2", "objc2",
"objc2-core-foundation", "objc2-core-foundation",
] ]
@ -3618,6 +3620,30 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "rfd"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672"
dependencies = [
"block2",
"dispatch2",
"glib-sys",
"gobject-sys",
"gtk-sys",
"js-sys",
"log",
"objc2",
"objc2-app-kit",
"objc2-core-foundation",
"objc2-foundation",
"raw-window-handle",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows-sys 0.60.2",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.14" version = "0.17.14"
@ -4637,6 +4663,48 @@ dependencies = [
"walkdir", "walkdir",
] ]
[[package]]
name = "tauri-plugin-dialog"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fa4150c95ae391946cc8b8f905ab14797427caba3a8a2f79628e956da91809"
dependencies = [
"log",
"raw-window-handle",
"rfd",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"tauri-plugin-fs",
"thiserror 2.0.18",
"url",
]
[[package]]
name = "tauri-plugin-fs"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36e1ec28b79f3d0683f4507e1615c36292c0ea6716668770d4396b9b39871ed8"
dependencies = [
"anyhow",
"dunce",
"glob",
"log",
"objc2-foundation",
"percent-encoding",
"schemars 0.8.22",
"serde",
"serde_json",
"serde_repr",
"tauri",
"tauri-plugin",
"tauri-utils",
"thiserror 2.0.18",
"toml 0.9.12+spec-1.1.0",
"url",
]
[[package]] [[package]]
name = "tauri-plugin-shell" name = "tauri-plugin-shell"
version = "2.3.5" version = "2.3.5"

View file

@ -27,6 +27,7 @@ base64 = "0.22"
tokio-tungstenite = "0.23" tokio-tungstenite = "0.23"
futures-util = "0.3" futures-util = "0.3"
sha2 = "0.10" sha2 = "0.10"
tauri-plugin-dialog = "2.7.0"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
webkit2gtk = "2.0" webkit2gtk = "2.0"

View file

@ -16,6 +16,7 @@
"shell:allow-execute", "shell:allow-execute",
"shell:allow-spawn", "shell:allow-spawn",
"shell:allow-stdin-write", "shell:allow-stdin-write",
"shell:allow-kill" "shell:allow-kill",
"dialog:allow-open"
] ]
} }

View file

@ -33,6 +33,7 @@ mod chat_window;
pub fn run() { pub fn run() {
tauri::Builder::default() tauri::Builder::default()
.plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_dialog::init())
.manage(Arc::new(Mutex::new(claude::ClaudeState::default()))) .manage(Arc::new(Mutex::new(claude::ClaudeState::default())))
.manage(guard::GuardState::new(Mutex::new(guard::GuardRails::new()))) .manage(guard::GuardState::new(Mutex::new(guard::GuardRails::new())))
.manage::<hooks::HookState>(Arc::new(Mutex::new(hooks::HookManager::default()))) .manage::<hooks::HookState>(Arc::new(Mutex::new(hooks::HookManager::default())))

View file

@ -2,6 +2,7 @@
import { onMount, onDestroy } from 'svelte'; import { onMount, onDestroy } from 'svelte';
import { invoke } from '@tauri-apps/api/core'; import { invoke } from '@tauri-apps/api/core';
import { listen, type UnlistenFn } from '@tauri-apps/api/event'; import { listen, type UnlistenFn } from '@tauri-apps/api/event';
import { open } from '@tauri-apps/plugin-dialog';
import { messages, clearAll, isProcessing, currentSessionId, setMessagesFromDb, type DbMessage } from '$lib/stores/app'; import { messages, clearAll, isProcessing, currentSessionId, setMessagesFromDb, type DbMessage } from '$lib/stores/app';
interface Session { interface Session {
@ -53,6 +54,27 @@
} }
} }
async function browseDir() {
try {
const selected = await open({
directory: true,
multiple: false,
title: 'Projektverzeichnis wählen',
defaultPath: '/mnt',
});
if (selected && typeof selected === 'string') {
newProjectDir = selected;
// Auto-Name aus Verzeichnisname wenn leer
if (!newProjectName.trim()) {
const parts = selected.replace(/\/+$/, '').split('/');
newProjectName = parts[parts.length - 1] || '';
}
}
} catch (err) {
console.error('Fehler beim Verzeichnis-Dialog:', err);
}
}
async function addProject() { async function addProject() {
const name = newProjectName.trim(); const name = newProjectName.trim();
const dir = newProjectDir.trim(); const dir = newProjectDir.trim();
@ -274,14 +296,18 @@
placeholder="Projektname" placeholder="Projektname"
onkeydown={(e: KeyboardEvent) => e.key === 'Enter' && addProject()} onkeydown={(e: KeyboardEvent) => e.key === 'Enter' && addProject()}
/> />
<div class="dir-input-row">
<input <input
type="text" type="text"
bind:value={newProjectDir} bind:value={newProjectDir}
placeholder="Verzeichnis (/mnt/...)" placeholder="Verzeichnis wählen..."
onkeydown={(e: KeyboardEvent) => e.key === 'Enter' && addProject()} onkeydown={(e: KeyboardEvent) => e.key === 'Enter' && addProject()}
readonly
/> />
<button class="btn-browse" onclick={browseDir} title="Verzeichnis wählen">📂</button>
</div>
<div class="project-add-buttons"> <div class="project-add-buttons">
<button class="btn-create" onclick={addProject}>Anlegen</button> <button class="btn-create" onclick={addProject} disabled={!newProjectName.trim() || !newProjectDir.trim()}>Anlegen</button>
<button class="btn-cancel" onclick={() => { showProjectAdd = false; newProjectName = ''; newProjectDir = ''; }}>Abbrechen</button> <button class="btn-cancel" onclick={() => { showProjectAdd = false; newProjectName = ''; newProjectDir = ''; }}>Abbrechen</button>
</div> </div>
</div> </div>
@ -541,6 +567,28 @@
padding: var(--spacing-xs) var(--spacing-sm); padding: var(--spacing-xs) var(--spacing-sm);
} }
.dir-input-row {
display: flex;
gap: var(--spacing-xs);
}
.dir-input-row input {
flex: 1;
cursor: pointer;
}
.btn-browse {
padding: var(--spacing-xs) var(--spacing-sm);
background: var(--accent);
border-radius: var(--radius-sm);
font-size: 0.75rem;
flex-shrink: 0;
}
.btn-browse:hover {
background: var(--accent-hover);
}
.project-add-buttons { .project-add-buttons {
display: flex; display: flex;
gap: var(--spacing-xs); gap: var(--spacing-xs);