feat: Chat-Detach richtig — Hauptfenster blendet Chat aus, separates Fenster lädt Session
Some checks failed
Build AppImage / build (push) Failing after 15s
Some checks failed
Build AppImage / build (push) Failing after 15s
- chatDetached Store: Hauptfenster blendet Chat-Pane aus wenn Fenster offen - Placeholder mit "Zurückholen"-Button statt leerem Pane - Backend sendet chat-detached/chat-reattached Events ans Hauptfenster - on_window_event(Destroyed): Automatisches Reattach wenn Fenster geschlossen - Chat-Window lädt aktive Session + Nachrichten aus DB beim Öffnen - Mehr Platz für andere Panels wenn Chat herausgelöst [appimage] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
51374720e7
commit
e87ac9ffc1
4 changed files with 121 additions and 7 deletions
|
|
@ -1,17 +1,22 @@
|
|||
// Claude Desktop — Chat-Fenster herauslösen
|
||||
// Öffnet den Chat-Panel in einem separaten Fenster
|
||||
// Öffnet den Chat-Panel in einem separaten Fenster.
|
||||
// Sendet Events an das Hauptfenster damit es den Chat-Bereich aus-/einblendet.
|
||||
|
||||
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder};
|
||||
use tauri::{AppHandle, Emitter, Manager, WebviewUrl, WebviewWindowBuilder};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn chat_window_open(app: AppHandle) -> Result<(), String> {
|
||||
// Falls Fenster schon existiert: anzeigen und fokussieren
|
||||
if let Some(win) = app.get_webview_window("chat-detached") {
|
||||
win.show().map_err(|e| e.to_string())?;
|
||||
win.set_focus().map_err(|e| e.to_string())?;
|
||||
// Event ans Hauptfenster: Chat ist herausgelöst
|
||||
app.emit("chat-detached", ()).ok();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
WebviewWindowBuilder::new(&app, "chat-detached", WebviewUrl::App("/chat-window".into()))
|
||||
// Neues Fenster erstellen
|
||||
let win = WebviewWindowBuilder::new(&app, "chat-detached", WebviewUrl::App("/chat-window".into()))
|
||||
.title("Claude \u{2014} Chat")
|
||||
.inner_size(800.0, 900.0)
|
||||
.min_inner_size(500.0, 400.0)
|
||||
|
|
@ -19,6 +24,17 @@ pub async fn chat_window_open(app: AppHandle) -> Result<(), String> {
|
|||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// Event ans Hauptfenster: Chat ist herausgelöst
|
||||
app.emit("chat-detached", ()).ok();
|
||||
|
||||
// Wenn das Chat-Fenster geschlossen wird → Hauptfenster benachrichtigen
|
||||
let app_clone = app.clone();
|
||||
win.on_window_event(move |event| {
|
||||
if let tauri::WindowEvent::Destroyed = event {
|
||||
app_clone.emit("chat-reattached", ()).ok();
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -27,5 +43,8 @@ pub async fn chat_window_close(app: AppHandle) -> Result<(), String> {
|
|||
if let Some(win) = app.get_webview_window("chat-detached") {
|
||||
win.close().map_err(|e| e.to_string())?;
|
||||
}
|
||||
// Event wird automatisch durch on_window_event/Destroyed gesendet
|
||||
// Zur Sicherheit aber auch manuell senden
|
||||
app.emit("chat-reattached", ()).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ export const permissions = writable<Permission[]>([]);
|
|||
|
||||
// UI-State
|
||||
export const isProcessing = writable(false);
|
||||
export const chatDetached = writable(false);
|
||||
export const currentInput = writable('');
|
||||
export const selectedAgentId = writable<string | null>(null);
|
||||
export const currentModel = writable('');
|
||||
|
|
|
|||
|
|
@ -1,9 +1,25 @@
|
|||
<script lang="ts">
|
||||
import { PaneGroup, Pane, PaneResizer } from 'paneforge';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { onMount } from 'svelte';
|
||||
import { chatDetached } from '$lib/stores/app';
|
||||
// Kritische Panels sofort laden (immer sichtbar)
|
||||
import SessionList from '$lib/components/SessionList.svelte';
|
||||
import ChatPanel from '$lib/components/ChatPanel.svelte';
|
||||
|
||||
// Chat-Detach: Listener für Fenster-Events
|
||||
onMount(async () => {
|
||||
// Wenn das Chat-Fenster geschlossen wird → Chat wieder einblenden
|
||||
await listen('chat-reattached', () => {
|
||||
$chatDetached = false;
|
||||
});
|
||||
// Wenn das Chat-Fenster geöffnet wird → Chat ausblenden
|
||||
await listen('chat-detached', () => {
|
||||
$chatDetached = true;
|
||||
});
|
||||
});
|
||||
|
||||
// Sekundäre Panels: Lazy-Load bei erstem Tab-Wechsel
|
||||
const lazyPanels = {
|
||||
activity: () => import('$lib/components/ActivityPanel.svelte'),
|
||||
|
|
@ -69,9 +85,21 @@
|
|||
</PaneResizer>
|
||||
|
||||
<!-- Chat -->
|
||||
<Pane defaultSize={35} minSize={15} class="panel">
|
||||
<ChatPanel />
|
||||
</Pane>
|
||||
{#if !$chatDetached}
|
||||
<Pane defaultSize={35} minSize={15} class="panel">
|
||||
<ChatPanel />
|
||||
</Pane>
|
||||
{:else}
|
||||
<Pane defaultSize={12} minSize={5} maxSize={20} class="panel">
|
||||
<div class="detached-placeholder">
|
||||
<span class="detached-icon">💬</span>
|
||||
<p>Chat ist herausgelöst</p>
|
||||
<button class="reattach-btn" onclick={() => invoke('chat_window_close')}>
|
||||
Zurückholen
|
||||
</button>
|
||||
</div>
|
||||
</Pane>
|
||||
{/if}
|
||||
|
||||
<PaneResizer class="resizer">
|
||||
<div class="resizer-line"></div>
|
||||
|
|
@ -204,4 +232,44 @@
|
|||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Detached-Chat Placeholder */
|
||||
.detached-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
gap: 8px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.detached-icon {
|
||||
font-size: 2rem;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.detached-placeholder p {
|
||||
font-size: 0.8rem;
|
||||
margin: 0;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.reattach-btn {
|
||||
margin-top: 8px;
|
||||
padding: 4px 12px;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.reattach-btn:hover {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,35 @@
|
|||
<script lang="ts">
|
||||
import ChatPanel from '$lib/components/ChatPanel.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { currentSessionId, setMessagesFromDb, type DbMessage } from '$lib/stores/app';
|
||||
|
||||
onMount(() => {
|
||||
onMount(async () => {
|
||||
document.documentElement.classList.add('detached-window');
|
||||
|
||||
// Aktive Session und Nachrichten laden (aus DB, nicht aus Store — der ist leer)
|
||||
try {
|
||||
interface Session {
|
||||
id: string;
|
||||
title: string;
|
||||
message_count: number;
|
||||
token_input: number;
|
||||
token_output: number;
|
||||
cost_usd: number;
|
||||
}
|
||||
|
||||
const session: Session | null = await invoke('get_active_session');
|
||||
if (session) {
|
||||
$currentSessionId = session.id;
|
||||
const messages: DbMessage[] = await invoke('load_messages', { sessionId: session.id });
|
||||
if (messages.length > 0) {
|
||||
setMessagesFromDb(messages);
|
||||
console.log(`💬 Chat-Window: ${messages.length} Nachrichten geladen`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Chat-Window: Session laden fehlgeschlagen:', err);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue