UnifiedNotes.Maui (1.4.4)

Published 2026-01-06 19:54:40 +00:00 by logikonline

Installation

dotnet nuget add source --name logikonline --username your_username --password your_token 
dotnet add package --source logikonline --version 1.4.4 UnifiedNotes.Maui

About this package

A comprehensive note-taking and management control library for .NET MAUI applications with features including:

  - Rich text note editing with Syncfusion RichTextEditor (WYSIWYG)
  - Bold, italic, underline, strikethrough, and more formatting options
  - Note organization with folders and tags
  - Search and filtering capabilities
  - Per-note dirty state tracking with undo support
  - AI-powered note insights using UnifiedRag.Maui
  - Data integration with UnifiedData.Maui
  - Customizable UI with bindable properties
  - Touch gesture support and responsive design
  - Note data persistence and sync

UnifiedNotes.Maui

A professional note-taking control for .NET MAUI applications featuring rich text editing, intelligent organization, hierarchical document trees, and AI-powered summaries.

Version 1.3.4

Features

  • Rich Text Editing - WYSIWYG editor powered by Syncfusion RichTextEditor with bold, italic, underline, lists, links, tables, and more
  • Smart Grouping - Notes automatically grouped by date (Today, Yesterday, This Week, Last Week, Month/Year) with sticky headers
  • Document Hierarchy - Tree view for parent-child document relationships with load-on-demand, node reordering, and pagination
  • Tag Management - Add/remove tags as pills, filter by tags
  • Search & Filter - Full-text search, filter by tags, dates, pinned status, or top-level only
  • Pin Notes - Pin important notes to the top of the list
  • Per-Note Dirty Tracking - Visual indicator (orange dot) shows unsaved changes, with Undo support
  • AI Features - AI-generated summaries and document classification via UnifiedRag.Maui integration
  • Responsive Layout - Three-pane layout (list, editor, details/tree) using MALayout
  • Data Persistence - Stores notes via UnifiedData.Maui service

Installation

dotnet add package UnifiedNotes.Maui

Dependencies

This package requires:

  • Syncfusion.Maui.RichTextEditor - Rich text editing
  • Syncfusion.Maui.ListView - Grouped list with sticky headers
  • Syncfusion.Maui.TreeView - Hierarchical document tree display
  • Syncfusion.Maui.DataSource - Sorting and grouping
  • MarketAlly.MALayout - Responsive pane layout
  • MarketAlly.MAToolbar - Toolbar controls
  • MarketAlly.Dialogs.Maui - Confirmation dialogs
  • CommunityToolkit.Mvvm - MVVM source generators
  • Indiko.Maui.Controls.Markdown - Markdown rendering
  • UnifiedData.Maui - Data persistence (via UnifiedRag.Maui)
  • UnifiedRag.Maui - AI integration (optional)

Note

: Syncfusion components require a valid license. See Syncfusion Licensing.

Setup

1. Register in MauiProgram.cs

There are two approaches to configure UnifiedNotes:

Use the AddUnifiedNotes* methods that configure everything automatically:

using UnifiedNotes.Maui.Extensions;

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();

    builder
        .UseMauiApp<App>()
        .ConfigureSyncfusionCore()
        // Add UnifiedNotes with OpenAI (configures data + RAG + notes)
        .AddUnifiedNotes(
            openAiApiKey: "sk-...",
            modelName: "gpt-4o-mini",
            configureNotes: options =>
            {
                options.SummaryMode = NoteSummaryMode.Both;
                options.AutoSave = true;
            }
        );

    return builder.Build();
}

Option B: Manual Setup with UseUnifiedNotes

Register services separately for more control:

using UnifiedNotes.Maui.Extensions;

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();

    builder
        .UseMauiApp<App>()
        // Register Syncfusion (required)
        .ConfigureSyncfusionCore()
        // Register UnifiedData for persistence
        .UseUnifiedData(options => { /* configure */ })
        // Register UnifiedRag for AI features (optional)
        .UseUnifiedRag(options => { /* configure */ })
        // Register UnifiedNotes
        .UseUnifiedNotes(options =>
        {
            options.SummaryMode = NoteSummaryMode.Both;  // AI generates, user can edit
            options.AutoSave = true;
            options.AutoSaveIntervalSeconds = 10;
        });

    return builder.Build();
}

