Skip to content

Commit 27477c3

Browse files
authored
codegen-ui-fixes-part-2 (#10849)
* refactor(codegen): rename CodegenInputQueued to CodegenQueued * feat(ui): display attachments in queued codegen messages * update composition for the "new unread" and "scroll-to-bottom" buttons. * update codegen header * update FileSearch box styles * Fix the box blinking on query change
1 parent 807ce3b commit 27477c3

File tree

7 files changed

+152
-89
lines changed

7 files changed

+152
-89
lines changed

apps/desktop/src/components/VirtualList.svelte

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,11 @@
335335
});
336336
} else if (items) {
337337
untrack(() => {
338+
const hadNewItems = items.length > previousItemsLength && items.length > end;
338339
recalculate();
339-
newUnseenTail = items.length > previousItemsLength && items.length > end;
340+
if (hadNewItems) {
341+
newUnseenTail = true;
342+
}
340343
});
341344
}
342345
previousItemsLength = items.length;
@@ -369,26 +372,26 @@
369372
</div>
370373
</ScrollableContainer>
371374

372-
{#if newUnseenTail}
373-
<div class="new-items-notification" transition:fade={{ duration: 150 }}>
374-
<Button
375-
kind="outline"
376-
icon="arrow-down"
377-
tooltip="Scroll to bottom"
378-
onclick={scrollToBottomAndDismiss}
379-
>
380-
New unread
381-
</Button>
382-
</div>
383-
{/if}
384-
{#if !newUnseenTail && distanceFromBottom > 300}
385-
<div class="chat-scroll-to-bottom" transition:fade={{ duration: 150 }}>
386-
<Button
387-
kind="outline"
388-
icon="arrow-down"
389-
tooltip="Scroll to bottom"
390-
onclick={scrollToBottomAndDismiss}
391-
/>
375+
{#if distanceFromBottom > 300}
376+
<div class="feed-actions">
377+
{#if newUnseenTail}
378+
<button
379+
type="button"
380+
class="text-12 feed-actions__new-messages"
381+
transition:fade={{ duration: 150 }}
382+
onclick={scrollToBottomAndDismiss}
383+
>
384+
New unread
385+
</button>
386+
{/if}
387+
<div class="feed-actions__scroll-to-bottom" transition:fade={{ duration: 150 }}>
388+
<Button
389+
kind="outline"
390+
icon="arrow-down"
391+
tooltip="Scroll to bottom"
392+
onclick={scrollToBottomAndDismiss}
393+
/>
394+
</div>
392395
</div>
393396
{/if}
394397

@@ -404,34 +407,28 @@
404407
flex-direction: column;
405408
}
406409
407-
.new-items-notification {
410+
.feed-actions {
411+
display: flex;
408412
z-index: var(--z-floating);
409413
position: absolute;
414+
right: 16px;
410415
bottom: 14px;
411-
left: 50%;
412-
overflow: hidden;
413-
transform: translateX(-50%);
414-
border-radius: var(--radius-btn);
415-
background-color: var(--clr-bg-1);
416-
transition:
417-
box-shadow var(--transition-fast),
418-
transform var(--transition-medium);
419-
420-
&:hover {
421-
transform: scale(1.05) translateY(-2px);
422-
box-shadow: var(--fx-shadow-s);
423-
}
416+
gap: 4px;
424417
}
425418
426-
.new-items-notification:hover {
427-
transform: translateX(-50%) translateY(-2px);
419+
.feed-actions__new-messages {
420+
display: flex;
421+
align-items: center;
422+
justify-content: center;
423+
padding: 0 8px;
424+
border: 1px solid var(--clr-border-3);
425+
border-radius: var(--radius-btn);
426+
background-color: var(--clr-bg-2);
427+
color: var(--clr-text-1);
428428
}
429429
430-
.chat-scroll-to-bottom {
430+
.feed-actions__scroll-to-bottom {
431431
z-index: var(--z-floating);
432-
position: absolute;
433-
right: 16px;
434-
bottom: 14px;
435432
overflow: hidden;
436433
border-radius: var(--radius-btn);
437434
background-color: var(--clr-bg-1);

apps/desktop/src/components/codegen/CodegenChatLayout.svelte

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
branchIcon?: Snippet;
1010
pageWorkspaceActions: Snippet;
1111
pageContextActions?: Snippet;
12-
inWorkspaceInlineActions?: Snippet;
1312
inWorkspaceInlineContextActions?: Snippet;
1413
messages: Snippet;
1514
input?: Snippet;
@@ -22,7 +21,6 @@
2221
branchIcon,
2322
pageWorkspaceActions,
2423
pageContextActions,
25-
inWorkspaceInlineActions,
2624
inWorkspaceInlineContextActions,
2725
messages,
2826
input,
@@ -60,8 +58,6 @@
6058

6159
{#snippet actions()}
6260
{@render inWorkspaceInlineContextActions?.()}
63-
<div class="divider"></div>
64-
{@render inWorkspaceInlineActions?.()}
6561
{/snippet}
6662
</PreviewHeader>
6763
{/if}

apps/desktop/src/components/codegen/CodegenInput.svelte

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import CardOverlay from '$components/CardOverlay.svelte';
33
import Dropzone from '$components/Dropzone.svelte';
44
import AttachmentList from '$components/codegen/AttachmentList.svelte';
5-
import CodegenInputQueued from '$components/codegen/CodegenInputQueued.svelte';
5+
import CodegenQueued from '$components/codegen/CodegenQueued.svelte';
66
import FileSearch from '$components/codegen/FileSearch.svelte';
77
import { ATTACHMENT_SERVICE } from '$lib/codegen/attachmentService.svelte';
88
import {
@@ -50,7 +50,6 @@
5050
const attachments = $derived(attachmentService.getByBranch(branchName));
5151
5252
let editorRef = $state<ReturnType<typeof RichTextEditor>>();
53-
5453
let showAbortButton = $state(false);
5554
5655
$effect(() => {
@@ -118,14 +117,21 @@
118117
119118
let query = $state('');
120119
let callback = $state<((result: string) => void) | undefined>();
120+
121+
const placeholderVariants = [
122+
'What to build?',
123+
'Describe your changes.',
124+
'What to create?',
125+
'Describe what to build.',
126+
'What to code?'
127+
];
121128
</script>
122129

123130
<div class="dialog-wrapper">
124-
{#if query}
125-
<FileSearch {projectId} {query} onselect={callback} limit={8} />
126-
{/if}
131+
<FileSearch {projectId} {query} onselect={callback} limit={8} />
132+
127133
<div class="text-input dialog-input" data-remove-from-panning>
128-
<CodegenInputQueued {projectId} {stackId} {branchName} />
134+
<CodegenQueued {projectId} {stackId} {branchName} />
129135

130136
<Dropzone {handlers}>
131137
{#snippet overlay({ hovered, activated })}
@@ -140,13 +146,15 @@
140146
</div>
141147
{/if}
142148

149+
{@const randomPlaceholder =
150+
placeholderVariants[Math.floor(Math.random() * placeholderVariants.length)]}
143151
<RichTextEditor
144152
bind:this={editorRef}
145153
bind:value
146154
namespace="codegen-input"
147155
markdown={false}
148156
styleContext="chat-input"
149-
placeholder="What would you like to make..."
157+
placeholder="{randomPlaceholder} Use @ to reference files…"
150158
minHeight="4rem"
151159
onError={(e: unknown) => console.warn('Editor error', e)}
152160
initialText={value}

apps/desktop/src/components/codegen/CodegenMessages.svelte

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import { STACK_SERVICE } from '$lib/stacks/stackService.svelte';
3636
import { combineResults } from '$lib/state/helpers';
3737
import { UI_STATE } from '$lib/state/uiState.svelte';
38+
import { formatCompactNumber } from '$lib/utils/number';
3839
import { getEditorUri, URL_SERVICE } from '$lib/utils/url';
3940
import { inject } from '@gitbutler/core/context';
4041
import { reactive } from '@gitbutler/shared/reactiveUtils.svelte';
@@ -345,37 +346,26 @@
345346
{#snippet inWorkspaceInlineContextActions()}
346347
{@const stats = usageStats(events)}
347348
{@const contextUsage = Math.round(stats.contextUtilization * 100)}
348-
<div class="flex gap-8 items-center">
349+
350+
<div class="flex gap-10 items-center">
351+
{#if stats.tokens > 0}
352+
<Tooltip text="Tokens: {stats.tokens.toLocaleString()} / ${stats.cost.toFixed(2)} ">
353+
<span class="text-12 clr-text-2">
354+
{formatCompactNumber(stats.tokens)}
355+
</span>
356+
</Tooltip>
357+
{/if}
358+
349359
<Tooltip text="{contextUsage}% context used">
350360
<div
351361
class="context-utilization-piechart"
352362
style="--context-utilization: {contextUsage}%"
353363
></div>
354364
</Tooltip>
355365

356-
{#if contextUsage > 0}
357-
<KebabButton
358-
bind:el={workspaceContextActionsKebab}
359-
onclick={() => contextActionsContextMenu?.toggle()}
360-
/>
361-
{/if}
362-
</div>
363-
{/snippet}
364-
{#snippet inWorkspaceInlineActions()}
365-
<div class="flex gap-4">
366-
<Button
367-
kind="ghost"
368-
icon="mcp"
369-
size="tag"
370-
tooltip="MCP settings"
371-
onclick={() => mcpConfigModal?.open()}
372-
/>
373-
<Button
374-
kind="ghost"
375-
icon="mixer"
376-
size="tag"
377-
tooltip="Agent settings"
378-
onclick={() => settingsModal?.show()}
366+
<KebabButton
367+
bind:el={workspaceContextActionsKebab}
368+
onclick={() => contextActionsContextMenu?.toggle()}
379369
/>
380370
</div>
381371
{/snippet}
@@ -396,6 +386,7 @@
396386
reversedDirection
397387
/>
398388
{/snippet}
389+
399390
{#snippet pageContextActions()}
400391
{@const stats = usageStats(events)}
401392

@@ -677,6 +668,24 @@
677668
events.response.length === 0 ||
678669
['running', 'compacting'].includes(currentStatus(events.response, isStackActive))}
679670

671+
<ContextMenuSection>
672+
<ContextMenuItem
673+
label="MCP settings"
674+
icon="mcp"
675+
onclick={() => {
676+
mcpConfigModal?.open();
677+
contextActionsContextMenu?.close();
678+
}}
679+
/>
680+
<ContextMenuItem
681+
label="Agent settings"
682+
icon="mixer"
683+
onclick={() => {
684+
settingsModal?.show();
685+
contextActionsContextMenu?.close();
686+
}}
687+
/>
688+
</ContextMenuSection>
680689
<ContextMenuSection>
681690
<ContextMenuItem
682691
label="Clear context and rules"
@@ -747,13 +756,24 @@
747756
748757
.context-utilization-piechart {
749758
position: relative;
750-
width: 16px;
751-
height: 16px;
759+
width: 17px;
760+
height: 17px;
761+
border: 0.094rem solid var(--clr-text-2);
752762
border-radius: 50%;
753-
background: conic-gradient(
754-
var(--clr-text-2) var(--context-utilization),
755-
var(--clr-border-2) var(--context-utilization)
756-
);
763+
764+
&::after {
765+
position: absolute;
766+
top: 2px;
767+
left: 2px;
768+
width: calc(100% - 4px);
769+
height: calc(100% - 4px);
770+
border-radius: 50%;
771+
background: conic-gradient(
772+
var(--clr-text-2) var(--context-utilization),
773+
transparent var(--context-utilization)
774+
);
775+
content: '';
776+
}
757777
}
758778
759779
.context-utilization-badge-2 {

apps/desktop/src/components/codegen/CodegenInputQueued.svelte renamed to apps/desktop/src/components/codegen/CodegenQueued.svelte

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
<script lang="ts">
2+
import AttachmentList from '$components/codegen/AttachmentList.svelte';
23
import { messageQueueSelectors, messageQueueSlice } from '$lib/codegen/messageQueueSlice';
34
import { CLIENT_STATE } from '$lib/state/clientState.svelte';
45
import { inject } from '@gitbutler/core/context';
56
import { Icon, Button } from '@gitbutler/ui';
67
import { slide } from 'svelte/transition';
8+
import type { PromptAttachment } from '$lib/codegen/types';
79
810
type Message = {
911
thinkingLevel: any;
1012
model: any;
1113
permissionMode: any;
1214
prompt: string;
15+
attachments?: PromptAttachment[];
1316
};
1417
1518
interface Props {
@@ -52,7 +55,13 @@
5255
</script>
5356

5457
{#snippet messageContent(message: Message)}
55-
<p class="text-13 text-body message-text">{message.prompt}</p>
58+
<div class="message-content">
59+
<p class="text-13 text-body message-text">{message.prompt}</p>
60+
61+
{#if message.attachments && message.attachments.length > 0}
62+
<AttachmentList attachments={message.attachments} showRemoveButton={false} />
63+
{/if}
64+
</div>
5665

5766
<div class="message-actions">
5867
<Button
@@ -145,6 +154,13 @@
145154
}
146155
}
147156
157+
.message-content {
158+
display: flex;
159+
flex: 1;
160+
flex-direction: column;
161+
gap: 10px;
162+
}
163+
148164
.message-text {
149165
display: -webkit-box;
150166
overflow: hidden;

0 commit comments

Comments
 (0)