Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7871cfd
UI redesign WIP
lcarrere Jan 21, 2026
7703818
UI/UX improvements WIP
lcarrere Jan 21, 2026
b270e87
UI/UX improvements WIP
lcarrere Jan 21, 2026
5ef1672
Redesign WIP
lcarrere Jan 21, 2026
84caaab
again
lcarrere Jan 21, 2026
159f8b8
WIP
lcarrere Jan 21, 2026
5f0bec1
removed some legacy codes
lcarrere Jan 21, 2026
355cbaa
Better reasoning support
lcarrere Jan 21, 2026
8b3826d
pack of UI improvements
lcarrere Jan 21, 2026
79f8e31
improved model card ui
lcarrere Jan 21, 2026
ada8aa6
attachment support WIP
lcarrere Jan 21, 2026
260f34c
Attachment support WIP
lcarrere Jan 21, 2026
2dc1e5f
Attachment support WIP
lcarrere Jan 21, 2026
2da4f17
Attachment support WIP
lcarrere Jan 21, 2026
88b5b11
attachment support WIP
lcarrere Jan 21, 2026
46e4f91
Mardown rendering improvements
lcarrere Jan 21, 2026
ea6d939
markdig drop
lcarrere Jan 21, 2026
d8089a1
Markdown support improvements
lcarrere Jan 21, 2026
34f1fe2
Markdown rendering improvements
lcarrere Jan 21, 2026
b57dc91
again
lcarrere Jan 21, 2026
275fa16
better equation rendering
lcarrere Jan 21, 2026
5732933
again
lcarrere Jan 21, 2026
810d3e3
better wordwrap support
lcarrere Jan 21, 2026
b5676d1
heart
lcarrere Jan 21, 2026
e9f8bdb
More unified style - theming support WIP
lcarrere Jan 21, 2026
c93f017
improved theming support
lcarrere Jan 21, 2026
3687b0c
model storage dir config WIP
lcarrere Jan 22, 2026
233e0e8
WIP
lcarrere Jan 22, 2026
6ef8fff
WIP
lcarrere Jan 22, 2026
09bd130
WIP
lcarrere Jan 22, 2026
fbabee2
WIP
lcarrere Jan 22, 2026
ada4114
WIP
lcarrere Jan 22, 2026
b92d7bf
latex rendering improvements
lcarrere Jan 22, 2026
542e07e
Starring support
lcarrere Jan 22, 2026
280c45f
WIP
lcarrere Jan 22, 2026
62a0d9f
more productive history area
lcarrere Jan 22, 2026
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
2 changes: 1 addition & 1 deletion LM-Kit-Maestro/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@


using LMKit.Maestro.UI;
using LMKit.Maestro.UI.Pages;
using LMKit.Maestro.ViewModels;
using Microsoft.AspNetCore.Components.WebView.Maui;
using LMKit.Maestro.UI.Pages;
#if MACCATALYST
using UIKit;
using WebKit;
Expand Down
2 changes: 1 addition & 1 deletion LM-Kit-Maestro/AppConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