2. Add to XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:notes="clr-namespace:UnifiedNotes.Maui.Controls;assembly=UnifiedNotes.Maui">

    <notes:UnifiedNotesControl x:Name="NotesControl" />

</ContentPage>

3. Initialize (Code-Behind)

public partial class NotesPage : ContentPage
{
    public NotesPage(UnifiedNotesControl notesControl)
    {
        InitializeComponent();

        // Use DI-provided control or get from service provider
        Content = notesControl;

        // Load notes on appearing
        notesControl.LoadNotesAsync();
    }
}

Configuration Options

Option Type Default Description
NoteType string "note" Document type used to identify and filter notes in the database
ContentType DocumentContentType AllNone Content type used to filter documents (0-99 built-in, 100+ custom). Ignored if ContentTypeRegistry is set.
ContentTypeRegistry Dictionary<int, string>? null Registry of content types to filter by, with display names. Takes precedence over ContentType.
SummaryMode NoteSummaryMode Both How summaries are generated
EnableRichTextEditing bool true Enable WYSIWYG editor
AutoSave bool true Auto-save notes
AutoSaveIntervalSeconds int 10 Auto-save interval
ShowFolders bool true Show folder organization
EnableAIInsights bool true Enable AI features
DefaultSortOrder string "modified" Default sort (created/modified/alphabetical)

Content Types

You can use the ContentType option to filter documents by their content type. This leverages the DocumentContentType enum from UnifiedData.Maui:

// Show only Plan-type documents
builder.UseUnifiedNotes(options =>
{
    options.NoteType = "execution";
    options.ContentType = DocumentContentType.Plan;
});

// Use a custom content type (100+)
builder.UseUnifiedNotes(options =>
{
    options.NoteType = "custom";
    options.ContentType = (DocumentContentType)150; // Custom type
});

Built-in content types (0-99):

  • AllNone (0) - No filtering, shows all content types
  • Plan - Execution plans
  • StepOutput - Step execution outputs
  • Trace - Trace/debug information

Custom content types (100+) can be defined by your application.

Content Type Registry (Multiple Types)

When you need to filter by multiple content types and display friendly names in the UI, use ContentTypeRegistry:

builder.UseUnifiedNotes(options =>
{
    options.NoteType = "conversation";
    options.ContentTypeRegistry = new Dictionary<int, string>
    {
        { 207, "Session" },
        { 208, "Exchange" },
        { 209, "Tool Call" }
    };
});

This provides:

  1. Multi-type filtering - SQL WHERE content_type IN (207, 208, 209) instead of a single type
  2. Display names - Details pane shows "Session" instead of "Custom (207)"

Example with a complete qLynq-style setup:

// Define content types as constants
private const int SessionType = 207;
private const int ExchangeType = 208;
private const int ToolCallType = 209;

builder.UseUnifiedNotes(options =>
{
    options.NoteType = "conversation";
    options.ContentTypeRegistry = new Dictionary<int, string>
    {
        { SessionType, "Session" },
        { ExchangeType, "Exchange" },
        { ToolCallType, "Tool Call" }
    };
});

Helper Methods on NotesOptions:

  • GetContentTypeIds() - Returns int[] of content type IDs to filter by
  • GetContentTypeName(int id) - Returns display name for a content type ID
  • HasMultipleContentTypes - true if registry has 2+ entries

Content Type Split Button:

When ContentTypeRegistry has multiple entries, the "New Note" toolbar button automatically becomes a split button with a dropdown menu:

  • Click the button - Creates a note with the first content type in the registry
  • Click the dropdown arrow - Shows all registered content types, allowing the user to select which type to create

This ensures newly created notes are visible in the filtered list, solving the issue where notes created with ContentType = 0 (None) wouldn't appear when filtering by specific types.

For child notes created via the tree view's "Add Child" button, the content type is inherited from the parent note (if it's in the registry) or defaults to the first registered type.

Multiple Control Instances

When you need multiple UnifiedNotesControl instances with different configurations (e.g., one for Notes, one for Memory), use the Configure() method or constructor with options:

Option 1: Configure() method (recommended for XAML-defined controls)

