go to laast session
This commit is contained in:
@@ -43,3 +43,6 @@ client/dev-dist/
|
|||||||
|
|
||||||
# Test results
|
# Test results
|
||||||
e2e/test-results/
|
e2e/test-results/
|
||||||
|
|
||||||
|
# Ideas/notes
|
||||||
|
ideas.md
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# Spiceflow
|
# Spiceflow
|
||||||
|
|
||||||
|
The ~~spice~~ *tokens* must flow.
|
||||||
|
|
||||||
A mobile-friendly web app for controlling AI coding assistants (Claude Code, OpenCode) remotely.
|
A mobile-friendly web app for controlling AI coding assistants (Claude Code, OpenCode) remotely.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
export let autoScroll: boolean = true;
|
export let autoScroll: boolean = true;
|
||||||
export let provider: 'claude' | 'opencode' | 'tmux' = 'claude';
|
export let provider: 'claude' | 'opencode' | 'tmux' = 'claude';
|
||||||
export let showBigMode: boolean = false;
|
export let showBigMode: boolean = false;
|
||||||
|
export let lastSessionId: string | null = null;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
toggleAutoAccept: boolean;
|
toggleAutoAccept: boolean;
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
refresh: void;
|
refresh: void;
|
||||||
eject: void;
|
eject: void;
|
||||||
bigMode: void;
|
bigMode: void;
|
||||||
|
goToLastSession: void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function handleToggleAutoScroll() {
|
function handleToggleAutoScroll() {
|
||||||
@@ -54,6 +56,11 @@
|
|||||||
open = false;
|
open = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleGoToLastSession() {
|
||||||
|
dispatch('goToLastSession');
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
|
||||||
function handleClickOutside(event: MouseEvent) {
|
function handleClickOutside(event: MouseEvent) {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
if (!target.closest('.settings-dropdown')) {
|
if (!target.closest('.settings-dropdown')) {
|
||||||
@@ -140,6 +147,19 @@
|
|||||||
<span class="text-sm text-zinc-200">Refresh</span>
|
<span class="text-sm text-zinc-200">Refresh</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<!-- Go to last session -->
|
||||||
|
{#if lastSessionId}
|
||||||
|
<button
|
||||||
|
on:click={handleGoToLastSession}
|
||||||
|
class="w-full flex items-center gap-3 px-3 py-2.5 hover:bg-zinc-700/50 transition-colors text-left"
|
||||||
|
>
|
||||||
|
<svg class="h-4 w-4 text-zinc-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
|
||||||
|
</svg>
|
||||||
|
<span class="text-sm text-zinc-200">Last session</span>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if provider !== 'tmux'}
|
{#if provider !== 'tmux'}
|
||||||
<button
|
<button
|
||||||
on:click={handleCondenseAll}
|
on:click={handleCondenseAll}
|
||||||
|
|||||||
@@ -6,8 +6,9 @@
|
|||||||
export let sessionId: string;
|
export let sessionId: string;
|
||||||
export let autoScroll: boolean = true;
|
export let autoScroll: boolean = true;
|
||||||
export let autoFocus: boolean = true;
|
export let autoFocus: boolean = true;
|
||||||
|
export let lastSessionId: string | null = null;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ aliveChange: boolean }>();
|
const dispatch = createEventDispatcher<{ aliveChange: boolean; goToLastSession: void }>();
|
||||||
|
|
||||||
// ANSI to HTML converter for terminal colors
|
// ANSI to HTML converter for terminal colors
|
||||||
const ansiConverter = new AnsiToHtml({
|
const ansiConverter = new AnsiToHtml({
|
||||||
@@ -588,6 +589,19 @@
|
|||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
{#if lastSessionId}
|
||||||
|
<span class="w-px h-6 bg-zinc-700"></span>
|
||||||
|
<button
|
||||||
|
on:click={() => dispatch('goToLastSession')}
|
||||||
|
class="{btnBase} bg-spice-600/80 hover:bg-spice-600 text-white"
|
||||||
|
aria-label="Go to last session"
|
||||||
|
title="Go to last session"
|
||||||
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/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="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Hidden input for keyboard capture (invisible but functional for mobile) -->
|
<!-- Hidden input for keyboard capture (invisible but functional for mobile) -->
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
let autoScroll = true;
|
let autoScroll = true;
|
||||||
let tmuxAlive = false;
|
let tmuxAlive = false;
|
||||||
let isMobile = false;
|
let isMobile = false;
|
||||||
|
let lastSessionId: string | null = null;
|
||||||
|
|
||||||
// Load auto-scroll preference from localStorage and detect mobile
|
// Load auto-scroll preference from localStorage and detect mobile
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@@ -32,6 +33,8 @@
|
|||||||
if (stored !== null) {
|
if (stored !== null) {
|
||||||
autoScroll = stored === 'true';
|
autoScroll = stored === 'true';
|
||||||
}
|
}
|
||||||
|
// Load last session from localStorage
|
||||||
|
lastSessionId = localStorage.getItem('spiceflow-last-session');
|
||||||
// Detect mobile (screen width < 640px or height < 450px)
|
// Detect mobile (screen width < 640px or height < 450px)
|
||||||
const checkMobile = () => {
|
const checkMobile = () => {
|
||||||
isMobile = window.innerWidth < 640 || window.innerHeight < 450;
|
isMobile = window.innerWidth < 640 || window.innerHeight < 450;
|
||||||
@@ -42,11 +45,28 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Track session history when navigating to a new session
|
||||||
|
let previousSessionId: string | null = null;
|
||||||
|
$: if (browser && sessionId && sessionId !== previousSessionId) {
|
||||||
|
// Save the previous session as "last session" before switching
|
||||||
|
if (previousSessionId) {
|
||||||
|
localStorage.setItem('spiceflow-last-session', previousSessionId);
|
||||||
|
lastSessionId = previousSessionId;
|
||||||
|
}
|
||||||
|
previousSessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
// Load session when sessionId changes (handles client-side navigation)
|
// Load session when sessionId changes (handles client-side navigation)
|
||||||
$: if (sessionId) {
|
$: if (sessionId) {
|
||||||
activeSession.load(sessionId);
|
activeSession.load(sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goToLastSession() {
|
||||||
|
if (lastSessionId) {
|
||||||
|
goto(`/session/${lastSessionId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleToggleAutoScroll(event: CustomEvent<boolean>) {
|
function handleToggleAutoScroll(event: CustomEvent<boolean>) {
|
||||||
autoScroll = event.detail;
|
autoScroll = event.detail;
|
||||||
if (browser) {
|
if (browser) {
|
||||||
@@ -236,6 +256,7 @@
|
|||||||
<SessionSettings
|
<SessionSettings
|
||||||
{autoAcceptEdits}
|
{autoAcceptEdits}
|
||||||
{autoScroll}
|
{autoScroll}
|
||||||
|
{lastSessionId}
|
||||||
provider={session.provider}
|
provider={session.provider}
|
||||||
showBigMode={isTmuxSession && isMobile}
|
showBigMode={isTmuxSession && isMobile}
|
||||||
on:toggleAutoAccept={handleToggleAutoAccept}
|
on:toggleAutoAccept={handleToggleAutoAccept}
|
||||||
@@ -244,6 +265,7 @@
|
|||||||
on:refresh={handleRefresh}
|
on:refresh={handleRefresh}
|
||||||
on:eject={handleEject}
|
on:eject={handleEject}
|
||||||
on:bigMode={handleBigMode}
|
on:bigMode={handleBigMode}
|
||||||
|
on:goToLastSession={goToLastSession}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Refresh button - desktop only -->
|
<!-- Refresh button - desktop only -->
|
||||||
@@ -326,6 +348,17 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Refresh
|
Refresh
|
||||||
</button>
|
</button>
|
||||||
|
{#if lastSessionId}
|
||||||
|
<button
|
||||||
|
on:click={() => { menuOpen = false; goToLastSession(); }}
|
||||||
|
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="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
|
||||||
|
</svg>
|
||||||
|
Last session
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
{#if !isTmuxSession}
|
{#if !isTmuxSession}
|
||||||
<button
|
<button
|
||||||
on:click={() => { menuOpen = false; messageList?.condenseAll(); }}
|
on:click={() => { menuOpen = false; messageList?.condenseAll(); }}
|
||||||
@@ -385,7 +418,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{:else if isTmuxSession}
|
{:else if isTmuxSession}
|
||||||
<!-- Terminal view for tmux sessions -->
|
<!-- Terminal view for tmux sessions -->
|
||||||
<TerminalView bind:this={terminalView} sessionId={sessionId || ''} {autoScroll} on:aliveChange={handleTmuxAliveChange} />
|
<TerminalView bind:this={terminalView} sessionId={sessionId || ''} {autoScroll} {lastSessionId} on:aliveChange={handleTmuxAliveChange} on:goToLastSession={goToLastSession} />
|
||||||
{:else}
|
{:else}
|
||||||
<MessageList bind:this={messageList} messages={$activeSession.messages} streamingContent={$activeSession.streamingContent} isThinking={$activeSession.isThinking} provider={session?.provider} {autoScroll} />
|
<MessageList bind:this={messageList} messages={$activeSession.messages} streamingContent={$activeSession.streamingContent} isThinking={$activeSession.isThinking} provider={session?.provider} {autoScroll} />
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user