Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,3 @@ temp-videos/

# Terminal output cache
.terminal-output-cache/

/okegas
/okegas (1)
/okegas (2)
11 changes: 11 additions & 0 deletions frontend/components/chat/tools/variants/classic/EditTool.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import type { ToolUseBlock, EditInput } from '$shared/types/unified';
import { FileHeader, DiffBlock } from './components';
import { addAiChange } from '$frontend/utils/ai-changes';

const { toolInput }: { toolInput: ToolUseBlock } = $props();
const input = $derived(toolInput.input as EditInput);
Expand All @@ -12,12 +13,22 @@
const replaceAll = $derived(input.replaceAll || false);

const badges = $derived(replaceAll ? [{ text: 'Replace All', color: 'bg-slate-100 dark:bg-slate-900 text-slate-700 dark:text-slate-300' }] : []);

const hasResult = $derived(!!toolInput.result && !toolInput.result.isError);
let editIndex = $state<number | null>(null);

$effect(() => {
if (hasResult && filePath) {
editIndex = addAiChange(filePath, oldString, newString);
}
});
</script>

<FileHeader
{filePath}
{fileName}
{badges}
{editIndex}
iconColor="text-emerald-600 dark:text-emerald-400"
/>

Expand Down
11 changes: 11 additions & 0 deletions frontend/components/chat/tools/variants/classic/WriteTool.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,29 @@
import type { ToolUseBlock, WriteInput } from '$shared/types/unified';
import { FileHeader, DiffBlock } from './components';
import TextMessage from '../../../formatters/TextMessage.svelte';
import { addAiChange } from '$frontend/utils/ai-changes';

const { toolInput }: { toolInput: ToolUseBlock } = $props();
const input = $derived(toolInput.input as WriteInput);

const filePath = $derived(input.filePath || '');
const fileName = $derived(filePath.split(/[/\\]/).pop() || filePath || 'unknown');
const content = $derived(input.content || '');

const hasResult = $derived(!!toolInput.result && !toolInput.result.isError);
let editIndex = $state<number | null>(null);

$effect(() => {
if (hasResult && filePath && content) {
editIndex = addAiChange(filePath, '', content);
}
});
</script>

<FileHeader
{filePath}
{fileName}
{editIndex}
iconColor="text-violet-600 dark:text-violet-400"
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
import Icon from '$frontend/components/common/display/Icon.svelte';
import { getFileIcon } from '$frontend/utils/file-icon-mappings';
import { revealFile } from '$frontend/stores/ui/file-peek.svelte';
import { requestAiScrollReveal } from '$frontend/utils/ai-changes';

function handleClick() {
revealFile(filePath);
if (editIndex !== undefined && editIndex !== null) {
requestAiScrollReveal(filePath, editIndex);
}
}

interface Props {
Expand All @@ -13,9 +17,10 @@
iconColor?: string;
badges?: Array<{ text: string; color: string }>;
box?: boolean;
editIndex?: number | null;
}

const { filePath, fileName, iconColor, badges = [], box = true }: Props = $props();
const { filePath, fileName, iconColor, badges = [], box = true, editIndex = null }: Props = $props();

const displayFileName = $derived(fileName || filePath.split(/[/\\]/).pop() || filePath);
</script>
Expand Down
14 changes: 13 additions & 1 deletion frontend/components/chat/tools/variants/compact/EditTool.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import type { ToolUseBlock, EditInput } from '$shared/types/unified';
import { countLineChanges } from '$shared/utils/diff-calculator';
import { ToolRow } from './components';
import { addAiChange } from '$frontend/utils/ai-changes';

const { toolInput }: { toolInput: ToolUseBlock } = $props();
const input = $derived(toolInput.input as EditInput);
Expand All @@ -12,7 +13,18 @@
// Count only lines that actually changed (LCS-based), so identical context
// lines inside the edited region aren't miscounted as +/-.
const diff = $derived(countLineChanges(input.oldString || '', input.newString || ''));

const hasResult = $derived(!!toolInput.result && !toolInput.result.isError);
const oldString = $derived(input.oldString || '');
const newString = $derived(input.newString || '');
let editIndex = $state<number | null>(null);

$effect(() => {
if (hasResult && filePath) {
editIndex = addAiChange(filePath, oldString, newString);
}
});
</script>

<ToolRow icon="lucide:pencil" label="Edited" {filePath} {fileName} {diff} />
<ToolRow icon="lucide:pencil" label="Edited" {filePath} {fileName} {diff} {editIndex} />

13 changes: 12 additions & 1 deletion frontend/components/chat/tools/variants/compact/WriteTool.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import type { ToolUseBlock, WriteInput } from '$shared/types/unified';
import { ToolRow } from './components';
import { addAiChange } from '$frontend/utils/ai-changes';

const { toolInput }: { toolInput: ToolUseBlock } = $props();
const input = $derived(toolInput.input as WriteInput);
Expand All @@ -9,7 +10,17 @@
const fileName = $derived(filePath.split(/[/\\]/).pop() || filePath || 'unknown');
const lines = $derived(input.content ? input.content.split('\n').length : 0);
const diff = $derived({ additions: lines });

const hasResult = $derived(!!toolInput.result && !toolInput.result.isError);
const content = $derived(input.content || '');
let editIndex = $state<number | null>(null);