<!-- In your XAML -->
<controls:UnifiedNotesControl x:Name="NotesControl" />
<controls:UnifiedNotesControl x:Name="MemoryControl" />
// In code-behind (e.g., OnAppearing or constructor)
protected override async void OnAppearing()
{
    base.OnAppearing();

    // Configure Notes control (standard notes)
    await NotesControl.Configure(new NotesOptions
    {
        NoteType = "note",
        ContentType = DocumentContentType.AllNone
    });

    // Configure Memory control (custom content types 200+)
    await MemoryControl.Configure(new NotesOptions
    {
        NoteType = "memory",
        ContentTypeRegistry = new Dictionary<int, string>
        {
            { 200, "Conversation" },
            { 201, "User Fact" },
            { 202, "Execution Trace" },
            { 207, "Session" },
            { 208, "Exchange" },
            { 209, "Tool Call" }
        }
    });
}

Option 2: Constructor with options (for code-created controls)

public partial class MemoryPopup : ContentPage
{
    public MemoryPopup(IUnifiedDataService dataService, IRAGService ragService)
    {
        InitializeComponent();

        var memoryOptions = new NotesOptions
        {
            NoteType = "memory",
            ContentTypeRegistry = new Dictionary<int, string>
            {
                { 200, "Conversation" },
                { 201, "User Fact" },
                { 202, "Execution Trace" }
            }
        };

        var memoryControl = new UnifiedNotesControl(
            dataService, ragService, null, null, memoryOptions);
        Content = memoryControl;
    }
}

Important: When using multiple instances, do NOT use builder.UseUnifiedNotes() for DI registration since that registers a single shared NotesOptions. Instead, register only the services and configure each control instance individually.

Custom Note Types

You can use the NoteType option to create separate note collections within the same database of the SAME Content Type. This is useful for:

  • Separating different types of content (e.g., "journal", "memo", "task", "meeting")
  • Creating multiple note controls that manage different document types
  • Integrating with other systems that use specific document types

YOU are NOT to use this in replacement of the ContentTypes - ContentTypes shuld be used for the larger grouping. The Custom Not Types are a sub category under the ContentType

// Journal entries - stored separately from regular notes
builder.UseUnifiedNotes(options =>
{
    options.NoteType = "journal";
});

// Meeting notes in a different control
builder.UseUnifiedNotes(options =>
{
    options.NoteType = "meeting";
});

Notes created with different NoteType values are stored in the same database but filtered separately, so each control only shows its own type.

Provider Setup Options

OpenAI (Default)

builder.AddUnifiedNotes(
    openAiApiKey: "your-api-key",
    modelName: "gpt-4o-mini"
);

Anthropic Claude

builder.AddUnifiedNotesWithClaude(
    anthropicApiKey: "your-api-key",
    modelName: "claude-3-5-sonnet-20241022"
);

Together.AI

builder.AddUnifiedNotesWithTogetherAI(
    togetherApiKey: "your-api-key",
    modelName: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo"
);

Local Model

builder.AddUnifiedNotesWithLocalModel(
    modelPath: "/path/to/model.gguf",
    maxTokens: 512
);

Basic (No AI)

builder.AddUnifiedNotesBasic();

Dynamic Settings (App-Provided IProviderSettings)

// Register your settings adapter first
builder.Services.AddSingleton<IProviderSettings, MySettingsAdapter>();

// Then add notes with dynamic settings
builder.AddUnifiedNotesWithDynamicSettings(
    configureNotes: options =>
    {
        options.SummaryMode = NoteSummaryMode.Auto;
    }
);

Runtime Settings (Built-in RuntimeProviderSettings)

// Initial setup with runtime-updateable settings
builder.AddUnifiedNotesWithRuntimeSettings(
    provider: "openai",
    apiKey: "sk-...",
    modelName: "gpt-4o-mini",
    configureNotes: options =>
    {
        options.SummaryMode = NoteSummaryMode.Both;
    }
);

// Later, update settings at runtime:
var settings = serviceProvider.GetRequiredService<RuntimeProviderSettings>();

// Update just the API key
await settings.UpdateApiKeyAsync("sk-new-key");

// Or switch to a different provider entirely
await settings.SwitchProviderAsync(
    provider: "anthropic",
    apiKey: "sk-ant-...",
    modelName: "claude-3-5-sonnet-20241022"
);

