diff --git a/src/games/storywriter/assets/editor.module.css b/src/games/storywriter/assets/editor.module.css
index 7728cde..aa5c147 100644
--- a/src/games/storywriter/assets/editor.module.css
+++ b/src/games/storywriter/assets/editor.module.css
@@ -75,6 +75,11 @@
word-break: break-word;
}
+.editable mark {
+ background: var(--bg-active);
+ color: inherit;
+}
+
.tab {
padding: 12px 16px;
background: transparent;
diff --git a/src/games/storywriter/components/editor.tsx b/src/games/storywriter/components/editor.tsx
index acbfbf9..ce4ecd9 100644
--- a/src/games/storywriter/components/editor.tsx
+++ b/src/games/storywriter/components/editor.tsx
@@ -50,9 +50,22 @@ export const Editor = () => {
if (contentRef.current) {
contentRef.current.scrollTop = contentRef.current.scrollHeight;
}
- }, [currentStory?.currentTab]);
+ }, [currentStory?.id, currentStory?.currentTab]);
+
+ const storyValue = useMemo(() => {
+ if (!currentStory) return '';
+
+ const { text, lastEditedText } = currentStory;
+ if (!lastEditedText) return highlight(text);
+
+ const idx = text.lastIndexOf(lastEditedText);
+ if (idx === -1) return highlight(text);
+
+ const marked = text.slice(0, idx) + '' + lastEditedText + '' + text.slice(idx + lastEditedText.length);
+
+ return highlight(marked);
+ }, [currentStory?.text, currentStory?.lastEditedText]);
- const storyValue = useMemo(() => currentStory ? highlight(currentStory.text) : '', [currentStory?.text]);
const promptPreview = useMemo(() => {
if (currentStory?.currentTab !== 'prompt') return '';
const text = Prompt.formatSystemPrompt(appState);
diff --git a/src/games/storywriter/contexts/state.tsx b/src/games/storywriter/contexts/state.tsx
index 97a15b0..6ac867a 100644
--- a/src/games/storywriter/contexts/state.tsx
+++ b/src/games/storywriter/contexts/state.tsx
@@ -74,6 +74,7 @@ export interface Story {
currentTab: Tab;
chatMessages: ChatMessage[];
chapters: Chapters.Chapter[];
+ lastEditedText?: string;
}
// ─── State ───────────────────────────────────────────────────────────────────
@@ -93,7 +94,7 @@ interface IState {
type Action =
| { type: 'CREATE_STORY'; title: string }
| { type: 'RENAME_STORY'; id: string; title: string }
- | { type: 'EDIT_STORY'; id: string; text: string }
+ | { type: 'EDIT_STORY'; id: string; text: string; highlightText?: string }
| { type: 'EDIT_SCRATCHPAD'; id: string; text: string }
| { type: 'ADD_LORE_ENTRY'; storyId: string; entry: LoreEntry }
| { type: 'EDIT_LORE_ENTRY'; storyId: string; entryId: string; updates: Partial }
@@ -190,7 +191,9 @@ function reducer(state: IState, action: Action): IState {
return {
...state,
stories: state.stories.map(s =>
- s.id === action.id ? { ...s, text: action.text } : s
+ s.id === action.id
+ ? { ...s, text: action.text, lastEditedText: action.highlightText }
+ : s
),
};
}
diff --git a/src/games/storywriter/utils/tools.ts b/src/games/storywriter/utils/tools.ts
index b6c702a..6e0cfd9 100644
--- a/src/games/storywriter/utils/tools.ts
+++ b/src/games/storywriter/utils/tools.ts
@@ -342,7 +342,7 @@ export namespace Tools {
const dispatchEdit = (text: string) => appState.dispatch(
isScratchpad
? { type: 'EDIT_SCRATCHPAD', id: appState.currentStory!.id, text }
- : { type: 'EDIT_STORY', id: appState.currentStory!.id, text }
+ : { type: 'EDIT_STORY', id: appState.currentStory!.id, text, highlightText: args.new_text }
);
// Append mode: when old_text is not provided, append new_text