From 6b5d51df390e1bc0f4192b59e94bb995e3afd2c3 Mon Sep 17 00:00:00 2001 From: Andrew Beal Date: Sat, 11 Jul 2026 14:46:39 +0100 Subject: [PATCH] Add scroll fade indicators to artifact lists and plan steps - Add fade gradient to artifacts list that hides when scrolled to bottom - Update plan area fades to use scroll position instead of always showing - Add hover effect to artifact cards with glowing ellipse indicator - Fix indentation in UserMessage attachment styles --- Components/AssistantMessage.svelte | 79 +++++++++++++++++++++++++----- Components/ChatPlanArea.svelte | 25 ++++++++-- Components/UserMessage.svelte | 16 +++--- 3 files changed, 97 insertions(+), 23 deletions(-) diff --git a/Components/AssistantMessage.svelte b/Components/AssistantMessage.svelte index 2a281f2..09a0081 100644 --- a/Components/AssistantMessage.svelte +++ b/Components/AssistantMessage.svelte @@ -29,6 +29,24 @@ } return tally; } + + let isScrolledToBottom = true; + + function updateScrollFade(element: HTMLElement) { + const { scrollTop, scrollHeight, clientHeight } = element; + isScrolledToBottom = scrollHeight - scrollTop - clientHeight < 1; + } + + function artifactsListScrollAction(element: HTMLElement) { + updateScrollFade(element); + const handleScroll = () => updateScrollFade(element); + element.addEventListener("scroll", handleScroll); + return { + destroy() { + element.removeEventListener("scroll", handleScroll); + } + }; + }
@@ -50,18 +68,24 @@ {/if} {/each}
-
- {#each message.artifacts as artifact} -
- +
+
+ {#each message.artifacts as artifact}
- {basename(artifact.filePath)} - {artifactActionToCopy(artifact.action)} -
- {/each} + class="artifact-card" + aria-label="{artifact.filePath}" + > + +
+ {basename(artifact.filePath)} + {artifactActionToCopy(artifact.action)} +
+ {/each} +
+
{/if} @@ -169,9 +193,14 @@ margin-left: var(--size-4-1); } - .artifacts-list-container { + .artifacts-list-wrapper { grid-row: 2; grid-column: 1 / 3; + position: relative; + width: 100%; + } + + .artifacts-list-container { display: flex; flex-direction: column; overflow: scroll; @@ -184,6 +213,22 @@ display: none; } + .artifacts-list-fade { + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: var(--size-4-8); + background-image: linear-gradient(to bottom, transparent, var(--background-secondary)); + pointer-events: none; + opacity: 1; + transition: opacity 0.2s ease-out; + } + + .artifacts-list-fade.hidden { + opacity: 0; + } + .artifact-card { display: grid; grid-template-rows: auto; @@ -195,6 +240,7 @@ background-color: var(--background-secondary-alt); border-radius: var(--size-4-2); padding: 0 var(--size-4-1) 0 var(--size-4-2); + cursor: pointer; } .artifact-ellipse { @@ -207,16 +253,25 @@ align-self: center; } + .artifact-card:hover .artifact-ellipse { + width: 12px; + height: 12px; + box-shadow: 0px 0px 4px 1px currentColor; + } + .artifact-ellipse-create { background: var(--color-green); + color: var(--color-green); } .artifact-ellipse-modify { background: var(--color-blue); + color: var(--color-blue); } .artifact-ellipse-delete { background: var(--color-red); + color: var(--color-red); } .artifact-icon { diff --git a/Components/ChatPlanArea.svelte b/Components/ChatPlanArea.svelte index e7616bc..79916ad 100644 --- a/Components/ChatPlanArea.svelte +++ b/Components/ChatPlanArea.svelte @@ -19,6 +19,8 @@ let stepElements: (HTMLDivElement | null)[] = []; let isTransitioning = false; let resizeObserver: ResizeObserver | null = null; + let isScrolledToTop = true; + let isScrolledToBottom = true; $: steps = $executionPlanState.plan?.executionSteps; $: activeStepIndex = $executionPlanState.currentStepIndex; @@ -65,6 +67,8 @@ const stepsToShow = Math.min(3, steps.length); collapsedHeight = (stepsToShow * stepHeight) + (Math.max(0, stepsToShow - 1) * separatorHeight); + + updateScrollFade(); } onDestroy(() => { @@ -73,6 +77,15 @@ } }); + function updateScrollFade() { + if (!wrapperDiv) { + return; + } + const { scrollTop, scrollHeight, clientHeight } = wrapperDiv; + isScrolledToTop = scrollTop < 1; + isScrolledToBottom = scrollHeight - scrollTop - clientHeight < 1; + } + async function scrollToActiveStep() { if (!wrapperDiv || !$executionPlanState.plan || activeStepIndex === -1) { return; @@ -124,7 +137,7 @@ aria-label={expanded ? "Collapse planned steps" : "Expand planned steps"} role="button" tabindex=0> -
+
{#each steps as step, index }
@@ -153,8 +166,8 @@
{#if steps.length > 2} -
-
+
+
{/if}