generated from nhcarrigan/template
feat: multiple UI improvements, font settings, and memory file display names #175
@@ -7,6 +7,7 @@
|
||||
import Markdown from "./Markdown.svelte";
|
||||
import HighlightedText from "./HighlightedText.svelte";
|
||||
import ThinkingBlock from "./ThinkingBlock.svelte";
|
||||
import ToolCallBlock from "./ToolCallBlock.svelte";
|
||||
import { searchState, searchQuery } from "$lib/stores/search";
|
||||
import { clipboardStore } from "$lib/stores/clipboard";
|
||||
import { shouldHidePaths, maskPaths, showThinkingBlocks } from "$lib/stores/config";
|
||||
@@ -208,22 +209,6 @@
|
||||
if (!currentConversationId) return;
|
||||
await invoke("send_prompt", { conversationId: currentConversationId, message: "/compact" });
|
||||
}
|
||||
|
||||
// Collapsible tool lines
|
||||
const TOOL_COLLAPSE_THRESHOLD = 60;
|
||||
let expandedToolLines: Record<string, boolean> = {};
|
||||
|
||||
function isToolContentLong(content: string): boolean {
|
||||
return content.length > TOOL_COLLAPSE_THRESHOLD;
|
||||
}
|
||||
|
||||
function truncateToolContent(content: string): string {
|
||||
return content.slice(0, TOOL_COLLAPSE_THRESHOLD) + "…";
|
||||
}
|
||||
|
||||
function toggleToolLine(id: string) {
|
||||
expandedToolLines = { ...expandedToolLines, [id]: !expandedToolLines[id] };
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -258,6 +243,18 @@
|
||||
{#if showThinking}
|
||||
<ThinkingBlock content={line.content} timestamp={line.timestamp} />
|
||||
{/if}
|
||||
{:else if line.type === "tool"}
|
||||
<div
|
||||
style={line.parentToolUseId
|
||||
? "margin-left: 16px; padding-left: 8px; border-left: 2px solid var(--accent-primary);"
|
||||
: ""}
|
||||
>
|
||||
<ToolCallBlock
|
||||
toolName={line.toolName ?? null}
|
||||
content={maskPaths(line.content, hidePaths)}
|
||||
timestamp={line.timestamp}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="terminal-line mb-2 {getLineClass(line.type)} relative group"
|
||||
@@ -296,9 +293,6 @@
|
||||
{#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 === "compact-prompt"}
|
||||
<button class="compact-action-btn" onclick={handleCompact}>
|
||||
⚡ Compact Conversation
|
||||
@@ -330,22 +324,6 @@
|
||||
<span class="copy-text">{copiedMessageId === line.id ? "Copied!" : "Copy"}</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else if line.type === "tool" && isToolContentLong(maskPaths(line.content, hidePaths))}
|
||||
<span class="tool-collapsible">
|
||||
<HighlightedText
|
||||
content={expandedToolLines[line.id]
|
||||
? maskPaths(line.content, hidePaths)
|
||||
: truncateToolContent(maskPaths(line.content, hidePaths))}
|
||||
searchQuery={currentSearchQuery}
|
||||
/>
|
||||
<button
|
||||
class="tool-toggle-btn"
|
||||
onclick={() => toggleToolLine(line.id)}
|
||||
title={expandedToolLines[line.id] ? "Collapse" : "Expand to see full content"}
|
||||
>
|
||||
{expandedToolLines[line.id] ? "▲" : "▼"}
|
||||
</button>
|
||||
</span>
|
||||
{:else}
|
||||
<HighlightedText
|
||||
content={maskPaths(line.content, hidePaths)}
|
||||
@@ -501,28 +479,4 @@
|
||||
.terminal-line {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tool-collapsible {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.4em;
|
||||
}
|
||||
|
||||
.tool-toggle-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-tertiary, #6b7280);
|
||||
cursor: pointer;
|
||||
font-size: 0.7em;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.15s ease;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.tool-toggle-btn:hover {
|
||||
opacity: 1;
|
||||
color: var(--terminal-tool, #c084fc);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
toolName: string | null;
|
||||
content: string;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
let { toolName, content, timestamp }: Props = $props();
|
||||
let isExpanded = $state(false);
|
||||
|
||||
function toggleExpanded() {
|
||||
isExpanded = !isExpanded;
|
||||
}
|
||||
|
||||
function formatTime(date: Date): string {
|
||||
return date.toLocaleTimeString("en-US", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="tool-block">
|
||||
<button class="tool-header" onclick={toggleExpanded} type="button">
|
||||
<span class="tool-timestamp">{formatTime(timestamp)}</span>
|
||||
<svg
|
||||
class="tool-icon"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
|
||||
/>
|
||||
</svg>
|
||||
{#if toolName}
|
||||
<span class="tool-name">[{toolName}]</span>
|
||||
{/if}
|
||||
<span class="tool-label">{content}</span>
|
||||
<svg
|
||||
class="chevron"
|
||||
class:expanded={isExpanded}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="14"
|
||||
height="14"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{#if isExpanded}
|
||||
<div class="tool-content">
|
||||
{content}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.tool-block {
|
||||
margin-bottom: 0.5rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0.375rem;
|
||||
background: var(--bg-secondary);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.tool-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
color: var(--terminal-tool, #c084fc);
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tool-header:hover {
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tool-timestamp {
|
||||
font-family: monospace;
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.7;
|
||||
color: var(--text-secondary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tool-icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tool-name {
|
||||
font-family: monospace;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
color: var(--terminal-tool-name, #ddd6fe);
|
||||
}
|
||||
|
||||
.tool-label {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
font-style: italic;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.chevron {
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.chevron.expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.tool-content {
|
||||
padding: 0.75rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
color: var(--terminal-tool, #c084fc);
|
||||
font-family: monospace;
|
||||
font-size: 0.875rem;
|
||||
font-style: italic;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user