managed sessions only. allow for rename/delete

This commit is contained in:
2026-01-19 19:34:58 -05:00
parent e2048d8b69
commit 313ac44337
32 changed files with 1759 additions and 331 deletions
+102 -7
View File
@@ -1,13 +1,20 @@
<script lang="ts">
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import { onMount, onDestroy } from 'svelte';
import { onMount, onDestroy, tick } from 'svelte';
import { activeSession } from '$lib/stores/sessions';
import MessageList from '$lib/components/MessageList.svelte';
import InputBar from '$lib/components/InputBar.svelte';
import PermissionRequest from '$lib/components/PermissionRequest.svelte';
$: sessionId = $page.params.id;
let inputBar: InputBar;
let steerMode = false;
let isEditingTitle = false;
let editedTitle = '';
let titleInput: HTMLInputElement;
onMount(() => {
if (sessionId) {
activeSession.load(sessionId);
@@ -19,13 +26,64 @@
});
function handleSend(event: CustomEvent<string>) {
activeSession.sendMessage(event.detail);
if (steerMode && $activeSession.pendingPermission) {
// Send as steer response
activeSession.respondToPermission('steer', event.detail);
steerMode = false;
} else {
activeSession.sendMessage(event.detail);
}
}
function handlePermissionAccept() {
activeSession.respondToPermission('accept');
}
function handlePermissionDeny() {
activeSession.respondToPermission('deny');
}
function handlePermissionSteer() {
steerMode = true;
// Focus the input bar
inputBar?.focus();
}
function goBack() {
goto('/');
}
async function startEditingTitle() {
if (!session) return;
editedTitle = session.title || '';
isEditingTitle = true;
await tick();
titleInput?.focus();
titleInput?.select();
}
async function saveTitle() {
if (!session || !isEditingTitle) return;
const newTitle = editedTitle.trim();
isEditingTitle = false;
if (newTitle !== (session.title || '')) {
await activeSession.rename(newTitle);
}
}
function cancelEditTitle() {
isEditingTitle = false;
editedTitle = '';
}
function handleTitleKeydown(event: KeyboardEvent) {
if (event.key === 'Enter') {
saveTitle();
} else if (event.key === 'Escape') {
cancelEditTitle();
}
}
$: session = $activeSession.session;
$: externalId = session?.['external-id'] || session?.externalId || '';
$: workingDir = session?.['working-dir'] || session?.workingDir || '';
@@ -66,9 +124,24 @@
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<span class="w-2 h-2 rounded-full {statusColors[session.status] || statusColors.idle}"></span>
<h1 class="font-semibold truncate">
{session.title || `Session ${shortId}`}
</h1>
{#if isEditingTitle}
<input
bind:this={titleInput}
bind:value={editedTitle}
on:blur={saveTitle}
on:keydown={handleTitleKeydown}
class="font-semibold bg-zinc-800 border border-zinc-600 rounded px-2 py-0.5 text-zinc-100 focus:outline-none focus:border-spice-500 w-full max-w-[200px]"
placeholder="Session name"
/>
{:else}
<button
on:click={startEditingTitle}
class="font-semibold truncate text-left hover:text-spice-400 transition-colors"
title="Click to rename"
>
{session.title || `Session ${shortId}`}
</button>
{/if}
</div>
{#if projectName}
<p class="text-xs text-zinc-500 truncate">{projectName}</p>
@@ -107,11 +180,33 @@
</svg>
</div>
{:else}
<MessageList messages={$activeSession.messages} streamingContent={$activeSession.streamingContent} />
{#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">
<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>
</div>
{/if}
<MessageList messages={$activeSession.messages} streamingContent={$activeSession.streamingContent} isThinking={$activeSession.isThinking} />
{#if $activeSession.pendingPermission}
<PermissionRequest
permission={$activeSession.pendingPermission}
on:accept={handlePermissionAccept}
on:deny={handlePermissionDeny}
on:steer={handlePermissionSteer}
/>
{/if}
<InputBar
bind:this={inputBar}
on:send={handleSend}
disabled={session?.status === 'running' && $activeSession.streamingContent !== ''}
placeholder={session?.status === 'running' ? 'Waiting for response...' : 'Type a message...'}
placeholder={steerMode
? 'Tell Claude what to do instead...'
: session?.status === 'running'
? 'Waiting for response...'
: 'Type a message...'}
/>
{/if}