feat: Chat-Panel herausloesbar + Expand-Button Fix [appimage]
All checks were successful
Build AppImage / build (push) Successful in 6m3s
All checks were successful
Build AppImage / build (push) Successful in 6m3s
- Neues Fenster fuer Chat-Panel (auf zweiten Monitor ziehbar) - Expand/Collapse nutzt jetzt $state Array statt Set (Svelte 5 Reaktivitaet) - Kein Collapse waehrend Streaming (verhindert unklickbare Buttons) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e0734303af
commit
2b7dfa6e37
4 changed files with 93 additions and 7 deletions
31
src-tauri/src/chat_window.rs
Normal file
31
src-tauri/src/chat_window.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Claude Desktop — Chat-Fenster herauslösen
|
||||
// Öffnet den Chat-Panel in einem separaten Fenster
|
||||
|
||||
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn chat_window_open(app: AppHandle) -> Result<(), String> {
|
||||
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())?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
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)
|
||||
.center()
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
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())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ mod session;
|
|||
mod teaching;
|
||||
mod update;
|
||||
mod voice;
|
||||
mod chat_window;
|
||||
|
||||
/// Initialisiert die App
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
|
|
@ -145,6 +146,9 @@ pub fn run() {
|
|||
teaching::presentation_close,
|
||||
teaching::presentation_send_slide,
|
||||
teaching::presentation_clear,
|
||||
// Chat-Fenster (herauslösen)
|
||||
chat_window::chat_window_open,
|
||||
chat_window::chat_window_close,
|
||||
// Auto-Update
|
||||
update::check_for_update,
|
||||
update::download_update,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
import { get } from 'svelte/store';
|
||||
import CommandPalette from './CommandPalette.svelte';
|
||||
|
||||
// Props
|
||||
let { detached = false }: { detached?: boolean } = $props();
|
||||
|
||||
// Input-Referenz für Focus-Shortcuts
|
||||
let inputTextarea: HTMLTextAreaElement;
|
||||
|
||||
|
|
@ -25,15 +28,14 @@
|
|||
|
||||
// Collapse: Nachrichten mit > 25 Zeilen werden eingeklappt
|
||||
const COLLAPSE_LINES = 25;
|
||||
let expandedMessages = new Set<string>();
|
||||
let expandedMessages = $state<string[]>([]);
|
||||
|
||||
function toggleExpand(msgId: string) {
|
||||
if (expandedMessages.has(msgId)) {
|
||||
expandedMessages.delete(msgId);
|
||||
if (expandedMessages.includes(msgId)) {
|
||||
expandedMessages = expandedMessages.filter(id => id !== msgId);
|
||||
} else {
|
||||
expandedMessages.add(msgId);
|
||||
expandedMessages = [...expandedMessages, msgId];
|
||||
}
|
||||
expandedMessages = expandedMessages;
|
||||
}
|
||||
|
||||
function shouldCollapse(content: string): boolean {
|
||||
|
|
@ -819,6 +821,11 @@
|
|||
<span class="token-count" class:warning={estimatedTokens > 20000} class:danger={estimatedTokens > TOKEN_WARNING_THRESHOLD}>
|
||||
~{(estimatedTokens / 1000).toFixed(1)}k Token
|
||||
</span>
|
||||
{#if !detached}
|
||||
<button class="detach-btn" onclick={() => invoke('chat_window_open')} title="Chat herausloesen">⧉</button>
|
||||
{:else}
|
||||
<button class="detach-btn" onclick={() => invoke('chat_window_close')} title="Zurueck ins Hauptfenster">⧇</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -878,7 +885,7 @@
|
|||
</div>
|
||||
{:else if message.role === 'assistant'}
|
||||
{#if message.content}
|
||||
{#if shouldCollapse(message.content) && !expandedMessages.has(message.id)}
|
||||
{#if shouldCollapse(message.content) && !expandedMessages.includes(message.id) && !($isProcessing && isLastAssistantMessage(index))}
|
||||
<div class="msg-collapsed">
|
||||
{@html renderMarkdown(message.content.split('\n').slice(0, COLLAPSE_LINES).join('\n') + '\n...')}
|
||||
</div>
|
||||
|
|
@ -887,7 +894,7 @@
|
|||
</button>
|
||||
{:else}
|
||||
{@html renderMarkdown(message.content)}
|
||||
{#if shouldCollapse(message.content)}
|
||||
{#if shouldCollapse(message.content) && !($isProcessing && isLastAssistantMessage(index))}
|
||||
<button class="expand-btn" onclick={() => toggleExpand(message.id)}>
|
||||
▲ Einklappen
|
||||
</button>
|
||||
|
|
@ -1154,6 +1161,24 @@
|
|||
color: var(--error);
|
||||
}
|
||||
|
||||
.detach-btn {
|
||||
padding: 0.2rem 0.45rem;
|
||||
background: var(--bg-tertiary, #2a2a3e);
|
||||
border: 1px solid var(--border, #333);
|
||||
border-radius: var(--radius-sm, 4px);
|
||||
color: var(--text-secondary, #aaa);
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.detach-btn:hover {
|
||||
background: var(--accent, #6c8aff);
|
||||
color: white;
|
||||
border-color: var(--accent, #6c8aff);
|
||||
}
|
||||
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
|
|
|
|||
26
src/routes/chat-window/+page.svelte
Normal file
26
src/routes/chat-window/+page.svelte
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<script lang="ts">
|
||||
import ChatPanel from '$lib/components/ChatPanel.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
document.documentElement.classList.add('detached-window');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="detached-chat">
|
||||
<ChatPanel detached={true} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(html.detached-window) {
|
||||
background: var(--bg-primary, #1a1a2e);
|
||||
}
|
||||
|
||||
.detached-chat {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in a new issue