Add last undo/redo for notebook page

This commit is contained in:
Leszek Hanusz 2026-02-02 22:39:07 +01:00
parent f041a864ed
commit 7892b259cb
3 changed files with 70 additions and 2 deletions

Binary file not shown.

View File

@ -2,7 +2,7 @@
import { notebookStore } from '$lib/stores/notebook.svelte';
import Button from '$lib/components/ui/button/button.svelte';
import Textarea from '$lib/components/ui/textarea/textarea.svelte';
import { Play, Square, Settings } from '@lucide/svelte';
import { Play, Square, Settings, Undo, Redo } from '@lucide/svelte';
import { config } from '$lib/stores/settings.svelte';
import DialogChatSettings from '$lib/components/app/dialogs/DialogChatSettings.svelte';
import { ModelsSelector, ChatMessageStatistics, DialogChatError } from '$lib/components/app';
@ -35,6 +35,8 @@
let isRouter = $derived(isRouterMode());
let errorDialog = $derived(notebookStore.error);
let canUndo = $derived(notebookStore.previousContent !== null && !notebookStore.isGenerating);
let canRedo = $derived(notebookStore.undoneContent !== null && !notebookStore.isGenerating);
// Sync local input with store content
$effect(() => {
@ -44,6 +46,7 @@
function handleInput(e: Event) {
const target = e.target as HTMLTextAreaElement;
notebookStore.content = target.value;
notebookStore.resetUndoRedo();
}
function handleErrorDialogOpenChange(open: boolean) {
@ -65,6 +68,13 @@
await notebookStore.generate(notebookModel);
}
function handleUndo() {
notebookStore.undo();
}
function handleRedo() {
notebookStore.redo();
}
function handleStop() {
notebookStore.stop();
@ -223,13 +233,45 @@
<div class="bg-background p-2 md:p-4">
<div class="flex flex-col-reverse gap-4 md:flex-row md:items-center md:justify-between">
<div class="flex items-center gap-2">
<Tooltip.Root>
<Tooltip.Trigger>
<Button
variant="ghost"
size="icon"
disabled={!canUndo}
onclick={handleUndo}
>
<Undo class="h-4 w-4" />
</Button>
</Tooltip.Trigger>
<Tooltip.Content>
<p>Undo last generation</p>
</Tooltip.Content>
</Tooltip.Root>
<Tooltip.Root>
<Tooltip.Trigger>
<Button
variant="ghost"
size="icon"
disabled={!canRedo}
onclick={handleRedo}
>
<Redo class="h-4 w-4" />
</Button>
</Tooltip.Trigger>
<Tooltip.Content>
<p>Redo last generation</p>
</Tooltip.Content>
</Tooltip.Root>
{#snippet generateButton(props = {})}
<Button
disabled={isDisabled}
onclick={notebookStore.isGenerating ? handleStop : handleGenerate}
size="sm"
variant={notebookStore.isGenerating ? 'destructive' : 'default'}
class="gap-2"
class="gap-2 min-w-[120px]"
>
{#if notebookStore.isGenerating}
<Square class="h-4 w-4 fill-current" />

View File

@ -18,9 +18,14 @@ export class NotebookStore {
contextInfo?: { n_prompt_tokens: number; n_ctx: number };
} | null>(null);
previousContent = $state<string | null>(null);
undoneContent = $state<string | null>(null);
async generate(model?: string) {
if (this.isGenerating) return;
this.previousContent = this.content;
this.undoneContent = null;
this.isGenerating = true;
this.abortController = new AbortController();
this.error = null;
@ -90,6 +95,27 @@ export class NotebookStore {
this.error = null;
}
undo() {
if (this.previousContent !== null) {
this.undoneContent = this.content;
this.content = this.previousContent;
this.previousContent = null;
}
}
redo() {
if (this.undoneContent !== null) {
this.previousContent = this.content;
this.content = this.undoneContent;
this.undoneContent = null;
}
}
resetUndoRedo() {
this.previousContent = null;
this.undoneContent = null;
}
stop() {
if (this.abortController) {
this.abortController.abort();