chore: linter
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 55s
CI / Lint & Test (pull_request) Failing after 7m32s
CI / Build Linux (pull_request) Has been skipped
CI / Build Windows (cross-compile) (pull_request) Has been skipped

This commit is contained in:
2026-01-20 11:12:41 -08:00
parent 511caa3a8e
commit e4d2d47e61
7 changed files with 74 additions and 30 deletions
+29 -13
View File
@@ -2,6 +2,7 @@
import { claudeStore } from "$lib/stores/claude"; import { claudeStore } from "$lib/stores/claude";
import { onMount } from "svelte"; import { onMount } from "svelte";
import type { Conversation } from "$lib/stores/conversations"; import type { Conversation } from "$lib/stores/conversations";
import { SvelteMap } from "svelte/reactivity";
let conversations: Map<string, Conversation> = new Map(); let conversations: Map<string, Conversation> = new Map();
let activeConversationId: string | null = null; let activeConversationId: string | null = null;
@@ -12,7 +13,7 @@
let connectedConversationId: string | null = null; let connectedConversationId: string | null = null;
// Track last seen message count for each conversation // Track last seen message count for each conversation
let lastSeenMessageCount: Map<string, number> = new Map(); let lastSeenMessageCount = new SvelteMap<string, number>();
claudeStore.conversations.subscribe((convs) => { claudeStore.conversations.subscribe((convs) => {
conversations = convs; conversations = convs;
@@ -75,6 +76,11 @@
event.stopPropagation(); event.stopPropagation();
editingTabId = id; editingTabId = id;
editingName = name; editingName = name;
// Focus input after DOM update
setTimeout(() => {
const input = document.querySelector('.tab-item input[type="text"]') as HTMLInputElement;
if (input) input.focus();
}, 0);
} }
function saveTabName() { function saveTabName() {
@@ -113,6 +119,13 @@
} }
} }
function handleTabKeydown(id: string, event: KeyboardEvent) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
switchTab(id);
}
}
// Keyboard shortcuts // Keyboard shortcuts
onMount(() => { onMount(() => {
function handleGlobalKeydown(event: KeyboardEvent) { function handleGlobalKeydown(event: KeyboardEvent) {
@@ -165,6 +178,7 @@
? 'bg-[var(--bg-terminal)] text-[var(--text-primary)] border-t border-l border-r border-[var(--border-color)]' ? 'bg-[var(--bg-terminal)] text-[var(--text-primary)] border-t border-l border-r border-[var(--border-color)]'
: 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)] hover:bg-[var(--bg-terminal)]/50'}" : 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)] hover:bg-[var(--bg-terminal)]/50'}"
onclick={() => switchTab(id)} onclick={() => switchTab(id)}
onkeydown={(e) => handleTabKeydown(id, e)}
role="tab" role="tab"
tabindex={0} tabindex={0}
aria-selected={id === activeConversationId} aria-selected={id === activeConversationId}
@@ -177,22 +191,29 @@
onkeydown={handleKeydown} onkeydown={handleKeydown}
onclick={(e) => e.stopPropagation()} onclick={(e) => e.stopPropagation()}
class="bg-transparent border-b border-[var(--border-color)] outline-none px-0 py-0 text-sm w-32" class="bg-transparent border-b border-[var(--border-color)] outline-none px-0 py-0 text-sm w-32"
autofocus
/> />
{:else} {:else}
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div <div
class="w-2 h-2 rounded-full {getConnectionStatusColor(conversation.connectionStatus)}" class="w-2 h-2 rounded-full {getConnectionStatusColor(conversation.connectionStatus)}"
title="Connection: {conversation.connectionStatus}{id !== connectedConversationId && connectedConversationId ? ' (Another tab is connected)' : ''}" title="Connection: {conversation.connectionStatus}{id !== connectedConversationId &&
/> connectedConversationId
? ' (Another tab is connected)'
: ''}"
></div>
<span <span
class="text-sm pr-6 max-w-[150px] truncate" class="text-sm pr-6 max-w-[150px] truncate"
ondblclick={(e) => startEditing(id, conversation.name, e)} ondblclick={(e) => startEditing(id, conversation.name, e)}
role="button"
tabindex={-1}
> >
{conversation.name} {conversation.name}
</span> </span>
{#if id !== activeConversationId && id === connectedConversationId} {#if id !== activeConversationId && id === connectedConversationId}
<span class="text-xs text-[var(--text-tertiary)]" title="This tab has the Claude connection"> <span
class="text-xs text-[var(--text-tertiary)]"
title="This tab has the Claude connection"
>
(connected) (connected)
</span> </span>
{/if} {/if}
@@ -200,7 +221,7 @@
<div <div
class="absolute -top-1 -right-1 w-2 h-2 rounded-full bg-blue-500 animate-pulse" class="absolute -top-1 -right-1 w-2 h-2 rounded-full bg-blue-500 animate-pulse"
title="New messages" title="New messages"
/> ></div>
{/if} {/if}
</div> </div>
{/if} {/if}
@@ -242,12 +263,7 @@
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg> </svg>
</button> </button>
</div> </div>
@@ -260,4 +276,4 @@
.tab-item { .tab-item {
min-width: 100px; min-width: 100px;
} }
</style> </style>
@@ -4,4 +4,4 @@
<div class="terminal-tabs" style="background: red; height: 36px; color: white;"> <div class="terminal-tabs" style="background: red; height: 36px; color: white;">
Debug: Tabs Component Loaded Debug: Tabs Component Loaded
</div> </div>
+1 -1
View File
@@ -73,7 +73,7 @@ User: ${formattedMessage}`;
} }
await invoke("send_prompt", { await invoke("send_prompt", {
conversationId, conversationId,
message: messageToSend message: messageToSend,
}); });
} catch (error) { } catch (error) {
console.error("Failed to send prompt:", error); console.error("Failed to send prompt:", error);
+1 -1
View File
@@ -81,7 +81,7 @@ Please continue where we left off and retry that action now that you have permis
await invoke("send_prompt", { await invoke("send_prompt", {
conversationId, conversationId,
message: contextMessage message: contextMessage,
}); });
} }
} catch (error) { } catch (error) {
+1 -1
View File
@@ -1,6 +1,6 @@
import { derived } from "svelte/store"; import { derived } from "svelte/store";
import { conversationsStore } from "./conversations"; import { conversationsStore } from "./conversations";
import type { ConnectionStatus, PermissionRequest, TerminalLine } from "$lib/types/messages"; import type { TerminalLine } from "$lib/types/messages";
import { characterState } from "$lib/stores/character"; import { characterState } from "$lib/stores/character";
import { import {
setShouldRestoreHistory, setShouldRestoreHistory,
+19 -6
View File
@@ -76,14 +76,23 @@ function createConversationsStore() {
); );
// Derived stores for compatibility with existing code // Derived stores for compatibility with existing code
const connectionStatus = derived(activeConversation, ($conv) => $conv?.connectionStatus || "disconnected"); const connectionStatus = derived(
activeConversation,
($conv) => $conv?.connectionStatus || "disconnected"
);
const terminalLines = derived(activeConversation, ($conv) => { const terminalLines = derived(activeConversation, ($conv) => {
return $conv?.terminalLines || []; return $conv?.terminalLines || [];
}); });
const sessionId = derived(activeConversation, ($conv) => $conv?.sessionId || null); const sessionId = derived(activeConversation, ($conv) => $conv?.sessionId || null);
const currentWorkingDirectory = derived(activeConversation, ($conv) => $conv?.workingDirectory || ""); const currentWorkingDirectory = derived(
activeConversation,
($conv) => $conv?.workingDirectory || ""
);
const isProcessing = derived(activeConversation, ($conv) => $conv?.isProcessing || false); const isProcessing = derived(activeConversation, ($conv) => $conv?.isProcessing || false);
const grantedTools = derived(activeConversation, ($conv) => $conv?.grantedTools || new Set()); const grantedTools = derived(
activeConversation,
($conv) => $conv?.grantedTools || new Set<string>()
);
return { return {
// Expose derived stores for compatibility // Expose derived stores for compatibility
@@ -198,7 +207,6 @@ function createConversationsStore() {
if (targetConv) { if (targetConv) {
characterState.setState(targetConv.characterState); characterState.setState(targetConv.characterState);
} }
} }
}, },
@@ -304,7 +312,12 @@ function createConversationsStore() {
return line.id; return line.id;
}, },
addLineToConversation: (conversationId: string, type: TerminalLine["type"], content: string, toolName?: string) => { addLineToConversation: (
conversationId: string,
type: TerminalLine["type"],
content: string,
toolName?: string
) => {
ensureInitialized(); ensureInitialized();
const line: TerminalLine = { const line: TerminalLine = {
@@ -443,4 +456,4 @@ function createConversationsStore() {
export const conversationsStore = createConversationsStore(); export const conversationsStore = createConversationsStore();
// Initialize immediately // Initialize immediately
conversationsStore.initialize(); conversationsStore.initialize();
+22 -7
View File
@@ -21,7 +21,7 @@ interface StateChangePayload {
conversation_id?: string; conversation_id?: string;
} }
let connectedConversations = new Set<string>(); const connectedConversations = new Set<string>();
let unlisteners: Array<() => void> = []; let unlisteners: Array<() => void> = [];
let skipNextGreeting = false; let skipNextGreeting = false;
@@ -72,7 +72,7 @@ async function sendGreeting(conversationId: string) {
try { try {
await invoke("send_prompt", { await invoke("send_prompt", {
conversationId, conversationId,
message: greetingPrompt message: greetingPrompt,
}); });
} catch (error) { } catch (error) {
console.error("Failed to send greeting:", error); console.error("Failed to send greeting:", error);
@@ -140,7 +140,11 @@ export async function initializeTauriListeners() {
if (targetConversationId) { if (targetConversationId) {
// Add system message to the correct conversation // Add system message to the correct conversation
claudeStore.addLineToConversation(targetConversationId, "system", "Connected to Claude Code"); claudeStore.addLineToConversation(
targetConversationId,
"system",
"Connected to Claude Code"
);
// Update character state for this conversation // Update character state for this conversation
claudeStore.setCharacterStateForConversation(targetConversationId, "idle"); claudeStore.setCharacterStateForConversation(targetConversationId, "idle");
@@ -162,7 +166,11 @@ export async function initializeTauriListeners() {
// Don't add system message if we're about to reconnect // Don't add system message if we're about to reconnect
if (!skipNextGreeting && targetConversationId) { if (!skipNextGreeting && targetConversationId) {
claudeStore.addLineToConversation(targetConversationId, "system", "Disconnected from Claude Code"); claudeStore.addLineToConversation(
targetConversationId,
"system",
"Disconnected from Claude Code"
);
} }
// Update character state for this conversation // Update character state for this conversation
@@ -225,7 +233,6 @@ export async function initializeTauriListeners() {
const outputUnlisten = await listen<OutputPayload>("claude:output", (event) => { const outputUnlisten = await listen<OutputPayload>("claude:output", (event) => {
const { line_type, content, tool_name, conversation_id } = event.payload; const { line_type, content, tool_name, conversation_id } = event.payload;
// Always store the output to the correct conversation // Always store the output to the correct conversation
if (conversation_id) { if (conversation_id) {
claudeStore.addLineToConversation( claudeStore.addLineToConversation(
@@ -256,7 +263,11 @@ export async function initializeTauriListeners() {
// Store session ID for the correct conversation // Store session ID for the correct conversation
if (conversation_id) { if (conversation_id) {
claudeStore.setSessionIdForConversation(conversation_id, session_id); claudeStore.setSessionIdForConversation(conversation_id, session_id);
claudeStore.addLineToConversation(conversation_id, "system", `Session: ${session_id.substring(0, 8)}...`); claudeStore.addLineToConversation(
conversation_id,
"system",
`Session: ${session_id.substring(0, 8)}...`
);
} else { } else {
// Fallback to active conversation if no conversation_id // Fallback to active conversation if no conversation_id
claudeStore.setSessionId(session_id); claudeStore.setSessionId(session_id);
@@ -294,7 +305,11 @@ export async function initializeTauriListeners() {
// Always store the permission message to the correct conversation // Always store the permission message to the correct conversation
if (conversation_id) { if (conversation_id) {
claudeStore.addLineToConversation(conversation_id, "system", `Permission requested for: ${tool_name}`); claudeStore.addLineToConversation(
conversation_id,
"system",
`Permission requested for: ${tool_name}`
);
} else if (conversation_id === activeConversationId) { } else if (conversation_id === activeConversationId) {
claudeStore.addLine("system", `Permission requested for: ${tool_name}`); claudeStore.addLine("system", `Permission requested for: ${tool_name}`);
} }