internal static class AppConstants
{
public const string AppVersion = "0.1.5";
public const string AppVersion = "2016.1.1";
public const string AppName = "LM-Kit Maestro";
public static string AppNameWithVersion => $"{AppName} {AppVersion}";

Expand Down
26 changes: 23 additions & 3 deletions LM-Kit-Maestro/Data/MaestroDatabase.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
using LMKit.Maestro.Models;
using LMKit.Maestro.Models;
using LMKit.Maestro.Services;
using SQLite;

namespace LMKit.Maestro.Data
{
public sealed class MaestroDatabase : IMaestroDatabase
{
public static string DatabasePath => Path.Combine(FileSystem.AppDataDirectory, "MaestroSQLite.db3");
// Static fallback for legacy access
public static string DefaultDatabasePath => Path.Combine(FileSystem.AppDataDirectory, "MaestroSQLite.db3");

private readonly string _databasePath;

public string DatabasePath => _databasePath;

private SQLiteAsyncConnection? _sqlDatabase;

public MaestroDatabase(IAppSettingsService appSettingsService)
{
// Read path once at construction - changes require restart
var historyDir = appSettingsService.ChatHistoryDirectory;
_databasePath = Path.Combine(historyDir, "MaestroSQLite.db3");
}

private async Task Init()
{
if (_sqlDatabase is not null)
Expand All @@ -18,7 +31,14 @@ private async Task Init()

try
{
_sqlDatabase = new SQLiteAsyncConnection(DatabasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache);
// Ensure directory exists
var directory = Path.GetDirectoryName(_databasePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}

_sqlDatabase = new SQLiteAsyncConnection(_databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache);

await _sqlDatabase.CreateTableAsync<ConversationLog>();
}
Expand Down
2 changes: 0 additions & 2 deletions LM-Kit-Maestro/LM-Kit-Maestro.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@
<ItemGroup>
<PackageReference Include="LM-Kit.NET" Version="2026.1.1" />
<PackageReference Include="Majorsoft.Blazor.Components.Common.JsInterop" Version="1.5.0" />
<PackageReference Include="Markdig" Version="0.44.0" />
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.20" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="10.0.20" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="10.0.20" />
Expand All @@ -86,7 +85,6 @@
<PackageReference Include="MetroLog.Maui" Version="2.1.0" />
<PackageReference Include="MudBlazor" Version="8.15.0" />
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
<PackageReference Include="System.Text.Json" Version="10.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
10 changes: 4 additions & 6 deletions LM-Kit-Maestro/LM-Kit-Maestro.dev.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,17 @@
<ItemGroup>

<PackageReference Include="Majorsoft.Blazor.Components.Common.JsInterop" Version="1.5.0" />
<PackageReference Include="Markdig" Version="0.44.0" />
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.20" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="10.0.20" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="10.0.20" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.1" />
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.30" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="10.0.30" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="10.0.30" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.2" />
<PackageReference Include="CommunityToolkit.Maui" Version="13.0.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="MetroLog.Maui" Version="2.1.0" />
<PackageReference Include="Mopups" Version="1.3.4" />
<PackageReference Include="MudBlazor" Version="8.15.0" />
<PackageReference Include="SimpleToolkit.SimpleShell" Version="5.0.1" />
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
<PackageReference Include="System.Text.Json" Version="10.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
8 changes: 4 additions & 4 deletions LM-Kit-Maestro/MainPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ namespace LMKit.Maestro.UI.Pages;

public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
public MainPage()
{
InitializeComponent();
}
}
31 changes: 21 additions & 10 deletions LM-Kit-Maestro/MauiProgram.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using CommunityToolkit.Maui;
using CommunityToolkit.Maui;
using CommunityToolkit.Maui.Storage;
using LMKit.Maestro.Data;
using LMKit.Maestro.Services;
Expand Down Expand Up @@ -74,8 +74,6 @@ private static void RegisterViewModels(this MauiAppBuilder builder)
builder.Services.AddSingleton<ModelsSettingsViewModel>();
builder.Services.AddSingleton<ChatSettingsViewModel>();
builder.Services.AddSingleton<ChatPageViewModel>();
builder.Services.AddTransient<ModelsPageViewModel>();
builder.Services.AddSingleton<AssistantsPageViewModel>();
}

private static void RegisterViews(this MauiAppBuilder builder)
Expand All @@ -85,20 +83,33 @@ private static void RegisterViews(this MauiAppBuilder builder)

private static void RegisterServices(this MauiAppBuilder builder)
{
// Core services (register first as they have no dependencies)
builder.Services.AddSingleton(Preferences.Default);
builder.Services.AddSingleton(Launcher.Default);
builder.Services.AddSingleton<HttpClient>();

// Settings service (depends on Preferences)
builder.Services.AddSingleton<AppSettingsService>();
builder.Services.AddSingleton<IAppSettingsService>(sp => sp.GetRequiredService<AppSettingsService>());

// Database (depends on IAppSettingsService)
builder.Services.AddSingleton<IMaestroDatabase, MaestroDatabase>();

// Folder picker service (platform-specific)
#if WINDOWS
builder.Services.AddSingleton<IFolderPickerService, WindowsFolderPickerService>();
#else
builder.Services.AddSingleton<IFolderPickerService, DefaultFolderPickerService>();
#endif

// Other services
builder.Services.AddSingleton<ILLMFileManager, LLMFileManager>();
builder.Services.AddSingleton<IAppSettingsService, AppSettingsService>();
builder.Services.AddSingleton<IMainThread, Services.MainThread>();
builder.Services.AddSingleton<ISnackbarService, SnackbarService>();

builder.Services.AddSingleton<ThemeService>();
builder.Services.AddSingleton<IFolderPicker>(FolderPicker.Default);

builder.Services.AddSingleton(Launcher.Default);
builder.Services.AddSingleton(Preferences.Default);
builder.Services.AddSingleton<LMKitService>();
builder.Services.AddSingleton<LLMFileManager>();
builder.Services.AddSingleton<HttpClient>();
}

private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
Expand Down Expand Up @@ -138,4 +149,4 @@ public static void ConfigureLogger(this MauiAppBuilder builder)
builder.Services.AddSingleton(LogOperatorRetriever.Instance);
}
}
}
}
166 changes: 166 additions & 0 deletions LM-Kit-Maestro/Models/ChatAttachment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace LMKit.Maestro.Models;

