commit 8b5173153bdad6b5d062f6d0930e21ccf8003245 Author: chrisbell Date: Wed Oct 15 14:16:26 2025 -0500 Inital project setup diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8afdcb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,454 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# JetBrains Rider +.idea/ +*.sln.iml + +## +## Visual Studio Code +## +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..9736b13 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,22 @@ + + + + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SessionZeroClient.Android/Icon.png b/SessionZeroClient.Android/Icon.png new file mode 100644 index 0000000..3c39845 Binary files /dev/null and b/SessionZeroClient.Android/Icon.png differ diff --git a/SessionZeroClient.Android/MainActivity.cs b/SessionZeroClient.Android/MainActivity.cs new file mode 100644 index 0000000..c94167a --- /dev/null +++ b/SessionZeroClient.Android/MainActivity.cs @@ -0,0 +1,21 @@ +using Android.App; +using Android.Content.PM; +using Avalonia; +using Avalonia.Android; + +namespace SessionZeroClient.Android; + +[Activity( + Label = "SessionZeroClient.Android", + Theme = "@style/MyTheme.NoActionBar", + Icon = "@drawable/icon", + MainLauncher = true, + ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)] +public class MainActivity : AvaloniaMainActivity +{ + protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) + { + return base.CustomizeAppBuilder(builder) + .WithInterFont(); + } +} \ No newline at end of file diff --git a/SessionZeroClient.Android/Properties/AndroidManifest.xml b/SessionZeroClient.Android/Properties/AndroidManifest.xml new file mode 100644 index 0000000..3294c69 --- /dev/null +++ b/SessionZeroClient.Android/Properties/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SessionZeroClient.Android/Resources/AboutResources.txt b/SessionZeroClient.Android/Resources/AboutResources.txt new file mode 100644 index 0000000..c2bca97 --- /dev/null +++ b/SessionZeroClient.Android/Resources/AboutResources.txt @@ -0,0 +1,44 @@ +Images, layout descriptions, binary blobs and string dictionaries can be included +in your application as resource files. Various Android APIs are designed to +operate on the resource IDs instead of dealing with images, strings or binary blobs +directly. + +For example, a sample Android app that contains a user interface layout (main.axml), +an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) +would keep its resources in the "Resources" directory of the application: + +Resources/ + drawable/ + icon.png + + layout/ + main.axml + + values/ + strings.xml + +In order to get the build system to recognize Android resources, set the build action to +"AndroidResource". The native Android APIs do not operate directly with filenames, but +instead operate on resource IDs. When you compile an Android application that uses resources, +the build system will package the resources for distribution and generate a class called "R" +(this is an Android convention) that contains the tokens for each one of the resources +included. For example, for the above Resources layout, this is what the R class would expose: + +public class R { + public class drawable { + public const int icon = 0x123; + } + + public class layout { + public const int main = 0x456; + } + + public class strings { + public const int first_string = 0xabc; + public const int second_string = 0xbcd; + } +} + +You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main +to reference the layout/main.axml file, or R.strings.first_string to reference the first +string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/SessionZeroClient.Android/Resources/drawable-night-v31/avalonia_anim.xml b/SessionZeroClient.Android/Resources/drawable-night-v31/avalonia_anim.xml new file mode 100644 index 0000000..dde4b5a --- /dev/null +++ b/SessionZeroClient.Android/Resources/drawable-night-v31/avalonia_anim.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SessionZeroClient.Android/Resources/drawable-v31/avalonia_anim.xml b/SessionZeroClient.Android/Resources/drawable-v31/avalonia_anim.xml new file mode 100644 index 0000000..94f27d9 --- /dev/null +++ b/SessionZeroClient.Android/Resources/drawable-v31/avalonia_anim.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SessionZeroClient.Android/Resources/drawable/splash_screen.xml b/SessionZeroClient.Android/Resources/drawable/splash_screen.xml new file mode 100644 index 0000000..2e920b4 --- /dev/null +++ b/SessionZeroClient.Android/Resources/drawable/splash_screen.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SessionZeroClient.Android/Resources/values-night/colors.xml b/SessionZeroClient.Android/Resources/values-night/colors.xml new file mode 100644 index 0000000..3d47b6f --- /dev/null +++ b/SessionZeroClient.Android/Resources/values-night/colors.xml @@ -0,0 +1,4 @@ + + + #212121 + diff --git a/SessionZeroClient.Android/Resources/values-v31/styles.xml b/SessionZeroClient.Android/Resources/values-v31/styles.xml new file mode 100644 index 0000000..d5ecec4 --- /dev/null +++ b/SessionZeroClient.Android/Resources/values-v31/styles.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/SessionZeroClient.Android/Resources/values/colors.xml b/SessionZeroClient.Android/Resources/values/colors.xml new file mode 100644 index 0000000..59279d5 --- /dev/null +++ b/SessionZeroClient.Android/Resources/values/colors.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + diff --git a/SessionZeroClient.Android/Resources/values/styles.xml b/SessionZeroClient.Android/Resources/values/styles.xml new file mode 100644 index 0000000..6e534de --- /dev/null +++ b/SessionZeroClient.Android/Resources/values/styles.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/SessionZeroClient.Android/SessionZeroClient.Android.csproj b/SessionZeroClient.Android/SessionZeroClient.Android.csproj new file mode 100644 index 0000000..acae508 --- /dev/null +++ b/SessionZeroClient.Android/SessionZeroClient.Android.csproj @@ -0,0 +1,28 @@ + + + Exe + net9.0-android + 21 + enable + com.CompanyName.SessionZeroClient + 1 + 1.0 + apk + false + + + + + Resources\drawable\Icon.png + + + + + + + + + + + + diff --git a/SessionZeroClient.Browser/Program.cs b/SessionZeroClient.Browser/Program.cs new file mode 100644 index 0000000..66676cf --- /dev/null +++ b/SessionZeroClient.Browser/Program.cs @@ -0,0 +1,15 @@ +using System.Runtime.Versioning; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Browser; +using SessionZeroClient; + +internal sealed partial class Program +{ + private static Task Main(string[] args) => BuildAvaloniaApp() + .WithInterFont() + .StartBrowserAppAsync("out"); + + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure(); +} \ No newline at end of file diff --git a/SessionZeroClient.Browser/Properties/AssemblyInfo.cs b/SessionZeroClient.Browser/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f31aed8 --- /dev/null +++ b/SessionZeroClient.Browser/Properties/AssemblyInfo.cs @@ -0,0 +1 @@ +[assembly: System.Runtime.Versioning.SupportedOSPlatform("browser")] \ No newline at end of file diff --git a/SessionZeroClient.Browser/Properties/launchSettings.json b/SessionZeroClient.Browser/Properties/launchSettings.json new file mode 100644 index 0000000..3a4c5f5 --- /dev/null +++ b/SessionZeroClient.Browser/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "SessionZeroClient.Browser": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:7169;http://localhost:5235", + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" + } + } +} diff --git a/SessionZeroClient.Browser/SessionZeroClient.Browser.csproj b/SessionZeroClient.Browser/SessionZeroClient.Browser.csproj new file mode 100644 index 0000000..3845346 --- /dev/null +++ b/SessionZeroClient.Browser/SessionZeroClient.Browser.csproj @@ -0,0 +1,16 @@ + + + net9.0-browser + Exe + true + enable + + + + + + + + + + diff --git a/SessionZeroClient.Browser/runtimeconfig.template.json b/SessionZeroClient.Browser/runtimeconfig.template.json new file mode 100644 index 0000000..b96a943 --- /dev/null +++ b/SessionZeroClient.Browser/runtimeconfig.template.json @@ -0,0 +1,10 @@ +{ + "wasmHostProperties": { + "perHostConfig": [ + { + "name": "browser", + "host": "browser" + } + ] + } +} \ No newline at end of file diff --git a/SessionZeroClient.Browser/wwwroot/app.css b/SessionZeroClient.Browser/wwwroot/app.css new file mode 100644 index 0000000..1d6f754 --- /dev/null +++ b/SessionZeroClient.Browser/wwwroot/app.css @@ -0,0 +1,58 @@ +/* HTML styles for the splash screen */ +.avalonia-splash { + position: absolute; + height: 100%; + width: 100%; + background: white; + font-family: 'Outfit', sans-serif; + justify-content: center; + align-items: center; + display: flex; + pointer-events: none; +} + +/* Light theme styles */ +@media (prefers-color-scheme: light) { + .avalonia-splash { + background: white; + } + + .avalonia-splash h2 { + color: #1b2a4e; + } + + .avalonia-splash a { + color: #0D6EFD; + } +} + +@media (prefers-color-scheme: dark) { + .avalonia-splash { + background: #1b2a4e; + } + + .avalonia-splash h2 { + color: white; + } + + .avalonia-splash a { + color: white; + } +} + +.avalonia-splash h2 { + font-weight: 400; + font-size: 1.5rem; +} + +.avalonia-splash a { + text-decoration: none; + font-size: 2.5rem; + display: block; +} + +.avalonia-splash.splash-close { + transition: opacity 200ms, display 200ms; + display: none; + opacity: 0; +} diff --git a/SessionZeroClient.Browser/wwwroot/favicon.ico b/SessionZeroClient.Browser/wwwroot/favicon.ico new file mode 100644 index 0000000..f7da8bb Binary files /dev/null and b/SessionZeroClient.Browser/wwwroot/favicon.ico differ diff --git a/SessionZeroClient.Browser/wwwroot/index.html b/SessionZeroClient.Browser/wwwroot/index.html new file mode 100644 index 0000000..13e1a8a --- /dev/null +++ b/SessionZeroClient.Browser/wwwroot/index.html @@ -0,0 +1,36 @@ + + + + + SessionZeroClient.Browser + + + + + + +
+
+