XAML Instantiation with Lazy Service Resolution

When you create the control in XAML (without DI), services are automatically resolved from the DI container when needed:

<!-- In XAML - services will be resolved lazily -->
<notes:UnifiedNotesControl x:Name="NotesControl" />

How it works:

  • When the control is created in XAML, the default constructor is used
  • Services (IRAGService, IUnifiedDataService, IRAGServiceSettings) are resolved lazily from Application.Current.Handler.MauiContext.Services
  • Resolution happens automatically when notes are loaded or saved
  • If you registered services via AddUnifiedNotesWithDynamicSettings() or other methods, they will be available

This means you can:

  1. Configure services in MauiProgram.cs using any AddUnifiedNotes* method
  2. Create the control in XAML without constructor parameters
  3. AI features will work automatically when the user interacts with notes
// In MauiProgram.cs
builder.Services.AddSingleton<IProviderSettings, MySettingsAdapter>();
builder.AddUnifiedNotesWithDynamicSettings();

// In XAML - just use the control, services resolve automatically
// <notes:UnifiedNotesControl x:Name="NotesControl" />

Summary Modes

public enum NoteSummaryMode
{
    None,    // No summary field shown
    Auto,    // AI generates summary automatically on save
    Manual,  // User enters summary manually
    Both     // AI generates, user can edit (default)
}

Architecture

UnifiedNotes.Maui/
├── Controls/
│   └── UnifiedNotesControl        # Main control with 3-pane layout
├── Views/
│   ├── NoteListView               # Left pane - searchable, grouped note list
│   └── NoteEditorView             # Center pane - rich text editor
├── ViewModels/
│   └── NotesViewModel             # MVVM ViewModel with commands and state
├── Models/
│   ├── NoteModel                  # Note data wrapper with dirty tracking
│   ├── TreeNodeModel              # Tree view node for document hierarchy
│   └── SourceUrlClickedEventArgs  # Event args for source URL clicks
├── Converters/                    # XAML value converters
│   ├── BoolToPaneVisibilityStateConverter
│   ├── InvertedBoolConverter
│   ├── IsNotNullConverter
│   ├── UtcToLocalDateTimeConverter
│   └── ... (8 total)
└── Extensions/
    ├── MauiAppBuilderExtensions   # AddUnifiedNotes* setup methods
    ├── ServiceCollectionExtensions # DI registration
    └── SharedRegistrationTracker  # Prevents duplicate registrations

Key Components

Component Description
UnifiedNotesControl Main control with 3-pane layout (list, editor, details/tree)
NoteListView Left pane - searchable, grouped note list with pagination
NoteEditorView Center pane - Syncfusion rich text editor
NotesViewModel MVVM ViewModel with commands, state, and tree management
NoteModel Note data wrapper with dirty tracking and undo support
TreeNodeModel Hierarchical tree node with load-on-demand and pagination

Data Model

Notes are stored as UnifiedDocument with DocumentType = "note":

public class NoteModel
{
    public string Id { get; }
    public string Title { get; set; }
    public string Content { get; set; }        // HTML content
    public string[] Tags { get; set; }
    public string Summary { get; set; }
    public bool IsPinned { get; set; }
    public bool IsDirty { get; }               // Has unsaved changes
    public DateTime CreatedAt { get; }
    public DateTime UpdatedAt { get; set; }
    public string DateGroupName { get; }       // "Today", "Yesterday", etc.
    public string Preview { get; }             // Plain text preview (HTML stripped)

    // Document hierarchy
    public string? ParentId { get; }           // Parent document ID
    public string? RootId { get; }             // Root document ID
    public int ChildCount { get; }             // Number of child documents

    // Source tracking
    public string? SourceUrl { get; set; }     // Source URL if imported
    public bool HasSourceUrl { get; }

    // Classification
    public bool IsClassified { get; }          // Has been AI-classified
    public DateTime? ClassifiedAt { get; }
}

