Project bootstrap #1

Merged
JMR-dev merged 3 commits from project-bootstrap into main 2025-10-11 01:21:15 +00:00
14 changed files with 319 additions and 0 deletions
+32
View File
@@ -32,3 +32,35 @@ google-services.json
# Android Profiling
*.hprof
# .NET build outputs
bin/
obj/
# Visual Studio / Rider
.vs/
.idea/
*.user
*.suo
*.userprefs
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio cache/options
.vs/
.vscode/
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
+15
View File
@@ -0,0 +1,15 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="BackSafe.App"
xmlns:local="using:BackSafe"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
+47
View File
@@ -0,0 +1,47 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins;
using System.Linq;
using Avalonia.Markup.Xaml;
using BackSafe.ViewModels;
using BackSafe.Views;
namespace BackSafe;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// Avoid duplicate validations from both Avalonia and the CommunityToolkit.
// More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
DisableAvaloniaDataAnnotationValidation();
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel(),
};
}
base.OnFrameworkInitializationCompleted();
}
private void DisableAvaloniaDataAnnotationValidation()
{
// Get an array of plugins to remove
var dataValidationPluginsToRemove =
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();
// remove each entry found
foreach (var plugin in dataValidationPluginsToRemove)
{
BindingPlugins.DataValidators.Remove(plugin);
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

+29
View File
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<Folder Include="Models\" />
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.6" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.6" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.6" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.6" />
<PackageReference Include="FluentAvalonia" Version="1.1.0-beta1" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.6">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
</ItemGroup>
</Project>
+58
View File
@@ -0,0 +1,58 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
BackSafe is a cross-platform desktop application for backing up data from mobile phones (primarily Android, with photos/videos support on iOS/iPadOS) using a user-provided API key. Built with Avalonia UI and .NET 8, targeting Windows, macOS, and Linux.
## Development Commands
### Build and Run
```bash
dotnet build # Build the project
dotnet run # Run the application
dotnet build -c Release # Build for release
```
### Debugging
- Avalonia diagnostics are enabled in Debug builds only (BackSafe.csproj:22-25)
- Use F12 or Ctrl+F12 within the running application to access DevTools in Debug mode
## Architecture
### UI Framework: Avalonia UI
- Uses AXAML (Avalonia XAML) for UI definitions
- Fluent theme is the default (App.axaml:13)
- Compiled bindings enabled by default (BackSafe.csproj:8)
### MVVM Pattern
- **ViewModels**: Inherit from `ViewModelBase` (extends `ObservableObject` from CommunityToolkit.Mvvm)
- **Views**: Located in `Views/` directory, defined with `.axaml` files and code-behind `.axaml.cs`
- **ViewLocator**: Automatically maps ViewModels to Views by naming convention (ViewModel → View)
- Example: `MainWindowViewModel` → `MainWindow`
- Implementation in ViewLocator.cs:16
### Application Entry Point
- `Program.cs`: Standard .NET entry point
- Configures Avalonia with platform detection, Inter font, and trace logging
- `App.axaml.cs`: Avalonia initialization
- Disables Avalonia's DataAnnotation validation to avoid conflicts with CommunityToolkit (App.axaml.cs:35-46)
- Sets MainWindow with MainWindowViewModel as DataContext
### Key Dependencies
- **Avalonia 11.3.6**: Cross-platform UI framework
- **CommunityToolkit.Mvvm 8.2.1**: MVVM helpers (ObservableObject, commands, etc.)
- **Avalonia.Fonts.Inter**: Default font package
### Project Structure
- `ViewModels/`: Contains all ViewModel classes
- `Views/`: Contains AXAML view definitions and code-behind
- `Models/`: Placeholder directory for data models
- `Assets/`: Application resources (icons, etc.)
- `app.manifest`: Windows application manifest
## MVVM Conventions
- ViewModels use partial classes to support MVVM Toolkit source generators
- Properties in ViewModels should use `[ObservableProperty]` attribute for automatic INotifyPropertyChanged implementation
- Views use `x:DataType` for compiled bindings (see MainWindow.axaml:8)
+21
View File
@@ -0,0 +1,21 @@
using Avalonia;
using System;
namespace BackSafe;
sealed class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
+31
View File
@@ -0,0 +1,31 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using BackSafe.ViewModels;
namespace BackSafe;
public class ViewLocator : IDataTemplate
{
public Control? Build(object? param)
{
if (param is null)
return null;
var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
var type = Type.GetType(name);
if (type != null)
{
return (Control)Activator.CreateInstance(type)!;
}
return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object? data)
{
return data is ViewModelBase;
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace BackSafe.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
public string Greeting { get; } = "Welcome to Avalonia!";
}
+7
View File
@@ -0,0 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace BackSafe.ViewModels;
public class ViewModelBase : ObservableObject
{
}
+20
View File
@@ -0,0 +1,20 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:BackSafe.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="BackSafe.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="BackSafe">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Window>
+11
View File
@@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace BackSafe.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="BackSafe.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
+24
View File
@@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BackSafe", "BackSafe.csproj", "{167A4AD4-4F11-F7F9-E8AC-6D41338AF814}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{167A4AD4-4F11-F7F9-E8AC-6D41338AF814}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{167A4AD4-4F11-F7F9-E8AC-6D41338AF814}.Debug|Any CPU.Build.0 = Debug|Any CPU
{167A4AD4-4F11-F7F9-E8AC-6D41338AF814}.Release|Any CPU.ActiveCfg = Release|Any CPU
{167A4AD4-4F11-F7F9-E8AC-6D41338AF814}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BC0EECAB-90E8-450F-BBB1-AF85D5BD3409}
EndGlobalSection
EndGlobal