mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	feat(client): support temporarily disabling read-only note when read-only threshold is triggered
feat(client): stop the cursor from going to the top of the note when note transitions to read-only but we're still editing feat(client): stop the cursor from going to the top of the note when note transitions to read-only but we're still editing, take 2
This commit is contained in:
		@@ -128,11 +128,50 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Check if note is read-only before attempting to save
 | 
			
		||||
            const isReadOnlyBefore = await this.noteContext.isReadOnly();
 | 
			
		||||
            
 | 
			
		||||
            // If the note is read-only due to size but user is still editing,
 | 
			
		||||
            // we need to temporarily disable read-only mode
 | 
			
		||||
            if (isReadOnlyBefore && !this.noteContext.viewScope?.readOnlyTemporarilyDisabled) {
 | 
			
		||||
                // Auto-enable temporary edit mode (same as "Edit this note" button)
 | 
			
		||||
                if (this.noteContext.viewScope) {
 | 
			
		||||
                    this.noteContext.viewScope.readOnlyTemporarilyDisabled = true;
 | 
			
		||||
                    appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext: this.noteContext });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            protectedSessionHolder.touchProtectedSessionIfNecessary(note);
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await server.put(`notes/${noteId}/data`, data, this.componentId);
 | 
			
		||||
 | 
			
		||||
                this.getTypeWidget().dataSaved();
 | 
			
		||||
                
 | 
			
		||||
                // Check if this save operation made the note cross the threshold
 | 
			
		||||
                if (!isReadOnlyBefore && (await this.noteContext.isReadOnly())) {
 | 
			
		||||
                    // Note just became read-only after this save - enable temporary edit mode
 | 
			
		||||
                    if (this.noteContext.viewScope) {
 | 
			
		||||
                        this.noteContext.viewScope.readOnlyTemporarilyDisabled = true;
 | 
			
		||||
                        appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext: this.noteContext });
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } catch (err) {
 | 
			
		||||
                // If save failed because the note just became read-only,
 | 
			
		||||
                // enable temporary edit mode and try again
 | 
			
		||||
                if (!isReadOnlyBefore && (await this.noteContext.isReadOnly())) {
 | 
			
		||||
                    if (this.noteContext.viewScope) {
 | 
			
		||||
                        this.noteContext.viewScope.readOnlyTemporarilyDisabled = true;
 | 
			
		||||
                        appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext: this.noteContext });
 | 
			
		||||
                        
 | 
			
		||||
                        // Try saving again with temporary edit mode enabled
 | 
			
		||||
                        await server.put(`notes/${noteId}/data`, data, this.componentId);
 | 
			
		||||
                        this.getTypeWidget().dataSaved();
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    // For other errors, just rethrow
 | 
			
		||||
                    throw err;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        appContext.addBeforeUnloadListener(this);
 | 
			
		||||
@@ -364,9 +403,73 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
 | 
			
		||||
        return this.spacedUpdate.isAllSavedAndTriggerUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    readOnlyTemporarilyDisabledEvent({ noteContext }: EventData<"readOnlyTemporarilyDisabled">) {
 | 
			
		||||
    async readOnlyTemporarilyDisabledEvent({ noteContext }: EventData<"readOnlyTemporarilyDisabled">) {
 | 
			
		||||
        if (this.isNoteContext(noteContext.ntxId)) {
 | 
			
		||||
            this.refresh();
 | 
			
		||||
            // Check if we're dealing with a text note/editor type
 | 
			
		||||
            const isTextNote = this.type === "editableText" || this.type === "readOnlyText";
 | 
			
		||||
            
 | 
			
		||||
            if (isTextNote) {
 | 
			
		||||
                // Get current editor and selection before refresh
 | 
			
		||||
                const currentEditor = await this.noteContext?.getTextEditor();
 | 
			
		||||
                let cursorInfo = null;
 | 
			
		||||
                
 | 
			
		||||
                if (currentEditor) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        const selection = currentEditor.model.document.selection;
 | 
			
		||||
                        if (selection) {
 | 
			
		||||
                            const cursorPos = selection.getFirstPosition();
 | 
			
		||||
                            if (cursorPos) {
 | 
			
		||||
                                // Store minimal info about cursor position to restore later
 | 
			
		||||
                                cursorInfo = {
 | 
			
		||||
                                    // Store paragraph index and character offset
 | 
			
		||||
                                    paraIndex: cursorPos.path[0] || 0,
 | 
			
		||||
                                    offset: cursorPos.offset
 | 
			
		||||
                                };
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } catch (e) {
 | 
			
		||||
                        // Ignore errors in getting selection
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Perform refresh to transition editor
 | 
			
		||||
                await this.refresh();
 | 
			
		||||
                
 | 
			
		||||
                // Immediately attempt to restore cursor position without delay
 | 
			
		||||
                if (cursorInfo) {
 | 
			
		||||
                    const newEditor = await this.noteContext?.getTextEditor();
 | 
			
		||||
                    if (newEditor) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            newEditor.model.change(writer => {
 | 
			
		||||
                                const root = newEditor.model.document.getRoot();
 | 
			
		||||
                                if (!root) return;
 | 
			
		||||
                                
 | 
			
		||||
                                // Find the paragraph at the same index or closest available
 | 
			
		||||
                                const targetParaIndex = Math.min(cursorInfo.paraIndex, root.childCount - 1);
 | 
			
		||||
                                const paragraph = root.getChild(targetParaIndex);
 | 
			
		||||
                                
 | 
			
		||||
                                if (paragraph) {
 | 
			
		||||
                                    // Use an explicit cast to access maxOffset
 | 
			
		||||
                                    const maxOffset = (paragraph as any).maxOffset || 0;
 | 
			
		||||
                                    const safeOffset = Math.min(cursorInfo.offset, maxOffset);
 | 
			
		||||
                                    
 | 
			
		||||
                                    // Set cursor at the preserved position
 | 
			
		||||
                                    const position = writer.createPositionAt(paragraph, safeOffset);
 | 
			
		||||
                                    writer.setSelection(position);
 | 
			
		||||
                                }
 | 
			
		||||
                            });
 | 
			
		||||
                            
 | 
			
		||||
                            // Focus editor to make cursor visible immediately
 | 
			
		||||
                            newEditor.editing.view.focus();
 | 
			
		||||
                        } catch (e) {
 | 
			
		||||
                            // Silently fail if we can't restore cursor
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                // Not a text editor, just do a regular refresh
 | 
			
		||||
                await this.refresh();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user