public class TreeNodeModel
{
    public string Id { get; }                  // Document ID
    public string? ParentId { get; }           // Parent document ID
    public string Title { get; set; }
    public string DocumentType { get; set; }
    public bool IsDirty { get; set; }
    public bool IsSelected { get; set; }
    public bool IsExpanded { get; set; }
    public bool IsPinned { get; set; }
    public bool HasChildrenInDb { get; set; }  // For load-on-demand expander
    public bool IsLoadMoreNode { get; }        // Pagination node
    public int LoadMoreOffset { get; }         // Pagination offset
    public ObservableCollection<TreeNodeModel> Children { get; }
    public NoteModel? NoteModel { get; set; }  // Reference to underlying note
}

ViewModel Commands

// Note operations
LoadNotesCommand          // Load all notes from data service
CreateNoteCommand         // Create a new note
SaveNoteCommand           // Save current note
DeleteNoteCommand         // Delete note (with confirmation)
UndoChangesCommand        // Revert unsaved changes
SaveAllDirtyNotesCommand  // Save all notes with changes

// Filtering
SearchNotesCommand        // Search by text
FilterByTagCommand        // Filter by tag
FilterByDateCommand       // Filter by date range
ClearFiltersCommand       // Clear all filters
TogglePinnedCommand       // Toggle pin status
ToggleTopLevelOnlyCommand // Toggle showing only top-level notes

// Tag management
AddTagCommand             // Add tag to current note
RemoveTagCommand          // Remove tag from current note

// Tree view operations
LoadTreeHierarchyCommand  // Load document tree for selected note
MoveNodeUpCommand         // Move node up in sibling order
MoveNodeDownCommand       // Move node down in sibling order
IndentNodeCommand         // Make node child of previous sibling
OutdentNodeCommand        // Make node sibling of its parent
ToggleTreeExpandCommand   // Expand/collapse all tree nodes
LoadChildrenOnDemandCommand // Load children for a tree node

// AI operations
ClassifyNoteCommand       // Run AI classification on current note

ViewModel Properties

// Collections
ObservableCollection<NoteModel> Notes           // All notes
ObservableCollection<NoteModel> FilteredNotes   // Filtered/sorted notes
ObservableCollection<TagGroup> TagGroups        // Available tags
ObservableCollection<DateGroup> DateGroups      // Date filter options
ObservableCollection<TreeNodeModel> TreeNodes   // Document hierarchy tree

// State
NoteModel? SelectedNote      // Currently selected note
TreeNodeModel? SelectedTreeNode // Currently selected tree node
bool IsLoading               // Loading indicator
bool IsSaving                // Saving indicator
bool IsEmpty                 // No notes exist
bool HasActiveFilters        // Any filter is active
string SearchQuery           // Current search text
bool ShowPinnedOnly          // Filter to pinned only
bool ShowTopLevelOnly        // Filter to top-level notes only (default: true)

// View visibility
bool IsNoteListVisible       // Left pane visible
bool IsEditorVisible         // Center pane visible
bool IsDetailsViewVisible    // Right pane showing details
bool IsTreeViewVisible       // Right pane showing tree
bool IsRightPaneVisible      // Right pane visible (details or tree)

// Tree view state
string? RootDocumentId       // Root of current tree hierarchy
bool IsTreeExpanded          // All nodes expanded
bool IsTreeReorderMode       // Tree reordering enabled
bool CanMoveNodeUp           // Selected node can move up
bool CanMoveNodeDown         // Selected node can move down
bool CanIndentNode           // Selected node can be indented
bool CanOutdentNode          // Selected node can be outdented

// AI state
bool IsAIAvailable           // RAG service configured with API key
bool IsClassifying           // AI classification in progress

// Editor bindings
string EditorTitle           // Bound to title entry
string EditorContent         // Bound to rich text editor (HTML)
string EditorSummary         // Bound to summary field
ObservableCollection<string> EditorTagsList  // Tags as pills

Events

event EventHandler<NoteModel?> NoteSelected;             // Note selection changed
event EventHandler NotesLoaded;                          // Notes finished loading
event EventHandler<NoteModel> NoteSaved;                 // Note was saved
event EventHandler<NoteModel> NoteDeleted;               // Note was deleted
event EventHandler<SourceUrlClickedEventArgs> SourceUrlClicked;  // Source URL clicked
event EventHandler<TreeNodeModel?> TreeNodeSelected;     // Tree node selection changed

UI Features