+ Powered by + + + + + + + + + + + + + + +

+
+
+ + + + diff --git a/SessionZeroClient.Browser/wwwroot/main.js b/SessionZeroClient.Browser/wwwroot/main.js new file mode 100644 index 0000000..bf1555e --- /dev/null +++ b/SessionZeroClient.Browser/wwwroot/main.js @@ -0,0 +1,13 @@ +import { dotnet } from './_framework/dotnet.js' + +const is_browser = typeof window != "undefined"; +if (!is_browser) throw new Error(`Expected to be running in a browser`); + +const dotnetRuntime = await dotnet + .withDiagnosticTracing(false) + .withApplicationArgumentsFromQuery() + .create(); + +const config = dotnetRuntime.getConfig(); + +await dotnetRuntime.runMain(config.mainAssemblyName, [globalThis.location.href]); diff --git a/SessionZeroClient.Desktop/Program.cs b/SessionZeroClient.Desktop/Program.cs new file mode 100644 index 0000000..5bd8689 --- /dev/null +++ b/SessionZeroClient.Desktop/Program.cs @@ -0,0 +1,21 @@ +using System; +using Avalonia; + +namespace SessionZeroClient.Desktop; + +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() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace(); +} \ No newline at end of file diff --git a/SessionZeroClient.Desktop/SessionZeroClient.Desktop.csproj b/SessionZeroClient.Desktop/SessionZeroClient.Desktop.csproj new file mode 100644 index 0000000..534be85 --- /dev/null +++ b/SessionZeroClient.Desktop/SessionZeroClient.Desktop.csproj @@ -0,0 +1,27 @@ + + + WinExe + + net9.0 + enable + true + + + + app.manifest + + + + + + + None + All + + + + + + + diff --git a/SessionZeroClient.Desktop/app.manifest b/SessionZeroClient.Desktop/app.manifest new file mode 100644 index 0000000..bc0c0ca --- /dev/null +++ b/SessionZeroClient.Desktop/app.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/SessionZeroClient.iOS/AppDelegate.cs b/SessionZeroClient.iOS/AppDelegate.cs new file mode 100644 index 0000000..9dae0d2 --- /dev/null +++ b/SessionZeroClient.iOS/AppDelegate.cs @@ -0,0 +1,23 @@ +using Foundation; +using UIKit; +using Avalonia; +using Avalonia.Controls; +using Avalonia.iOS; +using Avalonia.Media; + +namespace SessionZeroClient.iOS; + +// The UIApplicationDelegate for the application. This class is responsible for launching the +// User Interface of the application, as well as listening (and optionally responding) to +// application events from iOS. +[Register("AppDelegate")] +#pragma warning disable CA1711 // Identifiers should not have incorrect suffix +public partial class AppDelegate : AvaloniaAppDelegate +#pragma warning restore CA1711 // Identifiers should not have incorrect suffix +{ + protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) + { + return base.CustomizeAppBuilder(builder) + .WithInterFont(); + } +} \ No newline at end of file diff --git a/SessionZeroClient.iOS/Entitlements.plist b/SessionZeroClient.iOS/Entitlements.plist new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/SessionZeroClient.iOS/Entitlements.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/SessionZeroClient.iOS/Info.plist b/SessionZeroClient.iOS/Info.plist new file mode 100644 index 0000000..3c17833 --- /dev/null +++ b/SessionZeroClient.iOS/Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDisplayName + SessionZeroClient + CFBundleIdentifier + companyName.SessionZeroClient + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + MinimumOSVersion + 13.0 + UIDeviceFamily + + 1 + 2 + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/SessionZeroClient.iOS/Main.cs b/SessionZeroClient.iOS/Main.cs new file mode 100644 index 0000000..6028491 --- /dev/null +++ b/SessionZeroClient.iOS/Main.cs @@ -0,0 +1,14 @@ +using UIKit; + +namespace SessionZeroClient.iOS; + +public class Application +{ + // This is the main entry point of the application. + static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + UIApplication.Main(args, null, typeof(AppDelegate)); + } +} \ No newline at end of file diff --git a/SessionZeroClient.iOS/Resources/LaunchScreen.xib b/SessionZeroClient.iOS/Resources/LaunchScreen.xib new file mode 100644 index 0000000..e532d53 --- /dev/null +++ b/SessionZeroClient.iOS/Resources/LaunchScreen.xib @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SessionZeroClient.iOS/SessionZeroClient.iOS.csproj b/SessionZeroClient.iOS/SessionZeroClient.iOS.csproj new file mode 100644 index 0000000..86b1d3a --- /dev/null +++ b/SessionZeroClient.iOS/SessionZeroClient.iOS.csproj @@ -0,0 +1,16 @@ + + + Exe + net9.0-ios + 13.0 + enable + + + + + + + + + + diff --git a/SessionZeroClient.sln b/SessionZeroClient.sln new file mode 100644 index 0000000..2c3cafe --- /dev/null +++ b/SessionZeroClient.sln @@ -0,0 +1,54 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32811.315 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SessionZeroClient", "SessionZeroClient\SessionZeroClient.csproj", "{EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SessionZeroClient.Desktop", "SessionZeroClient.Desktop\SessionZeroClient.Desktop.csproj", "{ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SessionZeroClient.Browser", "SessionZeroClient.Browser\SessionZeroClient.Browser.csproj", "{1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SessionZeroClient.iOS", "SessionZeroClient.iOS\SessionZeroClient.iOS.csproj", "{EBD9022F-BC83-4846-9A11-6F7F3772DC64}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SessionZeroClient.Android", "SessionZeroClient.Android\SessionZeroClient.Android.csproj", "{7AD1DAC8-7FBE-49D5-8614-7321233DB82E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3DA99C4E-89E3-4049-9C22-0A7EC60D83D8}" + ProjectSection(SolutionItems) = preProject + Directory.Packages.props = Directory.Packages.props + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Release|Any CPU.Build.0 = Release|Any CPU + {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Release|Any CPU.Build.0 = Release|Any CPU + {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Release|Any CPU.Build.0 = Release|Any CPU + {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Release|Any CPU.Build.0 = Release|Any CPU + {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {83CB65B8-011F-4ED7-BCD3-A6CFA935EF7E} + EndGlobalSection +EndGlobal diff --git a/SessionZeroClient/App.axaml b/SessionZeroClient/App.axaml new file mode 100644 index 0000000..e1d3358 --- /dev/null +++ b/SessionZeroClient/App.axaml @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/SessionZeroClient/App.axaml.cs b/SessionZeroClient/App.axaml.cs new file mode 100644 index 0000000..78772dc --- /dev/null +++ b/SessionZeroClient/App.axaml.cs @@ -0,0 +1,54 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Data.Core; +using Avalonia.Data.Core.Plugins; +using System.Linq; +using Avalonia.Markup.Xaml; +using SessionZeroClient.ViewModels; +using SessionZeroClient.Views; + +namespace SessionZeroClient; + +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 MainViewModel() + }; + } + else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform) + { + singleViewPlatform.MainView = new MainView + { + DataContext = new MainViewModel() + }; + } + + base.OnFrameworkInitializationCompleted(); + } + + private void DisableAvaloniaDataAnnotationValidation() + { + // Get an array of plugins to remove + var dataValidationPluginsToRemove = + BindingPlugins.DataValidators.OfType().ToArray(); + + // remove each entry found + foreach (var plugin in dataValidationPluginsToRemove) + { + BindingPlugins.DataValidators.Remove(plugin); + } + } +} \ No newline at end of file diff --git a/SessionZeroClient/Assets/avalonia-logo.ico b/SessionZeroClient/Assets/avalonia-logo.ico new file mode 100644 index 0000000..f7da8bb Binary files /dev/null and b/SessionZeroClient/Assets/avalonia-logo.ico differ diff --git a/SessionZeroClient/SessionZeroClient.csproj b/SessionZeroClient/SessionZeroClient.csproj new file mode 100644 index 0000000..b0d8c33 --- /dev/null +++ b/SessionZeroClient/SessionZeroClient.csproj @@ -0,0 +1,24 @@ + + + net9.0 + enable + latest + true + + + + + + + + + + + + + None + All + + + + diff --git a/SessionZeroClient/ViewLocator.cs b/SessionZeroClient/ViewLocator.cs new file mode 100644 index 0000000..bcf05df --- /dev/null +++ b/SessionZeroClient/ViewLocator.cs @@ -0,0 +1,30 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using SessionZeroClient.ViewModels; + +namespace SessionZeroClient; + +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; + } +} \ No newline at end of file diff --git a/SessionZeroClient/ViewModels/MainViewModel.cs b/SessionZeroClient/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..0e19c96 --- /dev/null +++ b/SessionZeroClient/ViewModels/MainViewModel.cs @@ -0,0 +1,8 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SessionZeroClient.ViewModels; + +public partial class MainViewModel : ViewModelBase +{ + [ObservableProperty] private string _greeting = "Welcome to Avalonia!"; +} \ No newline at end of file diff --git a/SessionZeroClient/ViewModels/ViewModelBase.cs b/SessionZeroClient/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..a314609 --- /dev/null +++ b/SessionZeroClient/ViewModels/ViewModelBase.cs @@ -0,0 +1,7 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SessionZeroClient.ViewModels; + +public abstract class ViewModelBase : ObservableObject +{ +} \ No newline at end of file diff --git a/SessionZeroClient/Views/MainView.axaml b/SessionZeroClient/Views/MainView.axaml new file mode 100644 index 0000000..739e204 --- /dev/null +++ b/SessionZeroClient/Views/MainView.axaml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/SessionZeroClient/Views/MainView.axaml.cs b/SessionZeroClient/Views/MainView.axaml.cs new file mode 100644 index 0000000..e655286 --- /dev/null +++ b/SessionZeroClient/Views/MainView.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace SessionZeroClient.Views; + +public partial class MainView : UserControl +{ + public MainView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/SessionZeroClient/Views/MainWindow.axaml b/SessionZeroClient/Views/MainWindow.axaml new file mode 100644 index 0000000..96b46c5 --- /dev/null +++ b/SessionZeroClient/Views/MainWindow.axaml @@ -0,0 +1,12 @@ + + + diff --git a/SessionZeroClient/Views/MainWindow.axaml.cs b/SessionZeroClient/Views/MainWindow.axaml.cs new file mode 100644 index 0000000..47d473c --- /dev/null +++ b/SessionZeroClient/Views/MainWindow.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace SessionZeroClient.Views; + +public partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..edac3b0 --- /dev/null +++ b/readme.md @@ -0,0 +1,273 @@ +# SessionZero Design Document + +## 1. Introduction + +### 1.1 Purpose + +SessionZero is a free and open-source TTRPG companion app designed to streamline tabletop role-playing game management. It emphasizes flexibility, modularity, and federation through a system-agnostic datapack format, supporting both offline and self-hostable online play. The goal is to empower players and GMs with full control over their data, rulesets, and session management. + +### 1.2 Scope + +The project will begin as a cross-platform desktop application (Windows and Linux), offering tools for creating and managing TTRPG data such as characters, datasets, templates, and sessions. The backend supports user accounts, a database for shared content (SessionZeroDB), and prepares for future federation capabilities. Phase 2 introduces federation, while Phase 3 focuses on mobile support and plugin extensibility. + +### 1.3 Objectives + +* Provide a modular, open-source companion app for any TTRPG system. +* Implement a JSON-based datapack format for extensible content storage. +* Support both local use and self-hostable online instances. +* Create a foundation for future federation and mobile support. + +--- + +## 2. System Overview + +SessionZero consists of three main layers: + +1. **Frontend (Client):** Cross-platform desktop application (.NET Avalonia). +2. **Backend (Server):** ASP.NET backend managing user accounts, SessionZeroDB, and P2P connections. +3. **Datapack System:** A structured archive format for storing and sharing game data. + +--- + +## 3. Functional Requirements + +### 3.1 Core Features (Phase 1) + +* **User Authentication:** Email and password-based account creation and login. +* **Datapack Management:** + * Create, edit, and package datapacks. + * Import/export datapacks. + * Link media assets (e.g., images) within datapacks. + +* **Data Creation/Editing:** + * Datasets, characters, character templates, and session templates. + +* **Session Management:** + * GM control over player sheets and session datapacks. + * Player view limited to their own characters. + * Integrated chat/log system for simple in-session communication. + +* **SessionZeroDB:** + * Database for hosting and retrieving community content. + * Attribution and license information for each uploaded pack. + +### 3.2 Federation (Phase 2) + +* Federated servers can communicate for global content discovery. +* Configurable access controls for visibility (private, public, or indexed). +* P2P session management. + +### 3.3 Mobile/Plugin System (Phase 3) + +* Native mobile clients for Android/iOS. +* Plugin architecture for extending UI and data management capabilities. + +--- + +## 4. Non-Functional Requirements + +* **Cross-Platform Support:** Windows and Linux for MVP. +* **Performance:** Efficient JSON parsing and fast local caching. +* **Scalability:** Designed for multiple users per server instance. +* **Security:** Hashed passwords, secure token authentication. +* **Data Portability:** All user data exportable in open formats. + +--- + +## 5. Datapack Specification + +### 5.1 Structure + +``` +pack-name/ +├── szpack.json # Metadata +├── media/ +│ └── images/ # Image assets +└── objects/ + ├── datasets/ + ├── characters/ + ├── character-templates/ + └── session-templates/ +``` + +### 5.2 Metadata File (`szpack.json`) + +Example: + +```json +{ + "name": "Example Pack", + "version": "1.0", + "author": "UserName", + "license": "CC-BY-SA 4.0", + "description": "A fantasy setting for SessionZero.", + "created": "2025-10-15", + "sessionzeroVersion": "1.0" +} +``` + +### 5.3 Object Format Example + +```json +{ + "type": "character", + "name": "Thalindra", + "attributes": { + "strength": 14, + "intelligence": 12, + "dexterity": 10 + }, + "image": "../media/images/thalindra.png" +} +``` + +--- + +## 6. Architecture + +### 6.1 High-level Overview + +SessionZero is split into three cooperating layers: + +* **Client (Avalonia / .NET 9)** - the primary desktop application used by players and GMs. It is responsible for local data storage, datapack management, UI rendering, and establishing/maintaining real-time session connections. The client is offline-first and can run entirely without a backend. + +* **Backend (ASP.NET Core Web API)** - an optional, self-hostable server that provides account management, persistent session storage, the SessionZeroDB (index of community datapacks), and signaling for P2P connections (WebRTC). Servers may be run privately or publicly and can optionally federate with other servers. + +* **Federation Layer / SessionZeroDB** - a network of self-hosted servers that optionally synchronize metadata (datapack manifests, public session listings, user profiles) between instances. Federation is opt-in per server and respects server-configured visibility and access controls. + +### 6.2 Client Architecture (Avalonia) + +**Key responsibilities:** + +* Local datastore and caching (SQLite for structured local data, filesystem for datapacks and media). +* Datapack Manager: discovery, validation, import/export (.szp), and in-memory registry of loaded objects. +* UI layer (MVVM pattern) using Avalonia controls and styles. +* Networking components: + * SignalR client for communicating with an ASP.NET backend. + * WebRTC peer connection subsystem for direct P2P session data transfer (signaling via backend when needed). +* Tools integration: a CLI helper (`szpack`) for building/extracting packs and optional pack signing/verification. + +**Runtime layout:** + +* **ViewModels:** represent datapacks, objects, sessions, and user state. +* **Services:** DataPackService, StorageService (SQLite/File), NetworkingService (SignalR/WebRTC), AuthService, LoggingService. + +### 6.3 Backend Architecture (ASP.NET Core) + +**Key responsibilities:** + +* User account management (email/password auth, JWT tokens). +* Session hosting and persistence (store session metadata and optional session snapshots in PostgreSQL). +* SessionZeroDB: index and serve community datapacks and metadata; host uploaded `.szp` files on disk or object storage. +* Signaling service for WebRTC: setup offers/answers and ICE candidate exchange to enable P2P connections between clients. +* Federation endpoints for inter-server communication (signed JSON over HTTPS). + +**Components:** + +* **REST API controllers:** Accounts, Sessions, Datapacks, Federation, Admin +* **SignalR Hubs:** Real-time session coordination and lightweight session synchronization for clients that prefer server-mediated sync. +* **Storage:** PostgreSQL for server metadata and user accounts; filesystem/object storage for datapack blobs; optional S3-compatible storage for large deployments. +* **Auth & Security:** Password hashing (Argon2), JWT issuance and refresh, TLS for all endpoints, RBAC for admin operations. + +### 6.4 Networking Model + +* **Offline Mode:** Client runs entirely locally; datapacks and session files live on the local filesystem / SQLite; no networking required. + +* **LAN / P2P Mode:** Clients can host sessions on a LAN. Discovery via mDNS/Zeroconf or optional UDP broadcast. For internet play, the backend's signaling service facilitates NAT traversal and WebRTC handshake. Actual session traffic (character updates, chat) is sent peer-to-peer where possible to minimize server load. + +* **Server-Hosted Mode:** For users who run or subscribe to an ASP.NET instance, sessions may be persisted on the server and clients can reconnect to continue campaigns. The server can optionally mediate real-time traffic for clients that cannot form direct P2P connections. + +* **Federation:** Servers exchange signed metadata (pack manifests, publicly shared pack indexes, optionally session discovery) on a scheduled or event-driven basis. Federation rules and visibility are configured by each server operator. + +### 6.5 Data Flow Examples + +**Importing a Datapack (Client-only):** + +1. User selects `Import pack.szp`. +2. Client extracts the archive into the local datapacks directory. +3. DataPackService validates `szpack.json` and object schemas and registers objects into the in-memory registry. +4. The UI updates to show new templates and datasets. + +**Joining a Hosted Session (Server + P2P):** + +1. Client authenticates to ASP.NET server via email/password, obtains JWT. +2. Client requests to join a session; server verifies permissions and returns session metadata and peer list. +3. Server provides WebRTC signaling data (or the client uses STUN/TURN if needed). +4. Clients establish P2P channels and synchronize session state. Server stores session snapshots as needed. + +### 6.6 Extensibility & Plugins + +* Design client services around interfaces to permit replacement or extension (e.g., IDataPackStore, INetworkingTransport). +* Plugin API (Phase 3) will allow third-party modules to register new object types, UI panels, and data transformations while running within a sandboxed environment. + +### 6.7 Deployment & Operations + +* **Client:** Distributed via platform-native packaging (AppImage/Flatpak for Linux, MSI for Windows). Continuous builds via GitHub Actions. +* **Server:** Dockerized ASP.NET image with environment-driven configuration. Use PostgreSQL as the recommended production datastore and SQLite for small personal deployments. +* **SessionZeroDB:** Each server can host its own repository; the official public index is recommended to be a well-maintained instance but is optional. + +## 7. UI/UX Design + +### 7.1 Philosophy + +* Desktop-first layout with responsive adjustments. +* Blend of creative and functional design to support immersive TTRPG use. + +### 7.2 Core Views + +* Dashboard +* Datapack Manager +* Data Editor (characters, templates, datasets) +* Session View (GM & Player modes) +* Chat/Log interface + +--- + +## 8. Licensing + +### 8.1 Software License + +* **GNU Affero General Public License (AGPL-3.0)** - ensures open-source continuity and attribution, prevents uncredited commercial rebranding. + +### 8.2 User Content License + +* **Creative Commons BY-SA 4.0** - allows sharing and adaptation with attribution. + +--- + +## 9. Development Phases + +### Phase 1: Core Platform (MVP) + +* Client (UI, datapack editing, session management) +* Backend (accounts, SessionZeroDB, P2P prep) +* JSON datapack format implementation + +### Phase 2: Federation + +* Cross-server communication +* Access controls and search indexing + +### Phase 3: Mobile and Plugins + +* Cross-platform mobile apps +* Plugin and API extensibility + +--- + +## 10. Open Questions + +1. Should we support local single-user mode without an account? +2. How should dependency handling between datapacks work (e.g., one datapack relying on another)? +3. Should we include an optional offline mode for play sessions without a backend? +4. How should session persistence be handled for ongoing campaigns? + +--- + +## 11. Next Steps + +1. Confirm open questions above. +2. Create schema definitions for datapack object types. +3. Design backend API endpoints. +4. Create wireframes for client UI. +5. Begin core MVP development (client + backend).