/// <summary>
/// Represents a file attachment for chat messages (images or PDFs).
/// </summary>
public class ChatAttachment : INotifyPropertyChanged
{
private string _fileName = string.Empty;
private string _mimeType = string.Empty;
private byte[] _content = Array.Empty<byte>();
private string? _thumbnailBase64;
private bool _isImage;
private bool _isPdf;

public event PropertyChangedEventHandler? PropertyChanged;

/// <summary>
/// The original file name.
/// </summary>
public string FileName
{
get => _fileName;
set { _fileName = value; OnPropertyChanged(); }
}

/// <summary>
/// The MIME type of the attachment (e.g., "image/png", "application/pdf").
/// </summary>
public string MimeType
{
get => _mimeType;
set
{
_mimeType = value;
IsImage = value?.StartsWith("image/") == true;
IsPdf = value == "application/pdf";
OnPropertyChanged();
}
}

/// <summary>
/// The raw file content as bytes.
/// </summary>
public byte[] Content
{
get => _content;
set { _content = value; OnPropertyChanged(); }
}

/// <summary>
/// Base64-encoded thumbnail for display in the UI.
/// For images, this is the image itself (possibly resized).
/// For PDFs, this could be a PDF icon or first page preview.
/// </summary>
public string? ThumbnailBase64
{
get => _thumbnailBase64;
set { _thumbnailBase64 = value; OnPropertyChanged(); }
}

/// <summary>
/// Whether this attachment is an image.
/// </summary>
public bool IsImage
{
get => _isImage;
private set { _isImage = value; OnPropertyChanged(); }
}

/// <summary>
/// Whether this attachment is a PDF.
/// </summary>
public bool IsPdf
{
get => _isPdf;
private set { _isPdf = value; OnPropertyChanged(); }
}

/// <summary>
/// File size in bytes.
/// </summary>
public long FileSize => Content?.Length ?? 0;

/// <summary>
/// Human-readable file size.
/// </summary>
public string FileSizeDisplay
{
get
{
var size = FileSize;
if (size < 1024)
{
return $"{size} B";
}

if (size < 1024 * 1024)
{
return $"{size / 1024.0:F1} KB";
}

return $"{size / (1024.0 * 1024.0):F1} MB";
}
}

protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

/// <summary>
/// Creates a ChatAttachment from file bytes.
/// </summary>
public static ChatAttachment FromBytes(string fileName, string mimeType, byte[] content)
{
var attachment = new ChatAttachment
{
FileName = fileName,
MimeType = mimeType,
Content = content
};

// Generate thumbnail
if (attachment.IsImage)
{
// For images, use the image itself as thumbnail (base64)
attachment.ThumbnailBase64 = Convert.ToBase64String(content);
}
// For PDFs, ThumbnailBase64 remains null (PDF icon handled in UI)

return attachment;
}

/// <summary>
/// Gets the accepted file types for vision models.
/// </summary>
public static string AcceptedFileTypes => ".png,.jpg,.jpeg,.gif,.webp,.bmp,.tiff,.pdf";

/// <summary>
/// Validates if the given MIME type is supported for vision attachments.
/// </summary>
public static bool IsSupportedMimeType(string mimeType)
{
if (string.IsNullOrEmpty(mimeType))
{
return false;
}

var supportedTypes = new[]
{
"image/png",
"image/jpeg",
"image/jpg",
"image/gif",
"image/webp",
"image/bmp",
"image/tiff",
"application/pdf"
};

return supportedTypes.Contains(mimeType.ToLowerInvariant());
}
}
2 changes: 2 additions & 0 deletions LM-Kit-Maestro/Models/ConversationLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public sealed class ConversationLog

public Uri? LastUsedModel { get; set; }

public bool IsStarred { get; set; }

public ConversationLog()
{
}
Expand Down
Loading
Loading