Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/backend/models/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
type BlockType string

const (
// TODO: Align block types / remove unneded
TextBlock BlockType = "text"
TaskBlock BlockType = "task"
HeadingBlock BlockType = "header"
Expand Down
11 changes: 7 additions & 4 deletions src/frontend/lib/providers/note_editor_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -447,12 +447,12 @@ class NoteEditorProvider with ChangeNotifier implements NoteEditorViewModel {
}

// DocumentChangeListener implementation for content changes
void _documentChangeListener(dynamic _) {
void _documentChangeListener(dynamic changelog) {
if (_updatingDocument) return;

// Check if this change is a DocumentChangeLog which might contain TaskNode changes
if (_ is DocumentChangeLog) {
DocumentChangeLog changeLog = _;
if (changelog is DocumentChangeLog) {
DocumentChangeLog changeLog = changelog;

// Check if this change includes a TaskNode's isComplete property change
bool hasTaskStateChange = false;
Expand Down Expand Up @@ -527,7 +527,10 @@ class NoteEditorProvider with ChangeNotifier implements NoteEditorViewModel {

// Get node ID from selection
final nodeId = selection.extent.nodeId;

if (nodeId == documentBuilder.titleNode.id) {
updateNoteTitle(noteId!, documentBuilder.titleNode.asTextNode.text.toPlainText());
}

// Find the block ID for this node
final blockId = _documentBuilder.nodeToBlockMap[nodeId];
if (blockId == null) return;
Expand Down
26 changes: 9 additions & 17 deletions src/frontend/lib/screens/note_editor_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ class _NoteEditorScreenState extends State<NoteEditorScreen> {

// Load initial blocks for the note using ViewModel
await _noteEditorViewModel.fetchBlocksForNote(_noteId!, page: 1, pageSize: 30);

// Set the title in the editor
_noteEditorViewModel.documentBuilder.insertTitleNode(_note!.title);

// Initialize the scroll listener for pagination - JUST ONCE
_noteEditorViewModel.initScrollListener(_scrollController);
Expand Down Expand Up @@ -197,16 +200,6 @@ class _NoteEditorScreenState extends State<NoteEditorScreen> {
}
}

// Build the rich text editor directly, without using a separate widget
Widget _buildRichTextEditor(NoteEditorViewModel viewModel) {
// Remove redundant notification listener and use the scroll controller directly
return viewModel.documentBuilder.createSuperEditor(
readOnly: false,
scrollController: _scrollController,
themeData: Theme.of(context),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand All @@ -218,10 +211,7 @@ class _NoteEditorScreenState extends State<NoteEditorScreen> {
IconButton(
icon: const Icon(Icons.refresh_outlined),
tooltip: 'Refresh note content',
onPressed: () => _noteEditorViewModel.fetchBlocksForNote(
_noteEditorViewModel.noteId?? '',
refresh: true
),
onPressed: () => _initialize(),
),
const ThemeSwitcher(),
],
Expand All @@ -236,15 +226,14 @@ class _NoteEditorScreenState extends State<NoteEditorScreen> {
if (noteEditorViewModel.currentNote != null &&
noteEditorViewModel.currentNote!.id == _noteId) {
_note = noteEditorViewModel.currentNote;

// Update title if changed from server
if (_note != null &&
_titleController.text != _note!.title &&
!_titleFocusNode.hasFocus) {
_titleController.text = _note!.title;
}
}

// React to loading state
final isContentLoading = noteEditorViewModel.isLoading;

Expand Down Expand Up @@ -301,7 +290,10 @@ class _NoteEditorScreenState extends State<NoteEditorScreen> {
child: Stack(
children: [
// Editor content - using direct integration
_buildRichTextEditor(noteEditorViewModel),
_noteEditorViewModel.documentBuilder.createSuperEditor(
scrollController: _scrollController,
themeData: Theme.of(context),
),
// Loading overlay
if (isContentLoading)
const Center(child: CircularProgressIndicator()),
Expand Down
31 changes: 29 additions & 2 deletions src/frontend/lib/utils/document_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class DocumentBuilder {
late MutableDocumentComposer composer;
late Editor editor;
late FocusNode focusNode;
late DocumentNode titleNode;

// Document layout key for accessing the document layout
final GlobalKey documentLayoutKey = GlobalKey();
Expand Down Expand Up @@ -81,6 +82,15 @@ class DocumentBuilder {
// Create focus node
focusNode = FocusNode();

titleNode = ParagraphNode(
id: Editor.createNodeId(),
text: AttributedText(""),
metadata: const {
'blockType': NamedAttribution("header1"),
'isDeletable': false
},
);

// Store initial node IDs for tracking structure changes
_lastKnownNodeIds = document.map((node) => node.id).toList();
_lastKnownNodeCount = document.length;
Expand Down Expand Up @@ -110,6 +120,19 @@ class DocumentBuilder {
document.removeListener(listener);
}

// Create title special node
void insertTitleNode(String title) {
titleNode = titleNode.asTextNode.copyTextNodeWith(
text: AttributedText(title),
);
if (document.getNodeAt(0) != null &&
!document.getNodeAt(0)!.isDeletable ) {
document.replaceNodeById(titleNode.id, titleNode);
} else {
document.insertNodeAt(0, titleNode);
}
}

// Insert a new node into the document
void insertNode(DocumentNode node) {
try {
Expand Down Expand Up @@ -451,6 +474,12 @@ class DocumentBuilder {
return false;
}

// Skip if this node has it's note title
if (!node.isDeletable) {
_logger.debug('Node $nodeId is title node, skipping');
return false;
}

// Mark as uncommitted
_blockNodeMapping.markNodeAsUncommitted(nodeId);
return true;
Expand Down Expand Up @@ -1222,7 +1251,6 @@ class DocumentBuilder {

// Create Super Editor with configured components for SuperEditor 0.3.0
Widget createSuperEditor({
required bool readOnly,
ScrollController? scrollController,
ThemeData? themeData,
}) {
Expand All @@ -1244,7 +1272,6 @@ class DocumentBuilder {
stylesheet: stylesheet,
selectionStyle: selectionStyles,
componentBuilders: componentBuilders,
keyboardActions: defaultKeyboardActions,
documentOverlayBuilders: [
DefaultCaretOverlayBuilder(
caretStyle: CaretStyle(
Expand Down