mirror of
https://github.com/bal-spec/sillytavern-character-memory.git
synced 2026-04-28 03:39:44 +00:00
Complete rewrite of the UI and significant feature additions since v1.6.1. UX Redesign (v2.0): - Single-view dashboard replaces 4-tab sidebar - Settings, Prompts, Troubleshooter, Memory Manager moved to center-screen modals - Activity log in slide-out drawer - Setup Wizard for first-run configuration - Prompt version tracking with update notifications - Health indicator in stats bar Injection Viewer (v1.6–v2.1.6): - Per-message injection data: see exactly what memories, lorebook entries, and extension prompts were injected for any generation - Context/Prompt Breakdown with per-category token counts (System, Char card, Lorebook, Data Bank, Examples, Chat history) via ST Prompt Itemization - Stacked bar visualization, token hints in headers, Tips popup - Context overflow and heavy injection warnings Memory Management: - Unified block editor across all 5 editing surfaces (Memory Manager, Consolidation, Conversion, Reformat, Data Bank browser) - Find & Replace with highlighting across all editors - Undo support for all edit operations - Group chat character picker in Memory Manager Other features: - Tablet & phone display modes with touch-friendly controls - Topic-tagged memory format for better vector retrieval - Self-closing memory tag handling (GLM-4.7 compatibility) - Protect recent messages from extraction feedback loop - 9-point health check system with retrieve chunks and score threshold - Shared editor factory (editor.js), pure utility library (lib.js) - Vitest test suite: unit, snapshot, and live LLM tests - Full documentation suite in docs/ See CHANGELOG.md for detailed per-version notes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
125 lines
4.9 KiB
JavaScript
125 lines
4.9 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
|
import { createMemoryEditor } from '../../editor.js';
|
|
|
|
describe('createMemoryEditor', () => {
|
|
const sampleBlocks = [
|
|
{ chat: 'Test', date: '2026-01-01 14:00', bullets: ['- [A, B — test]', '- Fact one', '- Fact two'] },
|
|
{ chat: 'Other', date: '2026-01-02 10:00', bullets: ['- [C — other]', '- Something'] },
|
|
];
|
|
|
|
it('initializes with provided blocks', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
expect(editor.getBlocks()).toHaveLength(2);
|
|
expect(editor.getBlocks()[0].chat).toBe('Test');
|
|
});
|
|
|
|
it('getBlocks returns a deep clone, not a reference', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
const blocks = editor.getBlocks();
|
|
blocks[0].bullets.push('- New');
|
|
expect(editor.getBlocks()[0].bullets).toHaveLength(3);
|
|
});
|
|
|
|
it('deleteBullet removes the specified bullet', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
editor.deleteBullet(0, 1);
|
|
expect(editor.getBlocks()[0].bullets).toEqual(['- [A, B — test]', '- Fact two']);
|
|
});
|
|
|
|
it('deleteBlock removes the specified block', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
editor.deleteBlock(0);
|
|
expect(editor.getBlocks()).toHaveLength(1);
|
|
expect(editor.getBlocks()[0].chat).toBe('Other');
|
|
});
|
|
|
|
it('addBullet appends to the specified block', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
editor.addBullet(0);
|
|
const bullets = editor.getBlocks()[0].bullets;
|
|
expect(bullets[bullets.length - 1]).toBe('');
|
|
});
|
|
|
|
it('addBlock appends a new empty block', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
editor.addBlock();
|
|
const blocks = editor.getBlocks();
|
|
expect(blocks).toHaveLength(3);
|
|
expect(blocks[2].bullets).toEqual(['']);
|
|
});
|
|
|
|
it('addBlock uses provided timestamp', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
editor.addBlock('2026-03-01 12:00');
|
|
expect(editor.getBlocks()[2].date).toBe('2026-03-01 12:00');
|
|
});
|
|
|
|
it('updateBullet changes the text', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
editor.updateBullet(0, 1, '- Updated fact');
|
|
expect(editor.getBlocks()[0].bullets[1]).toBe('- Updated fact');
|
|
});
|
|
|
|
it('updateTheme changes the chat label', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
editor.updateTheme(0, 'New Label');
|
|
expect(editor.getBlocks()[0].chat).toBe('New Label');
|
|
});
|
|
|
|
it('undo restores previous state', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
editor.deleteBlock(0);
|
|
expect(editor.getBlocks()).toHaveLength(1);
|
|
editor.undo();
|
|
expect(editor.getBlocks()).toHaveLength(2);
|
|
});
|
|
|
|
it('undo returns false when no history', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
expect(editor.undo()).toBe(false);
|
|
});
|
|
|
|
it('canUndo reflects version stack state', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
expect(editor.canUndo()).toBe(false);
|
|
editor.deleteBlock(0);
|
|
expect(editor.canUndo()).toBe(true);
|
|
});
|
|
|
|
it('replaceAll swaps all blocks and clears undo history', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
editor.deleteBlock(0);
|
|
const newBlocks = [{ chat: 'Fresh', date: '2026-06-01 08:00', bullets: ['- New'] }];
|
|
editor.replaceAll(newBlocks);
|
|
expect(editor.getBlocks()).toHaveLength(1);
|
|
expect(editor.getBlocks()[0].chat).toBe('Fresh');
|
|
expect(editor.canUndo()).toBe(false);
|
|
});
|
|
|
|
it('deleteBullet auto-removes block when last bullet deleted', () => {
|
|
const editor = createMemoryEditor({ blocks: [
|
|
{ chat: 'Solo', date: '2026-01-01 10:00', bullets: ['- Only one'] },
|
|
{ chat: 'Other', date: '2026-01-02 10:00', bullets: ['- Keep'] },
|
|
] });
|
|
editor.deleteBullet(0, 0);
|
|
expect(editor.getBlocks()).toHaveLength(1);
|
|
expect(editor.getBlocks()[0].chat).toBe('Other');
|
|
});
|
|
|
|
it('getEditingSet returns a copy, not a reference', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
editor.toggleEdit(0);
|
|
const set = editor.getEditingSet();
|
|
set.delete(0);
|
|
expect(editor.isEditing(0)).toBe(true);
|
|
});
|
|
|
|
it('toggleEdit tracks which blocks are in edit mode', () => {
|
|
const editor = createMemoryEditor({ blocks: sampleBlocks });
|
|
expect(editor.isEditing(0)).toBe(false);
|
|
editor.toggleEdit(0);
|
|
expect(editor.isEditing(0)).toBe(true);
|
|
editor.toggleEdit(0);
|
|
expect(editor.isEditing(0)).toBe(false);
|
|
});
|
|
});
|