$effect(() => {
if (hasResult && filePath && content) {
editIndex = addAiChange(filePath, '', content);
}
});
</script>

<ToolRow icon="lucide:file-plus" label="Wrote" {filePath} {fileName} {diff} />
<ToolRow icon="lucide:file-plus" label="Wrote" {filePath} {fileName} {diff} {editIndex} />

Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
<script lang="ts">
import { revealFile } from '$frontend/stores/ui/file-peek.svelte';
import { requestAiScrollReveal } from '$frontend/utils/ai-changes';

interface Props {
filePath: string;
fileName?: string;
operation?: string;
badges?: string[];
editIndex?: number | null;
}

const { filePath, fileName, operation, badges = [] }: Props = $props();
const { filePath, fileName, operation, badges = [], editIndex = null }: Props = $props();

const displayFileName = $derived(fileName || filePath.split(/[/\\]/).pop() || filePath);

function handleClick() {
revealFile(filePath);
if (editIndex !== null) {
requestAiScrollReveal(filePath, editIndex);
}
}
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { getFileIcon } from '$frontend/utils/file-icon-mappings';
import { getFolderIcon } from '$frontend/utils/folder-icon-mappings';
import { revealFile } from '$frontend/stores/ui/file-peek.svelte';
import { requestAiScrollReveal } from '$frontend/utils/ai-changes';
import type { IconName } from '$shared/types/ui/icons';

interface DiffStat {
Expand Down Expand Up @@ -37,6 +38,8 @@
expanded?: boolean;
/** Called when header row is clicked (for expandable rows) */
onclick?: () => void;
/** AI edit index for scroll-reveal targeting */
editIndex?: number | null;
}

let {
Expand All @@ -53,6 +56,7 @@
expandable = false,
expanded = $bindable(false),
onclick,
editIndex = null,
}: Props = $props();

const displayName = $derived(fileName || (filePath ? filePath.split(/[/\\]/).pop() || filePath : ''));
Expand All @@ -68,6 +72,9 @@
e.stopPropagation();
if (!filePath) return;
revealFile(filePath);
if (editIndex !== null) {
requestAiScrollReveal(filePath, editIndex);
}
}

function handleRowClick() {
Expand Down
20 changes: 17 additions & 3 deletions frontend/components/files/FileNode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
onNodeDrop,
onNodeDragEnd,
dropTargetPath = null,
busyPaths = new Set<string>()
busyPaths = new Set<string>(),
aiChangesSet = new Set<string>()
}: {
file: FileNodeType;
isSelected?: boolean;
Expand All @@ -63,6 +64,7 @@
onNodeDragEnd?: (file: FileNodeType, event: DragEvent) => void;
dropTargetPath?: string | null;
busyPaths?: Set<string>;
aiChangesSet?: Set<string>;
} = $props();

const revealLabel = $derived(
Expand Down Expand Up @@ -119,6 +121,11 @@
gitStatusCode ? getGitStatusBadgeLabel(gitStatusCode) : ''
);

// AI changes indicator
const hasAiChanges = $derived(
file.type === 'file' && aiChangesSet.has(file.path)
);

let nodeElement: HTMLDivElement;
let menuButtonElement: HTMLButtonElement;
let menuStyle = $state('');
Expand Down Expand Up @@ -258,15 +265,21 @@
{file.name}
</span>

<!-- Status indicators (unsaved dot + git status letter) -->
{#if showModifiedIndicator || gitStatusCode}
<!-- Status indicators (unsaved dot + ai dot + git status letter) -->
{#if showModifiedIndicator || hasAiChanges || gitStatusCode}
<span class="flex items-center gap-1 flex-shrink-0 {isIgnored ? 'opacity-40' : ''}">
{#if showModifiedIndicator}
<span
class="w-1.5 h-1.5 rounded-full bg-amber-500 dark:bg-amber-600"
title="Unsaved changes"
></span>
{/if}
{#if hasAiChanges}
<span
class="w-1.5 h-1.5 rounded-full bg-violet-500 dark:bg-violet-400"
title="Has AI changes"
></span>
{/if}
{#if gitStatusCode}
<span
class="text-xs font-bold {gitStatusTextColor}"
Expand Down Expand Up @@ -472,6 +485,7 @@
{onNodeDragEnd}
{dropTargetPath}
{busyPaths}
{aiChangesSet}
/>
{/each}
{/if}
6 changes: 5 additions & 1 deletion frontend/components/files/FileTree.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
busyPaths?: Set<string>;
/** Root-level operation in progress (e.g. upload to project root). */
isRootBusy?: boolean;
/** File paths that have pending AI changes (for dot indicator). */
aiChangesSet?: Set<string>;
}

let {
Expand Down Expand Up @@ -100,7 +102,8 @@
onClearSelection,
isRootDropTarget = false,
busyPaths = new Set<string>(),
isRootBusy = false
isRootBusy = false,
aiChangesSet = new Set<string>()
}: Props = $props();

// Create local state if expandedFolders is not provided
Expand Down Expand Up @@ -839,6 +842,7 @@
{onNodeDragEnd}
{dropTargetPath}
{busyPaths}
{aiChangesSet}
/>
{/each}
</div>
Expand Down
Loading
Loading