List View

  • Grouped by date with sticky headers (Today, Yesterday, This Week, etc.)
  • Sorted by: Pinned first, then date group, then most recent
  • Visual indicators: Orange dot for unsaved, pin icon for pinned
  • Swipe/tap to delete with confirmation dialog
  • Empty state when no notes exist

Editor View

  • Title entry at top
  • Syncfusion RichTextEditor with toolbar:
    • Bold, Italic, Underline, Strikethrough
    • Font size, Text color, Highlight
    • Alignment, Bullet/Number lists, Indent
    • Hyperlinks, Tables
    • Undo/Redo
  • Bottom toolbar: Save, Undo, Pin, Delete buttons

Details Pane (Right)

  • Summary section - AI-generated or user-entered
  • Tags section - Pills with add/remove
  • Info section - Created/Modified dates, classification status
  • Source URL - Clickable link if note was imported from web

Tree View (Right Pane - Toggle)

  • Document hierarchy - Parent-child relationships displayed as expandable tree
  • Load-on-demand - Children loaded when node expanded (for performance with large trees)
  • Pagination - "Load More" nodes for branches with many children (30 per page)
  • Node reordering - Move up/down, indent/outdent operations
  • Visual indicators - Dirty state, pinned status on tree nodes
  • Two-level preload - Root and first-level children preloaded for immediate expander visibility

Toolbar Filters

  • Search - Full-text search
  • Tags - Filter by tag
  • Dates - Filter by date range
  • Pinned - Show pinned only
  • Top-level only - Show only root-level notes (default on)
  • Clear Filters - Reset all (only visible when filters active)

AI Integration

When UnifiedRag.Maui is configured with an AI provider, summaries are automatically generated:

// Summary generation prompt (internal)
"Summarize the following note in 1-2 concise sentences (max 150 characters).
Only provide the summary, no introductions:\n\n{noteContent}"

If no AI provider is configured, the summary field remains empty (no error shown).

Customization

Pane Visibility

Toggle panes via the main toolbar or programmatically:

// Access via the MALayout
NotesControl.MainLayout.Left.VisibilityState = PaneVisibilityState.Visible;
NotesControl.MainLayout.Right.VisibilityState = PaneVisibilityState.Collapsed;

Theming

The control supports Light/Dark themes via AppThemeBinding. Colors automatically adapt to system theme.

Public API for Developers/AI

The control exposes a comprehensive API for programmatic access:

Properties

// Get the configured note type (document type used for storage)
string noteType = notesControl.NoteType; // e.g., "note", "journal", "meeting"

// Get the configured content type (from ViewModel)
DocumentContentType contentType = notesControl.ViewModel.ConfiguredContentType;

// Get selected note
NoteModel? note = notesControl.SelectedNote;

// Get all notes
IReadOnlyList<NoteModel> allNotes = notesControl.Notes;

// Get filtered notes (based on current search/filter)
IReadOnlyList<NoteModel> filtered = notesControl.FilteredNotes;

// Check for unsaved changes
bool hasChanges = notesControl.HasUnsavedChanges;
int unsavedCount = notesControl.UnsavedChangesCount;

Selection Methods

// Select by ID
bool found = notesControl.SelectNoteById("note-123");

// Select by title (case-insensitive)
bool found = notesControl.SelectNoteByTitle("Meeting Notes");

// Get note by ID without selecting
NoteModel? note = notesControl.GetNoteById("note-123");

Search/Find Methods

// Find notes by title (partial match)
var notes = notesControl.FindNotesByTitle("meeting");

// Find notes by tag
var notes = notesControl.FindNotesByTag("work");

// Find notes by content
var notes = notesControl.FindNotesByContent("quarterly report");

// Full-text search (updates UI filter)
await notesControl.SearchNotesAsync("important");

Create/Update/Delete

// Create a new note
var note = await notesControl.CreateNoteAsync(
    title: "New Note",
    content: "<p>Hello world</p>",
    tags: new[] { "demo", "test" },
    autoSave: true
);

// Update selected note
notesControl.UpdateSelectedNoteTitle("Updated Title");
notesControl.UpdateSelectedNoteContent("<p>New content</p>");
notesControl.UpdateSelectedNoteTags(new[] { "tag1", "tag2" });
notesControl.UpdateSelectedNoteSummary("Brief summary");

