Use state and store

This commit is contained in:
Alexopus
2025-07-18 16:50:31 +02:00
parent be763c6504
commit 30b408533f
2 changed files with 172 additions and 68 deletions
+2 -1
View File
@@ -1 +1,2 @@
__pycache__
__pycache__
notes/
+170 -67
View File
@@ -6,19 +6,143 @@ link.rel = 'stylesheet';
link.href = '/extensions/comfyui-notes-sidebar/notes-sidebar.css';
document.head.appendChild(link);
class NotesStore extends EventTarget {
constructor() {
super();
this.state = {
notes: [],
activeNote: null,
unsavedChanges: new Map(), // noteName -> boolean
noteContent: new Map(), // noteName -> string (current content)
savedContent: new Map() // noteName -> string (last saved content)
};
}
getState() {
return { ...this.state };
}
setState(updates) {
const prevState = { ...this.state };
this.state = { ...this.state, ...updates };
// Emit state change event
this.dispatchEvent(new CustomEvent('stateChanged', {
detail: {
prevState,
newState: this.getState(),
updates
}
}));
}
// Convenience methods for common state operations
setNotes(notes) {
this.setState({ notes });
}
setActiveNote(activeNote) {
this.setState({ activeNote });
}
setNoteContent(noteName, content) {
const noteContent = new Map(this.state.noteContent);
noteContent.set(noteName, content);
this.setState({ noteContent });
}
setSavedContent(noteName, content) {
const savedContent = new Map(this.state.savedContent);
savedContent.set(noteName, content);
this.setState({ savedContent });
}
setUnsavedChanges(noteName, hasChanges) {
const unsavedChanges = new Map(this.state.unsavedChanges);
unsavedChanges.set(noteName, hasChanges);
this.setState({ unsavedChanges });
}
deleteNoteFromState(noteName) {
const unsavedChanges = new Map(this.state.unsavedChanges);
const noteContent = new Map(this.state.noteContent);
const savedContent = new Map(this.state.savedContent);
unsavedChanges.delete(noteName);
noteContent.delete(noteName);
savedContent.delete(noteName);
this.setState({ unsavedChanges, noteContent, savedContent });
}
renameNoteInState(oldName, newName) {
const unsavedChanges = new Map(this.state.unsavedChanges);
const noteContent = new Map(this.state.noteContent);
const savedContent = new Map(this.state.savedContent);
// Transfer state from old name to new name
const unsavedState = unsavedChanges.get(oldName) || false;
const content = noteContent.get(oldName) || '';
const saved = savedContent.get(oldName) || '';
// Remove old
unsavedChanges.delete(oldName);
noteContent.delete(oldName);
savedContent.delete(oldName);
// Add new
unsavedChanges.set(newName, unsavedState);
noteContent.set(newName, content);
savedContent.set(newName, saved);
this.setState({ unsavedChanges, noteContent, savedContent });
}
}
class NotesSidebar {
constructor() {
this.notes = [];
this.activeNote = null;
this.store = new NotesStore();
this.tabsContainer = null;
this.contentArea = null;
this.textArea = null;
this.isInitialized = false;
this.sidebarElement = null;
// Track unsaved changes and content for each note
this.unsavedChanges = new Map(); // noteName -> boolean
this.noteContent = new Map(); // noteName -> string (current content)
this.savedContent = new Map(); // noteName -> string (last saved content)
// Listen to state changes
this.store.addEventListener('stateChanged', (event) => {
this.onStateChanged(event.detail);
});
}
onStateChanged(changeDetails) {
const { prevState, newState, updates } = changeDetails;
// Update tabs if notes changed, active note changed, or unsaved changes changed
if (updates.notes || updates.activeNote || updates.unsavedChanges) {
this.renderTabs();
}
// Update textarea if active note changed or note content changed
if (updates.activeNote || updates.noteContent) {
this.updateTextArea();
}
}
get notes() { return this.store.state.notes; }
get activeNote() { return this.store.state.activeNote; }
get unsavedChanges() { return this.store.state.unsavedChanges; }
get noteContent() { return this.store.state.noteContent; }
get savedContent() { return this.store.state.savedContent; }
updateTextArea() {
if (!this.textArea || !this.activeNote) return;
// Check if we have unsaved content for this note
if (this.noteContent.has(this.activeNote)) {
this.textArea.value = this.noteContent.get(this.activeNote);
} else {
this.textArea.value = '';
}
}
async init() {
@@ -35,10 +159,9 @@ class NotesSidebar {
if (this.isInitialized && this.sidebarElement) {
el.innerHTML = '';
el.appendChild(this.sidebarElement);
// Refresh the active note content when reopening
if (this.activeNote) {
this.loadNoteContent(this.activeNote);
}
// Trigger reactive updates when reopening
this.renderTabs();
this.updateTextArea();
return;
}
@@ -127,15 +250,15 @@ class NotesSidebar {
try {
const response = await fetch('/notes_sidebar/list');
const data = await response.json();
this.notes = data.notes || [];
const notes = data.notes || [];
if (this.notes.length > 0 && !this.activeNote) {
this.activeNote = this.notes[0];
this.store.setNotes(notes);
if (notes.length > 0 && !this.activeNote) {
this.store.setActiveNote(notes[0]);
}
this.renderTabs();
// Load content for active note after rendering tabs
// Load content for active note after setting state
if (this.activeNote) {
await this.loadNoteContent(this.activeNote);
}
@@ -145,6 +268,8 @@ class NotesSidebar {
}
renderTabs() {
if (!this.tabsContainer) return;
const existingTabs = this.tabsContainer.querySelectorAll('.note-tab');
existingTabs.forEach(tab => tab.remove());
@@ -162,33 +287,28 @@ class NotesSidebar {
async selectNote(noteName) {
// Save current content before switching
if (this.activeNote && this.textArea) {
this.noteContent.set(this.activeNote, this.textArea.value);
this.store.setNoteContent(this.activeNote, this.textArea.value);
}
this.activeNote = noteName;
this.store.setActiveNote(noteName);
await this.loadNoteContent(noteName);
this.renderTabs();
}
async loadNoteContent(noteName) {
try {
// Check if we have unsaved content for this note
if (this.noteContent.has(noteName)) {
// Use the unsaved content
if (this.textArea) {
this.textArea.value = this.noteContent.get(noteName);
}
// Content is already in state, updateTextArea will handle it
return;
} else {
// Load from server
const response = await fetch(`/notes_sidebar/note/${encodeURIComponent(noteName)}`);
const data = await response.json();
const content = data.content || '';
if (this.textArea) {
this.textArea.value = content;
}
// Store both current and saved content
this.noteContent.set(noteName, content);
this.savedContent.set(noteName, content);
this.store.setNoteContent(noteName, content);
this.store.setSavedContent(noteName, content);
}
} catch (error) {
console.error('Error loading note content:', error);
@@ -209,10 +329,9 @@ class NotesSidebar {
if (response.ok) {
console.log('Note saved successfully');
// Mark as saved
this.savedContent.set(this.activeNote, content);
this.noteContent.set(this.activeNote, content);
this.unsavedChanges.set(this.activeNote, false);
this.renderTabs(); // Update tab display
this.store.setSavedContent(this.activeNote, content);
this.store.setNoteContent(this.activeNote, content);
this.store.setUnsavedChanges(this.activeNote, false);
} else {
console.error('Error saving note');
}
@@ -234,17 +353,15 @@ class NotesSidebar {
if (response.ok) {
// Clean up tracking for deleted note
const deletedNote = this.activeNote;
this.unsavedChanges.delete(deletedNote);
this.noteContent.delete(deletedNote);
this.savedContent.delete(deletedNote);
this.store.deleteNoteFromState(deletedNote);
await this.refreshNotes();
// Select the first available note or clear if no notes left
this.activeNote = this.notes.length > 0 ? this.notes[0] : null;
if (this.activeNote) {
await this.loadNoteContent(this.activeNote);
} else {
this.textArea.value = '';
const newActiveNote = this.notes.length > 0 ? this.notes[0] : null;
this.store.setActiveNote(newActiveNote);
if (newActiveNote) {
await this.loadNoteContent(newActiveNote);
}
}
} catch (error) {
@@ -279,25 +396,17 @@ class NotesSidebar {
// Delete old note
await fetch(`/notes_sidebar/note/${encodeURIComponent(this.activeNote)}`, { method: 'DELETE' });
// Update tracking maps
// Update tracking maps through store
const oldName = this.activeNote;
const unsavedState = this.unsavedChanges.get(oldName) || false;
const content = this.noteContent.get(oldName) || currentContent;
this.store.renameNoteInState(oldName, newName);
// Remove old tracking
this.unsavedChanges.delete(oldName);
this.noteContent.delete(oldName);
this.savedContent.delete(oldName);
// Add new tracking
this.noteContent.set(newName, content);
this.savedContent.set(newName, currentContent); // The content we just saved
this.unsavedChanges.set(newName, false); // Just saved, so no unsaved changes
// Set saved content to what we just saved
this.store.setSavedContent(newName, currentContent);
this.store.setUnsavedChanges(newName, false);
// Update active note and refresh
this.activeNote = newName;
this.store.setActiveNote(newName);
await this.refreshNotes();
this.renderTabs();
}
} catch (error) {
console.error('Error renaming note:', error);
@@ -317,15 +426,12 @@ class NotesSidebar {
if (response.ok) {
await this.refreshNotes();
this.activeNote = noteName;
this.textArea.value = '';
this.store.setActiveNote(noteName);
// Initialize tracking for new note
this.noteContent.set(noteName, '');
this.savedContent.set(noteName, '');
this.unsavedChanges.set(noteName, false);
this.renderTabs();
this.store.setNoteContent(noteName, '');
this.store.setSavedContent(noteName, '');
this.store.setUnsavedChanges(noteName, false);
}
} catch (error) {
console.error('Error creating note:', error);
@@ -335,7 +441,7 @@ class NotesSidebar {
async refreshNotes() {
// Preserve current content before refreshing
if (this.activeNote && this.textArea) {
this.noteContent.set(this.activeNote, this.textArea.value);
this.store.setNoteContent(this.activeNote, this.textArea.value);
}
await this.loadNotes();
@@ -348,14 +454,11 @@ class NotesSidebar {
const savedContent = this.savedContent.get(this.activeNote) || '';
// Update current content
this.noteContent.set(this.activeNote, currentContent);
this.store.setNoteContent(this.activeNote, currentContent);
// Check if content has changed from saved version
const hasUnsavedChanges = currentContent !== savedContent;
this.unsavedChanges.set(this.activeNote, hasUnsavedChanges);
// Update tab display
this.renderTabs();
this.store.setUnsavedChanges(this.activeNote, hasUnsavedChanges);
}
}