The single HostStatusText/JoinStatusText line gave no sense of where
the user was in the flow. Replaced with a step list:
- StartupStep model (INotifyPropertyChanged) tracks Title, State
(Pending/Active/Done/Failed), Detail, optional Progress 0..1
- ItemsControl + DataTemplate in App.xaml renders each step as a
status bullet (empty ring -> filled brand -> green check -> red X),
with conditional detail line + restyled mini progress bar
- StartupFlow.NewHostFlow / NewJoinFlow build the canonical 5-step
host and 2-step join lists; host flow swaps the tunnel step label
for LocalNetwork mode
- OnHostStart / OnJoinApply advance steps via StartStep/CompleteStep/
FailStep helpers and route progress callbacks to UpdateStepProgress
so each download phase has its own bar
- Errors mark the active step Failed with the message inline, leaving
the rest of the list visible so the user sees where it broke
- RecRoomPicker.Detect: scan common manual-install paths (Documents/Recnet/dist, Downloads, C:\Games, etc.) on first run; never touches Steam libraries per policy
- RecRoomVersionDetector: three-signal cascade (version.txt -> PE timestamp -> file size) maps install to a manifest branch so version dropdown defaults correctly
- CloudflaredInstaller: download latest cloudflared-windows-amd64.exe from GitHub releases to %LOCALAPPDATA%\DorkNet on first host; resolves side-by-side -> cache -> PATH -> download
- LocalNetwork.GetLanIp + AppState.HostingMode (Internet|LocalNetwork) + Host view "WHO CAN JOIN?" radio: LAN-only mode skips Cloudflare entirely for families on one network
- OnHostStart branches on HostingMode; status text reflects local IP vs public tunnel URL
DorkNet.ClientMod's config reads PhotonCloudRegion alongside the
AppIds, and the joining client needs the host's region to connect
to the right Photon cloud. Adds {PHOTON_REGION} to the substitution
map and threads region through ClientPatcher.ApplyAsync from both
host (AppState.PhotonRegion) and join (JoinPayload.PhotonRegion)
flows.
UI redesign:
- Warm coral + teal palette over deep purple-navy (closer to Rec Room's
marketing aesthetic). New wordmark gradient (coral -> pink -> teal).
- Chunky 18px rounded cards on the first-run wizard with per-mode
accent tinting (coral for host, teal for join) and clear CTAs.
- Form fields wrapped in elevated panels with sectional titles + pill
status chips in the header (HOST MODE / JOIN MODE / SETTINGS).
- Drop-shadow effects on primary buttons + cards for depth.
Patcher rewrite:
- ClientPatcher.cs no longer shells out to PowerShell; the launcher
unzips MelonLoader, copies the DorkNet plugin DLL, and renders the
user-data config template — all in C# in-process.
- Patcher zip now driven by a manifest.json at root (schema v1).
Per-version branches publish: manifest.json + MelonLoader.zip +
DorkNet.ClientMod.dll + config template.
- RELEASES.md updated with the new MelonLoader manifest contract and
the placeholder substitution rules.
Rec Room launch path:
- New Backend/RecRoomLauncher.cs runs Recroom_Release.exe directly
from the picked install folder's parent dir (no Steam).
- 'Launch Rec Room' button appears after a successful patch in both
host + join modes.
Initial v0.1 (commit df9dac0) used PhotinoNET wrapping WebView2 + HTML/JS,
but rendered as a blank window on first run — WebView2 + file:// URI
quirks. Switching to native WPF: built into the .NET 9 Windows SDK
(no NuGet), single-file self-contained publish, instant window open.
All Backend/*.cs orchestration logic carried over unchanged (they're
UI-framework-agnostic). Replaced:
- Program.cs (PhotinoNET bootstrap) -> App.xaml(.cs) + MainWindow.xaml(.cs)
- ui/index.html + style.css + app.js -> MainWindow.xaml (XAML views) +
App.xaml resource styles
- Backend/MessageBridge.cs (JS<->C# router) -> direct method calls in
MainWindow.xaml.cs code-behind
RecRoomPicker now uses Microsoft.Win32.OpenFolderDialog (added in .NET 8)
instead of WinForms FolderBrowserDialog — keeps the project WPF-only.
Backend files needed explicit 'using System.IO;' since the WPF SDK
doesn't include it in implicit usings.