generated from nhcarrigan/template
bf411adeb7
## Summary This PR resolves several critical bugs that were blocking the permission modal and causing config loss: - **Permission modal not appearing** - Fixed z-index issues and runtime errors - **Config store race condition** - Resolved critical race condition causing settings to be lost - **Excessive logging** - Removed redundant fmt layer that was writing to hidden stdout - **System tool prompts** - Prevented unnecessary permission prompts for built-in tools - **Permission batching** - Added support for parallel permission requests - **ExitPlanMode tool** - Fixed ExitPlanMode tool not functioning correctly ## Changes Made ### Permission Modal Fixes - Updated z-index to proper value (9999) to ensure modal appears above all other UI elements - Fixed runtime errors that were preventing modal from rendering - Resolved issues with permission grants not being properly applied ### Config Store Race Condition - Fixed critical race condition where multiple rapid config updates would result in lost settings - Ensured config writes are properly sequenced to prevent data loss - Added proper synchronisation for config store operations ### Logging Cleanup - Removed redundant fmt formatting layer that was outputting to hidden stdout - Cleaned up excessive debug logging added during troubleshooting - Removed temporary debugging documentation files ### UX Improvements - Added close confirmation modal with minimise to tray option - Implemented batching for parallel permission requests - Added debug console for viewing frontend and backend logs ### ExitPlanMode Fix - Fixed ExitPlanMode tool not functioning correctly, ensuring proper transitions out of plan mode ## Issues Resolved Closes #112 - Permission flow now properly handles multiple tool requests Closes #113 - ExitPlanMode tool now functions correctly Closes #126 - Debug console feature added (partial - basic implementation complete) ## Test Plan - [x] Permission modal appears and functions correctly - [x] Config settings persist across app restarts - [x] No excessive logging in production builds - [x] System tools don't trigger permission prompts - [x] Parallel permission requests are properly batched - [x] Debug console displays frontend and backend logs - [x] ExitPlanMode properly exits plan mode --- ✨ This PR was created with help from Hikari~ 🌸 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Reviewed-on: #127 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
331 lines
7.7 KiB
Svelte
331 lines
7.7 KiB
Svelte
<script lang="ts">
|
|
import { onMount } from "svelte";
|
|
import { debugConsoleStore, filteredLogs, type LogLevel } from "$lib/stores/debugConsole";
|
|
|
|
let isOpen = $state(false);
|
|
let logs = $state($filteredLogs);
|
|
let filterLevel = $state<LogLevel | "all">("all");
|
|
let autoScroll = $state(true);
|
|
let logContainerElement: HTMLDivElement | undefined = $state();
|
|
|
|
// Watch for log changes and auto-scroll
|
|
$effect(() => {
|
|
logs = $filteredLogs;
|
|
|
|
// Auto-scroll to bottom when logs change
|
|
if (autoScroll && logContainerElement) {
|
|
setTimeout(() => {
|
|
if (logContainerElement) {
|
|
logContainerElement.scrollTop = logContainerElement.scrollHeight;
|
|
}
|
|
}, 0);
|
|
}
|
|
});
|
|
|
|
onMount(() => {
|
|
// Set up console capture and backend listener
|
|
debugConsoleStore.setupConsoleCapture();
|
|
debugConsoleStore.setupBackendLogsListener();
|
|
|
|
// Subscribe to store
|
|
const unsubscribe = debugConsoleStore.subscribe((state) => {
|
|
isOpen = state.isOpen;
|
|
filterLevel = state.filterLevel;
|
|
autoScroll = state.autoScroll;
|
|
});
|
|
|
|
return () => {
|
|
unsubscribe();
|
|
debugConsoleStore.restoreConsole();
|
|
};
|
|
});
|
|
|
|
function handleClose() {
|
|
debugConsoleStore.close();
|
|
}
|
|
|
|
function handleClear() {
|
|
debugConsoleStore.clear();
|
|
}
|
|
|
|
function handleFilterChange(level: LogLevel | "all") {
|
|
debugConsoleStore.setFilterLevel(level);
|
|
}
|
|
|
|
function handleAutoScrollToggle() {
|
|
debugConsoleStore.setAutoScroll(!autoScroll);
|
|
}
|
|
|
|
function formatTimestamp(date: Date): string {
|
|
return date.toLocaleTimeString("en-US", {
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
second: "2-digit",
|
|
fractionalSecondDigits: 3,
|
|
});
|
|
}
|
|
|
|
function getLevelColor(level: LogLevel): string {
|
|
switch (level) {
|
|
case "debug":
|
|
return "#9CA3AF"; // gray
|
|
case "info":
|
|
return "#3B82F6"; // blue
|
|
case "warn":
|
|
return "#F59E0B"; // amber
|
|
case "error":
|
|
return "#EF4444"; // red
|
|
}
|
|
}
|
|
|
|
function getSourceBadgeColor(source: "frontend" | "backend"): string {
|
|
return source === "frontend" ? "#8B5CF6" : "#10B981"; // purple for frontend, green for backend
|
|
}
|
|
</script>
|
|
|
|
{#if isOpen}
|
|
<div class="debug-console-overlay">
|
|
<div class="debug-console">
|
|
<div class="debug-console-header">
|
|
<h2>Debug Console</h2>
|
|
<div class="debug-console-controls">
|
|
<div class="filter-buttons">
|
|
<button
|
|
class="filter-btn"
|
|
class:active={filterLevel === "all"}
|
|
onclick={() => handleFilterChange("all")}
|
|
>
|
|
All
|
|
</button>
|
|
<button
|
|
class="filter-btn"
|
|
class:active={filterLevel === "debug"}
|
|
onclick={() => handleFilterChange("debug")}
|
|
style="color: {getLevelColor('debug')}"
|
|
>
|
|
Debug
|
|
</button>
|
|
<button
|
|
class="filter-btn"
|
|
class:active={filterLevel === "info"}
|
|
onclick={() => handleFilterChange("info")}
|
|
style="color: {getLevelColor('info')}"
|
|
>
|
|
Info
|
|
</button>
|
|
<button
|
|
class="filter-btn"
|
|
class:active={filterLevel === "warn"}
|
|
onclick={() => handleFilterChange("warn")}
|
|
style="color: {getLevelColor('warn')}"
|
|
>
|
|
Warn
|
|
</button>
|
|
<button
|
|
class="filter-btn"
|
|
class:active={filterLevel === "error"}
|
|
onclick={() => handleFilterChange("error")}
|
|
style="color: {getLevelColor('error')}"
|
|
>
|
|
Error
|
|
</button>
|
|
</div>
|
|
<button
|
|
class="auto-scroll-btn"
|
|
class:active={autoScroll}
|
|
onclick={handleAutoScrollToggle}
|
|
>
|
|
{autoScroll ? "🔒" : "🔓"} Auto-scroll
|
|
</button>
|
|
<button class="clear-btn" onclick={handleClear}> 🗑️ Clear </button>
|
|
<button class="close-btn" onclick={handleClose}> ✕ </button>
|
|
</div>
|
|
</div>
|
|
<div class="debug-console-content" bind:this={logContainerElement}>
|
|
{#if logs.length === 0}
|
|
<div class="empty-state">No logs yet...</div>
|
|
{:else}
|
|
{#each logs as log (log.id)}
|
|
<div class="log-entry" data-level={log.level}>
|
|
<span class="log-timestamp">{formatTimestamp(log.timestamp)}</span>
|
|
<span class="log-level" style="color: {getLevelColor(log.level)}">
|
|
[{log.level.toUpperCase()}]
|
|
</span>
|
|
<span class="log-source" style="background-color: {getSourceBadgeColor(log.source)}">
|
|
{log.source}
|
|
</span>
|
|
<span class="log-message">{log.message}</span>
|
|
</div>
|
|
{/each}
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<style>
|
|
.debug-console-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 9999;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
backdrop-filter: blur(4px);
|
|
}
|
|
|
|
.debug-console {
|
|
width: 90%;
|
|
height: 80%;
|
|
max-width: 1400px;
|
|
background-color: #1a1a1a;
|
|
border-radius: 8px;
|
|
border: 1px solid #333;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
.debug-console-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 12px 16px;
|
|
background-color: #252525;
|
|
border-bottom: 1px solid #333;
|
|
}
|
|
|
|
.debug-console-header h2 {
|
|
margin: 0;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #fff;
|
|
}
|
|
|
|
.debug-console-controls {
|
|
display: flex;
|
|
gap: 8px;
|
|
align-items: center;
|
|
}
|
|
|
|
.filter-buttons {
|
|
display: flex;
|
|
gap: 4px;
|
|
}
|
|
|
|
.filter-btn {
|
|
padding: 4px 12px;
|
|
background-color: transparent;
|
|
border: 1px solid #444;
|
|
border-radius: 4px;
|
|
color: #999;
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.filter-btn:hover {
|
|
background-color: #333;
|
|
}
|
|
|
|
.filter-btn.active {
|
|
background-color: #444;
|
|
border-color: currentColor;
|
|
}
|
|
|
|
.auto-scroll-btn,
|
|
.clear-btn {
|
|
padding: 4px 12px;
|
|
background-color: #333;
|
|
border: 1px solid #444;
|
|
border-radius: 4px;
|
|
color: #fff;
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.auto-scroll-btn:hover,
|
|
.clear-btn:hover {
|
|
background-color: #444;
|
|
}
|
|
|
|
.auto-scroll-btn.active {
|
|
background-color: #10b981;
|
|
border-color: #10b981;
|
|
}
|
|
|
|
.close-btn {
|
|
padding: 4px 12px;
|
|
background-color: #ef4444;
|
|
border: none;
|
|
border-radius: 4px;
|
|
color: #fff;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.close-btn:hover {
|
|
background-color: #dc2626;
|
|
}
|
|
|
|
.debug-console-content {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 16px;
|
|
background-color: #0f0f0f;
|
|
font-family: "Fira Code", "Consolas", monospace;
|
|
font-size: 13px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.empty-state {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
color: #666;
|
|
font-style: italic;
|
|
}
|
|
|
|
.log-entry {
|
|
display: flex;
|
|
gap: 8px;
|
|
padding: 4px 0;
|
|
border-bottom: 1px solid #1a1a1a;
|
|
}
|
|
|
|
.log-entry:hover {
|
|
background-color: #1a1a1a;
|
|
}
|
|
|
|
.log-timestamp {
|
|
color: #666;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.log-level {
|
|
font-weight: 600;
|
|
flex-shrink: 0;
|
|
min-width: 60px;
|
|
}
|
|
|
|
.log-source {
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: #fff;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.log-message {
|
|
color: #e5e5e5;
|
|
word-break: break-word;
|
|
}
|
|
</style>
|