generated from nhcarrigan/template
feat: major feature additions and improvements (#135)
## Summary This PR includes major feature additions, bug fixes, comprehensive testing improvements, and responsive design enhancements! ## New Features ✨ ### Plugin & MCP Management (#133, #134) - **Plugin Management Panel**: Install, uninstall, enable/disable, and update plugins - **MCP Server Management Panel**: Add/remove MCP servers, view detailed configuration - **Marketplace Management**: Add/remove plugin marketplaces from GitHub - Backend commands for full CLI integration (`list_plugins`, `install_plugin`, `add_mcp_server`, etc.) - Beautiful UI with proper loading states, error handling, and theme support ### Visual Todo List Panel (#132) - Real-time todo list display when Hikari uses the `TodoWrite` tool - Shows pending/in-progress/completed status with visual indicators - Progress bar and completion count - Automatically clears on disconnect - Theme-aware styling ### Clear Session History Button (#130) - "Clear All Sessions" button in Session History panel - Confirmation dialog with session count - Keyboard support and accessibility features - Gives users control over disk usage ### CLI Version Display (#131) - Displays Claude CLI version in status bar - Auto-polls every 30 seconds for updates - Useful for debugging and feature compatibility ## Bug Fixes 🐛 ### Stats Panel Scrolling (#136) - **Fixed stats panel overflow**: Added scrollable container with `max-height` constraint - Stats panel now scrolls when content (Tools Used, Historical Costs, Budget sections) gets too long - Prevents content from overflowing off screen ### Agent Monitor Fixes (#122) - **Fixed agents stuck in "running" state**: Added `SubagentStop` hook parsing - **Fixed agents persisting after disconnect**: Call `clearConversation()` on disconnect - **Fixed "Kill All" button**: Now properly marks all agents as errored - **Fixed badge persisting after tab close**: Cleanup agents when conversation is deleted - Comprehensive tests for agent lifecycle management ### Discord RPC Cleanup (#129) - Removed file-based logging for Discord RPC - Replaced with proper `tracing` framework usage - Reduces disk usage and eliminates maintenance burden ### Close Modal Bug Fix (#128) - Fixed close confirmation modal not triggering after Discord RPC refactor - Removed frontend calls to deleted `log_discord_rpc` command - Modal now works correctly after all operations ### Responsive Design Fixes (#118) - Fixed top navigation icons getting cut off at small screen widths - Fixed Connect button disappearing on narrow screens - Fixed bottom status info (clock, CLI version) getting cut off - Added flex-wrap and mobile-optimised layouts - Icons-only mode on screens < 640px - Vertical stacking on screens < 768px ## Testing Improvements 🧪 ### Comprehensive Test Coverage (#114) - **417 backend tests** (up from 408) - **387 frontend tests** (up from 363) - **61%+ backend code coverage** - Added E2E integration tests for cross-platform notification commands - New test files: `agents.test.ts`, comprehensive CLI parsing tests - Tests for `debug_logger.rs`, `bridge_manager.rs`, `notifications.rs` - Console mocking for cleaner test output - Fixed flaky frontend tests ### Testing Documentation - Updated CLAUDE.md with comprehensive testing guidelines - Documented mocking approaches (console mocking, E2E command structure testing) - Added step-by-step guide for adding tests to new features - Goal to maintain ~100% test coverage documented ## Closes Closes #114 Closes #118 Closes #122 Closes #128 Closes #129 Closes #130 Closes #131 Closes #132 Closes #133 Closes #134 Closes #136 ## Technical Details - All new backend commands properly registered in `lib.rs` - CLI output parsing with comprehensive test coverage - Cross-platform compatibility verified through E2E tests (Linux CI can test Windows commands) - Theme-aware UI components using CSS variables throughout - Proper TypeScript types for all new stores and components - ESLint and Prettier compliant - All Clippy warnings addressed ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: #135 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #135.
This commit is contained in:
@@ -4,9 +4,10 @@
|
||||
import ConversationTabs from "./ConversationTabs.svelte";
|
||||
import Markdown from "./Markdown.svelte";
|
||||
import HighlightedText from "./HighlightedText.svelte";
|
||||
import ThinkingBlock from "./ThinkingBlock.svelte";
|
||||
import { searchState, searchQuery } from "$lib/stores/search";
|
||||
import { clipboardStore } from "$lib/stores/clipboard";
|
||||
import { shouldHidePaths, maskPaths } from "$lib/stores/config";
|
||||
import { shouldHidePaths, maskPaths, showThinkingBlocks } from "$lib/stores/config";
|
||||
|
||||
let terminalElement: HTMLDivElement;
|
||||
let shouldAutoScroll = true;
|
||||
@@ -24,6 +25,11 @@
|
||||
hidePaths = value;
|
||||
});
|
||||
|
||||
let showThinking = true;
|
||||
showThinkingBlocks.subscribe((value) => {
|
||||
showThinking = value;
|
||||
});
|
||||
|
||||
claudeStore.terminalLines.subscribe((value) => {
|
||||
lines = value;
|
||||
});
|
||||
@@ -84,6 +90,8 @@
|
||||
return "terminal-tool";
|
||||
case "error":
|
||||
return "terminal-error";
|
||||
case "thinking":
|
||||
return "terminal-thinking";
|
||||
default:
|
||||
return "terminal-default";
|
||||
}
|
||||
@@ -209,80 +217,86 @@
|
||||
</div>
|
||||
{:else}
|
||||
{#each lines as line (line.id)}
|
||||
<div
|
||||
class="terminal-line mb-2 {getLineClass(line.type)} relative group"
|
||||
style={line.parentToolUseId
|
||||
? "margin-left: 16px; padding-left: 8px; border-left: 2px solid var(--accent-primary);"
|
||||
: ""}
|
||||
>
|
||||
<span class="terminal-timestamp text-xs mr-2">{formatTime(line.timestamp)}</span>
|
||||
{#if line.parentToolUseId}
|
||||
<span class="text-xs mr-2 opacity-60" title="Message from subagent">
|
||||
<svg
|
||||
class="inline-block w-3 h-3 -mt-0.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
{#if line.type === "thinking"}
|
||||
{#if showThinking}
|
||||
<ThinkingBlock content={line.content} timestamp={line.timestamp} />
|
||||
{/if}
|
||||
{:else}
|
||||
<div
|
||||
class="terminal-line mb-2 {getLineClass(line.type)} relative group"
|
||||
style={line.parentToolUseId
|
||||
? "margin-left: 16px; padding-left: 8px; border-left: 2px solid var(--accent-primary);"
|
||||
: ""}
|
||||
>
|
||||
<span class="terminal-timestamp text-xs mr-2">{formatTime(line.timestamp)}</span>
|
||||
{#if line.parentToolUseId}
|
||||
<span class="text-xs mr-2 opacity-60" title="Message from subagent">
|
||||
<svg
|
||||
class="inline-block w-3 h-3 -mt-0.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
{/if}
|
||||
{#if line.cost && line.cost.costUsd > 0}
|
||||
<span
|
||||
class="terminal-cost text-xs mr-2"
|
||||
title="Input: {line.cost.inputTokens} | Output: {line.cost.outputTokens}"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
${line.cost.costUsd < 0.01
|
||||
? line.cost.costUsd.toFixed(4)
|
||||
: line.cost.costUsd.toFixed(3)}
|
||||
</span>
|
||||
{/if}
|
||||
{#if getLinePrefix(line.type)}
|
||||
<span class="terminal-prefix mr-2">{getLinePrefix(line.type)}</span>
|
||||
{/if}
|
||||
{#if line.toolName}
|
||||
<span class="terminal-tool-name mr-2">[{line.toolName}]</span>
|
||||
{/if}
|
||||
{#if line.type === "assistant" || line.type === "user"}
|
||||
<div class="message-content-wrapper">
|
||||
<Markdown
|
||||
content={maskPaths(line.content, hidePaths)}
|
||||
searchQuery={currentSearchQuery}
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
{/if}
|
||||
{#if line.cost && line.cost.costUsd > 0}
|
||||
<span
|
||||
class="terminal-cost text-xs mr-2"
|
||||
title="Input: {line.cost.inputTokens} | Output: {line.cost.outputTokens}"
|
||||
>
|
||||
${line.cost.costUsd < 0.01
|
||||
? line.cost.costUsd.toFixed(4)
|
||||
: line.cost.costUsd.toFixed(3)}
|
||||
</span>
|
||||
{/if}
|
||||
{#if getLinePrefix(line.type)}
|
||||
<span class="terminal-prefix mr-2">{getLinePrefix(line.type)}</span>
|
||||
{/if}
|
||||
{#if line.toolName}
|
||||
<span class="terminal-tool-name mr-2">[{line.toolName}]</span>
|
||||
{/if}
|
||||
{#if line.type === "assistant" || line.type === "user"}
|
||||
<div class="message-content-wrapper">
|
||||
<Markdown
|
||||
<button
|
||||
class="copy-message-btn opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
onclick={() => handleCopyMessage(line.id, line.content)}
|
||||
title="Copy message"
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>
|
||||
<span class="copy-text">{copiedMessageId === line.id ? "Copied!" : "Copy"}</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<HighlightedText
|
||||
content={maskPaths(line.content, hidePaths)}
|
||||
searchQuery={currentSearchQuery}
|
||||
/>
|
||||
<button
|
||||
class="copy-message-btn opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
onclick={() => handleCopyMessage(line.id, line.content)}
|
||||
title="Copy message"
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>
|
||||
<span class="copy-text">{copiedMessageId === line.id ? "Copied!" : "Copy"}</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<HighlightedText
|
||||
content={maskPaths(line.content, hidePaths)}
|
||||
searchQuery={currentSearchQuery}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user