// Update any note by ID
await notesControl.UpdateNoteByIdAsync(
    noteId: "note-123",
    title: "New Title",
    content: "<p>Updated</p>",
    tags: new[] { "updated" },
    autoSave: true
);

// Delete by ID
await notesControl.DeleteNoteByIdAsync("note-123", skipConfirmation: false);

// Pin/unpin
await notesControl.SetNotePinnedAsync("note-123", isPinned: true);

Save/Filter Operations

// Save current note
await notesControl.SaveCurrentNoteAsync();

// Save all dirty notes
await notesControl.SaveAllAsync();

// Refresh from database
await notesControl.RefreshNotesAsync();

// Filter to pinned only
notesControl.ShowPinnedOnly(true);

// Clear all filters
notesControl.ClearFilters();

Example: AI Integration

// Find and update a note based on user request
public async Task UpdateNoteFromAI(UnifiedNotesControl control, string searchTerm, string newContent)
{
    var notes = control.FindNotesByTitle(searchTerm);
    if (notes.Any())
    {
        var note = notes.First();
        await control.UpdateNoteByIdAsync(
            note.Id,
            content: newContent,
            autoSave: true
        );
    }
    else
    {
        // Create new note if not found
        await control.CreateNoteAsync(
            title: searchTerm,
            content: newContent,
            autoSave: true
        );
    }
}

Sharing Services with Other Controls

If you're using UnifiedNotes with other controls (e.g., UnifiedBrowser, UnifiedChat) that share the same database, you can set or share the IUnifiedDataService:

// Get shared services from DI
var dataService = serviceProvider.GetRequiredService<IUnifiedDataService>();
var ragService = serviceProvider.GetService<IRAGService>();

// Set services on the notes control (recommended method)
await notesControl.SetServicesAsync(dataService, ragService, autoReload: true);

// Or set individually (will trigger reload for each)
notesControl.DataService = dataService;
notesControl.RAGService = ragService;

// Access current services
IUnifiedDataService? currentDataService = notesControl.DataService;
IRAGService? currentRagService = notesControl.RAGService;

Example: Sharing database across multiple controls

public class MainPage : ContentPage
{
    private readonly IUnifiedDataService _sharedDataService;

    public MainPage(IUnifiedDataService dataService)
    {
        _sharedDataService = dataService;

        // All controls share the same database
        var notesControl = new UnifiedNotesControl(_sharedDataService);
        var browserControl = new UnifiedBrowserControl(_sharedDataService);
        var chatControl = new UnifiedChatControl(_sharedDataService);

        // Notes, browser history, and chat all stored in same database
    }
}

License

MIT License - Copyright 2025 MarketAlly

Version 1.4.4: - Fixed split button menu items not populated when using ContentTypeRegistry in constructor

  Version 1.2.0:
  - Syncfusion Rich Text Editor for WYSIWYG note editing
  - Bold, italic, underline, and other rich text formatting
  - Per-note dirty state tracking with undo support
  - Bottom toolbar with Save, Undo, Pin, Delete actions

  Version 1.1.0:
  - Complete implementation of Notes control
  - Note creation, editing, and deletion
  - Search and filter by text, tags, and date
  - Pin notes for quick access
  - MALayout responsive two-pane layout
  - Integration with UnifiedData.Maui for persistence
  - Cross-platform support (iOS, Android)

Dependencies

