add git diffs and permission support

This commit is contained in:
2026-01-19 23:45:03 -05:00
parent 313ac44337
commit 61a2e9b8af
44 changed files with 2051 additions and 267 deletions
+93 -9
View File
@@ -6,14 +6,17 @@
import MessageList from '$lib/components/MessageList.svelte';
import InputBar from '$lib/components/InputBar.svelte';
import PermissionRequest from '$lib/components/PermissionRequest.svelte';
import SessionSettings from '$lib/components/SessionSettings.svelte';
$: sessionId = $page.params.id;
let inputBar: InputBar;
let messageList: MessageList;
let steerMode = false;
let isEditingTitle = false;
let editedTitle = '';
let titleInput: HTMLInputElement;
let menuOpen = false;
onMount(() => {
if (sessionId) {
@@ -90,11 +93,17 @@
$: shortId = externalId ? externalId.slice(0, 8) : session?.id?.slice(0, 8) || '';
$: projectName = workingDir.split('/').pop() || '';
$: isNewSession = !externalId && $activeSession.messages.length === 0;
$: assistantName = session?.provider === 'opencode' ? 'OpenCode' : 'Claude';
$: autoAcceptEdits = session?.['auto-accept-edits'] || session?.autoAcceptEdits || false;
function handleToggleAutoAccept(event: CustomEvent<boolean>) {
activeSession.setAutoAcceptEdits(event.detail);
}
const statusColors: Record<string, string> = {
idle: 'bg-zinc-600',
running: 'bg-green-500 animate-pulse',
completed: 'bg-blue-500'
processing: 'bg-green-500 animate-pulse',
'awaiting-permission': 'bg-amber-500 animate-pulse'
};
</script>
@@ -102,8 +111,10 @@
<title>{session?.title || `Session ${shortId}`} - Spiceflow</title>
</svelte:head>
<!-- Header -->
<header class="flex-shrink-0 border-b border-zinc-800 px-4 py-3">
<svelte:window on:click={() => (menuOpen = false)} />
<!-- Header - Full (portrait) -->
<header class="flex-shrink-0 border-b border-zinc-800 px-4 py-3 landscape-mobile:hidden">
<div class="flex items-center gap-3">
<button
on:click={goBack}
@@ -148,6 +159,12 @@
{/if}
</div>
<SessionSettings
{autoAcceptEdits}
provider={session.provider}
on:toggleAutoAccept={handleToggleAutoAccept}
/>
<span
class="text-xs font-medium uppercase {session.provider === 'claude'
? 'text-spice-400'
@@ -159,6 +176,65 @@
</div>
</header>
<!-- Header - Collapsed (landscape mobile) -->
<div class="landscape-menu fixed top-2 right-2 z-50">
<button
on:click|stopPropagation={() => (menuOpen = !menuOpen)}
class="p-2 bg-zinc-800 hover:bg-zinc-700 rounded-lg border border-zinc-700 transition-colors"
aria-label="Menu"
>
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
{#if menuOpen}
<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions -->
<div on:click|stopPropagation class="absolute right-0 top-full mt-1 bg-zinc-800 border border-zinc-700 rounded-lg shadow-xl min-w-[200px] overflow-hidden">
{#if session}
<div class="px-3 py-2 border-b border-zinc-700">
<div class="flex items-center gap-2">
<span class="w-2 h-2 rounded-full {statusColors[session.status] || statusColors.idle}"></span>
<span class="font-semibold truncate">{session.title || `Session ${shortId}`}</span>
</div>
{#if projectName}
<p class="text-xs text-zinc-500 truncate mt-0.5">{projectName}</p>
{/if}
</div>
{/if}
<button
on:click={() => { menuOpen = false; goBack(); }}
class="w-full px-3 py-2 text-left text-sm hover:bg-zinc-700 flex items-center gap-2 transition-colors"
>
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
Back to sessions
</button>
<button
on:click={() => { menuOpen = false; messageList?.condenseAll(); }}
class="w-full px-3 py-2 text-left text-sm hover:bg-zinc-700 flex items-center gap-2 transition-colors"
>
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
</svg>
Condense all
</button>
{#if session?.provider === 'claude'}
<label class="w-full px-3 py-2 text-left text-sm hover:bg-zinc-700 flex items-center gap-2 transition-colors cursor-pointer border-t border-zinc-700">
<input
type="checkbox"
checked={autoAcceptEdits}
on:change={() => activeSession.setAutoAcceptEdits(!autoAcceptEdits)}
class="h-4 w-4 rounded border-zinc-600 bg-zinc-700 text-spice-500 focus:ring-spice-500 focus:ring-offset-0"
/>
<span>Auto-accept edits</span>
</label>
{/if}
</div>
{/if}
</div>
<!-- Content -->
{#if $activeSession.error}
<div class="flex-1 flex items-center justify-center p-4">
@@ -181,18 +257,26 @@
</div>
{:else}
{#if workingDir}
<div class="flex-shrink-0 px-4 py-2 bg-zinc-900/50 border-b border-zinc-800 flex items-center gap-2 text-xs text-zinc-500">
<div class="flex-shrink-0 px-4 py-2 bg-zinc-900/50 border-b border-zinc-800 flex items-center gap-2 text-xs text-zinc-500 landscape-mobile:hidden">
<svg class="h-3.5 w-3.5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
</svg>
<span class="truncate font-mono">{workingDir}</span>
<span class="flex-1"></span>
<button
on:click={() => messageList?.condenseAll()}
class="text-zinc-400 hover:text-zinc-200 transition-colors whitespace-nowrap"
>
condense all
</button>
</div>
{/if}
<MessageList messages={$activeSession.messages} streamingContent={$activeSession.streamingContent} isThinking={$activeSession.isThinking} />
<MessageList bind:this={messageList} messages={$activeSession.messages} streamingContent={$activeSession.streamingContent} isThinking={$activeSession.isThinking} />
{#if $activeSession.pendingPermission}
<PermissionRequest
permission={$activeSession.pendingPermission}
{assistantName}
on:accept={handlePermissionAccept}
on:deny={handlePermissionDeny}
on:steer={handlePermissionSteer}
@@ -202,10 +286,10 @@
<InputBar
bind:this={inputBar}
on:send={handleSend}
disabled={session?.status === 'running' && $activeSession.streamingContent !== ''}
disabled={session?.status === 'processing' && $activeSession.streamingContent !== ''}
placeholder={steerMode
? 'Tell Claude what to do instead...'
: session?.status === 'running'
? `Tell ${assistantName} what to do instead...`
: session?.status === 'processing'
? 'Waiting for response...'
: 'Type a message...'}
/>