fix: improve chat scroll calculation and conversation deletion handling

- Use getOuterHeight helper for accurate element measurements including margins
- Simplify gap calculation for thought and streaming indicators
- Add top fade gradient to chat area for better visual polish
- Queue AI file deletions to prevent blocking conversation deletion
- Only trigger modal close callback when no conversation is active
This commit is contained in:
Andrew Beal 2025-12-20 01:34:41 +00:00
parent 71d19545dd
commit e3e30a2b47
4 changed files with 29 additions and 9 deletions

View file

@ -11,6 +11,7 @@
import { tick } from "svelte";
import { Selector } from "Enums/Selector";
import { Exception } from "Helpers/Exception";
import { getOuterHeight } from "Helpers/ElementHelper";
export let cancelling: boolean = false;
export let messages: ConversationContent[] = [];
@ -45,20 +46,19 @@
return;
}
const gap = parseFloat(getComputedStyle(chatContainer).gap) || 0;
const paddingTop = parseFloat(getComputedStyle(chatContainer).paddingTop) || 0;
const paddingBottom = parseFloat(getComputedStyle(chatContainer).paddingBottom) || 0;
const messageElement = messageElements.sort((a, b) => a.index - b.index)[messageElements.length - 1];
let messageSpace = messageElement.element.offsetHeight;
let messageSpace = getOuterHeight(messageElement.element);
if (!shouldSettle) {
const gap = parseFloat(getComputedStyle(chatContainer).gap) || 0;
if (thoughtIndicatorElement) {
messageSpace += thoughtIndicatorElement.offsetHeight + gap + gap;
messageSpace += getOuterHeight(thoughtIndicatorElement) + gap;
}
if (streamingIndicatorElement) {
messageSpace += streamingIndicatorElement.offsetHeight + gap + gap;
messageSpace += getOuterHeight(streamingIndicatorElement) + gap;
}
}
@ -247,6 +247,7 @@
</script>
<div class="chat-area" bind:this={chatContainer} on:scroll={handleScroll} use:observeResize>
<div class="top-fade"></div>
{#each messages as message, index}
{@const content = message.getDisplayContent()}
{#if message.shouldDisplayContent && content.trim() !== ""}
@ -294,12 +295,22 @@
</div>
<style>
.top-fade {
position: absolute;
width: 100%;
height: var(--size-4-4);
background-image: linear-gradient(to bottom, var(--background-secondary), transparent);
margin-top: calc(var(--size-4-4) * -1);
margin-left: calc(var(--size-4-3) * -1);
z-index: 1;
}
.chat-area {
display: flex;
flex-direction: column;
height: 100%;
overflow: auto;
padding: var(--size-4-3);
padding: var(--size-4-4) var(--size-4-3) var(--size-4-3) var(--size-4-3);
gap: var(--size-4-2);
scroll-behavior: smooth;
}

5
Helpers/ElementHelper.ts Normal file
View file

@ -0,0 +1,5 @@
export function getOuterHeight(element: HTMLElement): number {
const marginTop = parseFloat(getComputedStyle(element).marginTop) || 0;
const marginBottom = parseFloat(getComputedStyle(element).marginBottom) || 0;
return element.offsetHeight + marginTop + marginBottom;
}

View file

@ -129,6 +129,8 @@ export class ConversationHistoryModal extends Modal {
const { contentEl } = this;
contentEl.empty();
this.onModalClose?.();
if (this.conversationFileSystemService.getCurrentConversationPath() === null) {
this.onModalClose?.();
}
}
}

View file

@ -14,6 +14,7 @@ export class ConversationFileSystemService {
private aiFileService: IAIFileService | undefined;
private currentConversationPath: string | null = null;
private deletionQueue: Promise<void> = Promise.resolve();
public constructor() {
this.fileSystemService = Resolve<FileSystemService>(Services.FileSystemService);
@ -92,8 +93,9 @@ export class ConversationFileSystemService {
return readResult;
}
await this.attemptAIFileDeletion(readResult);
// Queue this to execute silently in the background - it's just a best effort
this.deletionQueue = this.deletionQueue.then(() => this.attemptAIFileDeletion(readResult));
const deleteResult = await this.fileSystemService.deleteFile(this.currentConversationPath, true, false);
if (deleteResult instanceof Error) {