ID Version Target Framework
MarketAlly.Dialogs.Maui 1.6.0 net9.0-android35.0
MarketAlly.MAToolbar 1.9.10 net9.0-android35.0
UnifiedRag.Maui 1.1.3 net9.0-android35.0
CommunityToolkit.Maui 12.3.0 net9.0-android35.0
CommunityToolkit.Mvvm 8.4.0 net9.0-android35.0
Indiko.Maui.Controls.Markdown 1.3.14 net9.0-android35.0
MarketAlly.MABottomSheet 1.3.0 net9.0-android35.0
MarketAlly.MALayout 3.6.4 net9.0-android35.0
MarketAlly.TouchEffect.Maui 1.0.0 net9.0-android35.0
Microsoft.Extensions.Configuration.Binder 9.0.10 net9.0-android35.0
Microsoft.Extensions.Configuration.Json 9.0.10 net9.0-android35.0
Microsoft.Extensions.Logging.Abstractions 9.0.10 net9.0-android35.0
Microsoft.Maui.Controls 9.0.120 net9.0-android35.0
Microsoft.Maui.Controls.Compatibility 9.0.120 net9.0-android35.0
Syncfusion.Maui.Core 31.2.5 net9.0-android35.0
Syncfusion.Maui.DataSource 31.2.5 net9.0-android35.0
Syncfusion.Maui.ListView 31.2.5 net9.0-android35.0
Syncfusion.Maui.RichTextEditor 31.2.5 net9.0-android35.0
Syncfusion.Maui.TreeView 31.2.5 net9.0-android35.0
MarketAlly.Dialogs.Maui 1.6.0 net9.0-ios18.0
MarketAlly.MAToolbar 1.9.10 net9.0-ios18.0
UnifiedRag.Maui 1.1.3 net9.0-ios18.0
CommunityToolkit.Maui 12.3.0 net9.0-ios18.0
CommunityToolkit.Mvvm 8.4.0 net9.0-ios18.0
Indiko.Maui.Controls.Markdown 1.3.14 net9.0-ios18.0
MarketAlly.MABottomSheet 1.3.0 net9.0-ios18.0
MarketAlly.MALayout 3.6.4 net9.0-ios18.0
MarketAlly.TouchEffect.Maui 1.0.0 net9.0-ios18.0
Microsoft.Extensions.Configuration.Binder 9.0.10 net9.0-ios18.0
Microsoft.Extensions.Configuration.Json 9.0.10 net9.0-ios18.0
Microsoft.Extensions.Logging.Abstractions 9.0.10 net9.0-ios18.0
Microsoft.Maui.Controls 9.0.120 net9.0-ios18.0
Microsoft.Maui.Controls.Compatibility 9.0.120 net9.0-ios18.0
Syncfusion.Maui.Core 31.2.5 net9.0-ios18.0
Syncfusion.Maui.DataSource 31.2.5 net9.0-ios18.0
Syncfusion.Maui.ListView 31.2.5 net9.0-ios18.0
Syncfusion.Maui.RichTextEditor 31.2.5 net9.0-ios18.0
Syncfusion.Maui.TreeView 31.2.5 net9.0-ios18.0
MarketAlly.Dialogs.Maui 1.6.0 net9.0-windows10.0.19041
MarketAlly.MAToolbar 1.9.10 net9.0-windows10.0.19041
UnifiedRag.Maui 1.1.3 net9.0-windows10.0.19041
CommunityToolkit.Maui 12.3.0 net9.0-windows10.0.19041
CommunityToolkit.Mvvm 8.4.0 net9.0-windows10.0.19041
Indiko.Maui.Controls.Markdown 1.3.14 net9.0-windows10.0.19041
MarketAlly.MABottomSheet 1.3.0 net9.0-windows10.0.19041
MarketAlly.MALayout 3.6.4 net9.0-windows10.0.19041
MarketAlly.TouchEffect.Maui 1.0.0 net9.0-windows10.0.19041
Microsoft.Extensions.Configuration.Binder 9.0.10 net9.0-windows10.0.19041
Microsoft.Extensions.Configuration.Json 9.0.10 net9.0-windows10.0.19041
Microsoft.Extensions.Logging.Abstractions 9.0.10 net9.0-windows10.0.19041
Microsoft.Maui.Controls 9.0.120 net9.0-windows10.0.19041
Microsoft.Maui.Controls.Compatibility 9.0.120 net9.0-windows10.0.19041
Syncfusion.Maui.Core 31.2.5 net9.0-windows10.0.19041
Syncfusion.Maui.DataSource 31.2.5 net9.0-windows10.0.19041
Syncfusion.Maui.ListView 31.2.5 net9.0-windows10.0.19041
Syncfusion.Maui.RichTextEditor 31.2.5 net9.0-windows10.0.19041
Syncfusion.Maui.TreeView 31.2.5 net9.0-windows10.0.19041
Details
NuGet
2026-01-06 19:54:40 +00:00
0
David H Friedel Jr
321 KiB
Assets (2)
Versions (1) View all
1.4.4 2026-01-06