Full Code of Ashfaaq18/OpenNetMeter for AI

main d94ec569cb39 cached
154 files
698.1 KB
153.2k tokens
667 symbols
1 requests
Download .txt
Showing preview only (747K chars total). Download the full file or copy to clipboard to get everything.
Repository: Ashfaaq18/OpenNetMeter
Branch: main
Commit: d94ec569cb39
Files: 154
Total size: 698.1 KB

Directory structure:
gitextract_0pgbfx2f/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       └── dotnet.yml
├── .gitignore
├── .vscode/
│   └── launch.json
├── Directory.Build.props
├── Installer/
│   ├── OpenNetMeter-Installer.wixproj
│   ├── Product.wxs
│   ├── Strings_en-us.wxl
│   └── WixUIFeatureTree.wxs
├── LICENSE
├── NOTICE
├── OpenNetMeter/
│   ├── App.axaml
│   ├── App.axaml.cs
│   ├── Compat/
│   │   ├── Models/
│   │   │   ├── ApplicationDB.cs
│   │   │   ├── MyProcess_Small.cs
│   │   │   ├── NetworkProcess.TestHooks.cs
│   │   │   └── NetworkProcess.cs
│   │   ├── Properties/
│   │   │   ├── AppSettings.cs
│   │   │   ├── Global.cs
│   │   │   └── SettingsManager.cs
│   │   └── Utilities/
│   │       ├── ByteArray.cs
│   │       ├── EventLogger.cs
│   │       └── PeriodicWork.cs
│   ├── OpenNetMeter.csproj
│   ├── Program.cs
│   ├── Services/
│   │   ├── AvaloniaThemeService.cs
│   │   ├── AvaloniaWindowService.cs
│   │   ├── ExternalLinkService.cs
│   │   ├── IMiniWidgetService.cs
│   │   ├── IThemeService.cs
│   │   ├── ITrayNotificationService.cs
│   │   ├── ITrayService.cs
│   │   ├── NoOpThemeService.cs
│   │   ├── PlaceholderMiniWidgetService.cs
│   │   ├── PlaceholderNetworkCaptureService.cs
│   │   ├── PlaceholderProcessIconService.cs
│   │   ├── PlaceholderStartupRegistrationService.cs
│   │   ├── PlaceholderTrayNotificationService.cs
│   │   ├── PlaceholderTrayService.cs
│   │   ├── UpdateChecker.cs
│   │   ├── WindowsMiniWidgetService.cs
│   │   ├── WindowsNetworkCaptureService.cs
│   │   ├── WindowsProcessIconService.cs
│   │   ├── WindowsStartupRegistrationService.cs
│   │   ├── WindowsTrayNotificationService.cs
│   │   ├── WindowsTrayService.cs
│   │   └── WindowsWidgetZOrderHelper.cs
│   ├── ViewModels/
│   │   ├── ByteSizeFormatter.cs
│   │   ├── HistoryViewModel.cs
│   │   ├── MainWindowViewModel.cs
│   │   ├── MiniWidgetViewModel.cs
│   │   ├── RelayCommand.cs
│   │   ├── SettingsViewModel.cs
│   │   └── SummaryViewModel.cs
│   ├── Views/
│   │   ├── MainWindow/
│   │   │   ├── History.axaml
│   │   │   ├── History.axaml.cs
│   │   │   ├── Settings.axaml
│   │   │   ├── Settings.axaml.cs
│   │   │   ├── Summary.axaml
│   │   │   └── Summary.axaml.cs
│   │   ├── MainWindow.axaml
│   │   ├── MainWindow.axaml.cs
│   │   ├── MiniWidgetWindow.axaml
│   │   ├── MiniWidgetWindow.axaml.cs
│   │   └── Themes.axaml
│   └── app.manifest
├── OpenNetMeter.Core/
│   ├── OpenNetMeter.Core.csproj
│   └── ViewModels/
│       ├── ConfirmationDialogVM.cs
│       └── MainShellTabsViewModel.cs
├── OpenNetMeter.Old/
│   ├── DatabaseEngine/
│   │   ├── Connection.cs
│   │   ├── Database.cs
│   │   └── DatabaseEngine.csproj
│   └── OpenNetMeter/
│       ├── App.xaml
│       ├── App.xaml.cs
│       ├── AssemblyInfo.cs
│       ├── Models/
│       │   ├── ApplicationDB.cs
│       │   ├── MyProcess.cs
│       │   ├── NativeMethods.cs
│       │   ├── NetworkProcess.TestHooks.cs
│       │   ├── NetworkProcess.cs
│       │   └── SpeedGraph.cs
│       ├── OpenNetMeter.Old.csproj
│       ├── Properties/
│       │   ├── AppSettings.cs
│       │   ├── Global.cs
│       │   ├── InternalsVisibleTo.cs
│       │   ├── Resources.Designer.cs
│       │   ├── Resources.resx
│       │   └── SettingsManager.cs
│       ├── Utilities/
│       │   ├── BaseCommand.cs
│       │   ├── ByteArray.cs
│       │   ├── DataSizeSuffix.cs
│       │   ├── EventLogger.cs
│       │   ├── IconToImgSource.cs
│       │   ├── JsonHelper.cs
│       │   ├── PeriodicWork.cs
│       │   ├── ProcessIconCache.cs
│       │   ├── UIMeasure.cs
│       │   ├── UpdateChecker.cs
│       │   ├── WindowsNetworkCaptureService.cs
│       │   ├── WindowsProcessIconService.cs
│       │   ├── WindowsStartupRegistrationService.cs
│       │   └── WpfUiDispatcher.cs
│       ├── ViewModels/
│       │   ├── DataUsageHistoryVM.cs
│       │   ├── DataUsageSummaryVM.cs
│       │   ├── MainWindowVM.cs
│       │   ├── MiniWidgetVM.cs
│       │   └── SettingsVM.cs
│       ├── Views/
│       │   ├── AboutWindow.xaml
│       │   ├── AboutWindow.xaml.cs
│       │   ├── ConfirmationDialog.xaml
│       │   ├── ConfirmationDialog.xaml.cs
│       │   ├── Converters/
│       │   │   ├── BitmapToImageConverter.cs
│       │   │   ├── NetSpeedFormatConverter.cs
│       │   │   ├── RadioBoolToIntConverter.cs
│       │   │   ├── UiVisibilityToWpfVisibilityConverter.cs
│       │   │   └── UnitConverterBytes.cs
│       │   ├── CustomSystemTray.cs
│       │   ├── MainWindow.xaml
│       │   ├── MainWindow.xaml.cs
│       │   ├── MainWindowTabs/
│       │   │   ├── DataUsageHistoryV.xaml
│       │   │   ├── DataUsageHistoryV.xaml.cs
│       │   │   ├── DataUsageSummaryV.xaml
│       │   │   ├── DataUsageSummaryV.xaml.cs
│       │   │   ├── SettingsV.xaml
│       │   │   └── SettingsV.xaml.cs
│       │   ├── MiniWidgetV.xaml
│       │   ├── MiniWidgetV.xaml.cs
│       │   └── ResourceDictionaries/
│       │       ├── CustomDatePicker.xaml
│       │       ├── Theme.Colors.xaml
│       │       ├── Theme.ComboBox.xaml
│       │       ├── Theme.ContextMenu.xaml
│       │       ├── Theme.DataGrid.xaml
│       │       ├── Theme.MainWindowTabs.xaml
│       │       ├── Theme.Styles.xaml
│       │       ├── Theme.SummaryPage.xaml
│       │       └── ThemeResources.xaml
│       └── app.manifest
├── OpenNetMeter.PlatformAbstractions/
│   ├── IExternalLinkService.cs
│   ├── INetworkCaptureService.cs
│   ├── IProcessIconService.cs
│   ├── IStartupRegistrationService.cs
│   ├── IUiDispatcher.cs
│   ├── IWindowService.cs
│   ├── OpenNetMeter.PlatformAbstractions.csproj
│   └── UiVisibility.cs
├── OpenNetMeter.Tests/
│   ├── NetworkProcessTests.cs
│   └── OpenNetMeter.Tests.csproj
├── OpenNetMeter.sln
├── README.md
├── Resources/
│   └── documentation/
│       └── README.md
└── scripts/
    ├── build-avalonia-msi.ps1
    └── publish-avalonia-rc.ps1

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: ashfaaq18
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**App and Windows Version (please complete the following information):**
 - Windows Specs: [Select Start > Settings > System > About. Open About settings.]
 - OpenNetMeter Version: 

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/workflows/dotnet.yml
================================================
name: Build OpenNetMeter MSI and Push to Single Draft Release

on:
  workflow_dispatch:

permissions:
  contents: write

jobs:
  draft-msi:
    runs-on: windows-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup .NET 8
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: "8.0.x"

      - name: Build Avalonia MSI
        shell: pwsh
        run: |
          powershell -ExecutionPolicy Bypass -File .\scripts\build-avalonia-msi.ps1 -Runtime win-x64 -Configuration Release

      - name: Find built MSI
        id: find_msi
        shell: pwsh
        run: |
          $msi = Get-ChildItem `
            "$Env:GITHUB_WORKSPACE\Installer\bin" `
            -Filter 'OpenNetMeter-*.msi' `
            -Recurse `
            | Sort-Object LastWriteTimeUtc -Descending `
            | Select-Object -First 1
          if (-not $msi) { throw 'No MSI found after build.' }

          "msi_path=$($msi.FullName)" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf8 -Append

      - name: Create or Update Draft Release and Upload MSI
        uses: ncipollo/release-action@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          tag: opennetmeter-draft
          name: OpenNetMeter (Draft Release)
          draft: true
          allowUpdates: true
          artifacts: ${{ steps.find_msi.outputs.msi_path }}

      - name: Echo MSI path
        shell: pwsh
        env:
          MSI_PATH: ${{ steps.find_msi.outputs.msi_path }}
        run: |
          Write-Host "Built MSI path was: $Env:MSI_PATH"


================================================
FILE: .gitignore
================================================
## 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/
[Oo]ut/
[Ll]og/
[Ll]ogs/
_rc/
_verify_build/

# 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/

# 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

#My stuff
MyStuff/

================================================
FILE: .vscode/launch.json
================================================
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": ".NET_CoreAttach",
            "type": "coreclr",
            "request": "attach"
        }
    ]
}

================================================
FILE: Directory.Build.props
================================================
<Project>
  <PropertyGroup>
	  <ProductVersion>0.15.0</ProductVersion>
	  <ProductName>OpenNetMeter</ProductName>
	  <Manufacturer>Ashfaaq18</Manufacturer>
  </PropertyGroup>

  <Target Name="ValidateProductVersionForMsi" BeforeTargets="Build">
    <Error
      Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('$(ProductVersion)', '^[0-9]+\.[0-9]+\.[0-9]+$')) != 'True'"
      Text="Invalid ProductVersion '$(ProductVersion)'. Use 'major.minor.build' format (for example, 0.14.2). MSI major-upgrade detection compares numeric version parts and ignores a fourth segment." />
  </Target>
</Project>


================================================
FILE: Installer/OpenNetMeter-Installer.wixproj
================================================
<Project Sdk="WixToolset.Sdk/5.0.1">
  <ItemGroup>
    <PackageReference Include="WixToolset.Netfx.wixext" Version="5.0.1" />
    <PackageReference Include="WixToolset.UI.wixext" Version="5.0.1" />
    <PackageReference Include="WixToolset.Util.wixext" Version="5.0.1" />
  </ItemGroup>
  <PropertyGroup>
    <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
    <RepoRoot>$(MSBuildProjectDirectory)\..\</RepoRoot>
    <!-- Optional: fallback -->
	  <DefineConstants>
		  $(DefineConstants);
		  RepoRoot=$(RepoRoot);
		  TargetFramework=$(TargetFramework);
		  Manufacturer=$(Manufacturer);
		  ProductName=$(ProductName);
		  ProductVersion=$(ProductVersion);
	  </DefineConstants>
	  <OutputName>$(ProductName)-$(ProductVersion)</OutputName>
  </PropertyGroup>  
</Project>


================================================
FILE: Installer/Product.wxs
================================================
<!-- Import global variables -->

<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
	 xmlns:netfx="http://wixtoolset.org/schemas/v4/wxs/netfx"
	 xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
	<Package Language="1033"
		 Name="$(ProductName)" Version="$(ProductVersion)" Manufacturer="$(Manufacturer)"
		 UpgradeCode="c8795db6-2426-4f40-a615-671f26fcaf3a" InstallerVersion="200">

		<MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="!(loc.DowngradeError)" />
		<Icon Id="AppIcon.ico" SourceFile="$(RepoRoot)Resources\AppIcon.ico"/>
		<Property Id="ARPPRODUCTICON" Value="AppIcon.ico" />
 
		<Property Id="DESKTOP_SHORTCUT" Value="1"/>

		<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch $(ProductName)" />
    	<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
		<Property Id="WixShellExecTarget" Value="[InstallFolder]$(ProductName).exe" />

    	<CustomAction Id="LaunchApplication"
              BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)"
              DllEntry="WixShellExec"
              Impersonate="yes" />

		<CustomAction Id="CloseRunningApp"
              Directory="System64Folder"
              Execute="deferred"
              Impersonate="no"
              Return="ignore"
              ExeCommand='cmd.exe /c "taskkill /F /IM $(ProductName).exe"' />

		<CustomAction Id="CleanupAppData"
              Directory="System64Folder"
              Execute="deferred"
              Impersonate="yes"
              Return="ignore"
              ExeCommand='cmd.exe /c "rmdir /S /Q "%LOCALAPPDATA%\$(ProductName)""' />

		<CustomAction Id="DeleteOpenNetMeterTask"
					  Directory="System64Folder"
					  Execute="deferred"
					  Impersonate="no"
					  Return="ignore"
					  ExeCommand='cmd.exe /c "schtasks /Delete /TN "\OpenNetMeter\OpenNetMeterLogon" /F &amp; schtasks /Delete /TN "\OpenNetMeter" /F"' />

		<InstallExecuteSequence>
			<Custom Action="CloseRunningApp"
					After="InstallInitialize"
					Condition="WIX_UPGRADE_DETECTED OR REMOVE=&quot;ALL&quot;" />
			<Custom Action="DeleteOpenNetMeterTask"
					Before="RemoveFiles"
					Condition="REMOVE=&quot;ALL&quot; AND NOT UPGRADINGPRODUCTCODE" />
			<Custom Action="CleanupAppData"
					Before="InstallFinalize"
					Condition="REMOVE=&quot;ALL&quot; AND NOT UPGRADINGPRODUCTCODE" />
		</InstallExecuteSequence>

		<MediaTemplate EmbedCab="yes"/>

		<netfx:DotNetCompatibilityCheck Id="netCoreStatus64"
		Property="NETCORESTATUS" RollForward="latestMajor"
		RuntimeType="core" Version="8.0.1" Platform="x64" />
			<Launch Condition="Installed OR NETCORESTATUS=&quot;0&quot;"
				Message="[ProductName] requires Microsoft .NET 8.0 Runtime." />
		
		<!-- UI stuff-->
		<UIRef Id="WixUIFeatureTree" />

		<!-- components to install -->
		<Feature Id="ProductFeature" Level="1">
			<ComponentGroupRef Id="ProductComponents" />
			<ComponentGroupRef Id="ShortcutComponents" />
		</Feature>
		
	</Package>
	
	<Fragment>
		<StandardDirectory Id="ProgramFiles6432Folder">
			<Directory Id="InstallFolder" Name="$(ProductName)"/>
		</StandardDirectory>
		
		<ComponentGroup Id="ProductComponents" Directory="InstallFolder">
			<!-- Files to install -->
			<Files Include="$(RepoRoot)_rc\avalonia\win-x64\OpenNetMeter.exe" />
		</ComponentGroup>
	</Fragment>

	<Fragment>
		<!-- Declare the standard desktop folder so MSI knows where to place the shortcut -->
		<StandardDirectory Id="DesktopFolder" />

		<ComponentGroup Id="ShortcutComponents" Directory="DesktopFolder">
			<Component Id="CmpDesktopShortcut" Guid="2dd319fe-df0c-4f5f-95d6-00a83eb09cb4" Condition="DESKTOP_SHORTCUT=&quot;1&quot;">
				<Shortcut Id="AppDesktopShortcut"
						  Name="$(ProductName)"
						  Target="[InstallFolder]$(ProductName).exe"
						  WorkingDirectory="InstallFolder" />

				<!-- Key path so this component is well-formed -->
				<RegistryValue Root="HKCU"
							   Key="Software\$(Manufacturer)\$(ProductName)"
							   Name="DesktopShortcutInstalled"
							   Type="integer" Value="1" KeyPath="yes" />
			</Component>
		</ComponentGroup>
	</Fragment>
	
</Wix>


================================================
FILE: Installer/Strings_en-us.wxl
================================================
<WixLocalization Culture="en-us" xmlns="http://wixtoolset.org/schemas/v4/wxl">
  <String Id="DowngradeError" Value="A newer version of [ProductName] is already installed." />
  <String Id="UpgradeDetectedDlg_Title" Value="[ProductName] Setup" />
  <String Id="UpgradeDetectedDlgTitle" Value="Upgrade existing installation" />
  <String Id="UpgradeDetectedDlgDescription" Value="A previous version of [ProductName] is already installed. Setup will upgrade it to this version and keep your existing settings." />

  <String Id="PrerequisitesDlg_Title" Value="[ProductName] Setup" />
  <String Id="PrerequisitesDlgBitmap" Value="WixUI_Bmp_Banner" />
  <String Id="PrerequisitesDlgTitle" Value="Prerequisites of [ProductName]" />
  <String Id="PrerequisitesDlgDescription" Value="List and state of prerequisites. Install them manually or use the bootstrapper of [ProductName]" />
</WixLocalization>


================================================
FILE: Installer/WixUIFeatureTree.wxs
================================================
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->

<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
     xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
	<Fragment>
		<UI Id="WixUIFeatureTree">
			<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
			<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
			<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />

			<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />

			<DialogRef Id="ErrorDlg" />
			<DialogRef Id="FatalError" />
			<DialogRef Id="FilesInUse" />
			<DialogRef Id="MsiRMFilesInUse" />
			<DialogRef Id="PrepareDlg" />
			<DialogRef Id="ProgressDlg" />
			<DialogRef Id="ResumeDlg" />
			<DialogRef Id="UserExit" />

			<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchApplication" Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX=&quot;1&quot; and NOT Installed" Order="998"/>
			<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" />

			<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="UpgradeDetectedDlg" Condition="WIX_UPGRADE_DETECTED AND NOT Installed" Order="1" />
			<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="OptionsDlg" Condition="NOT (WIX_UPGRADE_DETECTED AND NOT Installed)" Order="2" />
			
			<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Condition="Installed" Order="1"/>
			<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="UpgradeDetectedDlg" Condition="WIX_UPGRADE_DETECTED AND NOT Installed" Order="2"/>
			<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="OptionsDlg" Condition="NOT Installed AND NOT WIX_UPGRADE_DETECTED" Order="3"/>

			<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" />

			<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" />
			<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" />
			<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" />

			<Dialog Id="UpgradeDetectedDlg" Width="370" Height="270" Title="!(loc.UpgradeDetectedDlg_Title)">
				<Control Id="Title" Type="Text" X="15" Y="6" Width="340" Height="15"
							Transparent="yes" NoPrefix="yes" Text="{\WixUI_Font_Title}!(loc.UpgradeDetectedDlgTitle)" />
				<Control Id="Line" Type="Line" X="0" Y="23" Width="370" Height="0" />
				<Control Id="Description" Type="Text" X="20" Y="55" Width="330" Height="60"
							Transparent="yes" NoPrefix="yes" Text="!(loc.UpgradeDetectedDlgDescription)" />

				<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="Back">
					<Publish Event="NewDialog" Value="WelcomeDlg" />
				</Control>
				<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="Next">
					<Publish Event="NewDialog" Value="VerifyReadyDlg" />
				</Control>
				<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="Cancel">
					<Publish Event="SpawnDialog" Value="CancelDlg" />
				</Control>
			</Dialog>

			<Dialog Id="OptionsDlg" Width="370" Height="270" Title="[ProductName] Setup">
				<Control Id="Title" Type="Text" X="15" Y="6" Width="340" Height="15"
							Transparent="yes" NoPrefix="yes" Text="{\WixUI_Font_Title}Choose options" />
				<Control Id="Line" Type="Line" X="0" Y="23" Width="370" Height="0" />

				<Control Id="DesktopShortcutCheck" Type="CheckBox"
							X="20" Y="60" Width="330" Height="17"
							Property="DESKTOP_SHORTCUT" CheckBoxValue="1"
							Text="Create a Desktop shortcut" />

				<!-- Nav buttons -->
				<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="Back">
					<Publish Event="NewDialog" Value="WelcomeDlg"/>
				</Control>
				<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="Next">
					<Publish Event="NewDialog" Value="VerifyReadyDlg"/>
				</Control>
				<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="Cancel">
					<Publish Event="SpawnDialog" Value="CancelDlg"/>
				</Control>
			</Dialog>
			
		</UI>
		

		<UIRef Id="WixUI_Common" />
	</Fragment>
</Wix>


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright 2021-2026 Ashfaaq Riphque

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: NOTICE
================================================
OpenNetMeter
Copyright 2021-2026 Ashfaaq Riphque

================================================
FILE: OpenNetMeter/App.axaml
================================================
<Application xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Class="OpenNetMeter.App">
  <Application.Styles>
    <FluentTheme/>
  </Application.Styles>
</Application>


================================================
FILE: OpenNetMeter/App.axaml.cs
================================================
using System;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using OpenNetMeter.Services;
using OpenNetMeter.ViewModels;
using OpenNetMeter.Views;
using OpenNetMeter.PlatformAbstractions;
using OpenNetMeter.Properties;
using OpenNetMeter.Utilities;

namespace OpenNetMeter;

public partial class App : Application
{
    private static bool unhandledHandlersRegistered;

    public override void Initialize()
    {
        AvaloniaXamlLoader.Load(this);
    }

    public override void OnFrameworkInitializationCompleted()
    {
        RegisterUnhandledExceptionLoggingOnce();
        EventLogger.Info("Application starting");

        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            desktop.ShutdownMode = global::Avalonia.Controls.ShutdownMode.OnExplicitShutdown;
            bool startMinimized = HasStartMinimizedArgument();

            IMiniWidgetService miniWidgetService = new PlaceholderMiniWidgetService();
            ITrayService trayService = new PlaceholderTrayService();
            ITrayNotificationService trayNotificationService = new PlaceholderTrayNotificationService();

            desktop.Exit += (_, _) =>
            {
                EventLogger.Info("Application exiting");
                trayService.Dispose();
                trayNotificationService.Dispose();
                miniWidgetService.Dispose();
                SettingsManager.Save();
            };

            var windowService = new AvaloniaWindowService();
            IExternalLinkService externalLinkService = new ExternalLinkService();
            IThemeService themeService = new AvaloniaThemeService(this);
            var miniWidgetViewModel = new MiniWidgetViewModel();
            IStartupRegistrationService startupRegistrationService = OperatingSystem.IsWindows()
                ? new WindowsStartupRegistrationService()
                : new PlaceholderStartupRegistrationService();
            INetworkCaptureService networkCaptureService = OperatingSystem.IsWindows()
                ? new WindowsNetworkCaptureService()
                : new PlaceholderNetworkCaptureService();
            IProcessIconService processIconService = OperatingSystem.IsWindows()
                ? new WindowsProcessIconService()
                : new PlaceholderProcessIconService();
            var mainWindow = new MainWindow();

            miniWidgetService = OperatingSystem.IsWindows()
                ? new WindowsMiniWidgetService(miniWidgetViewModel, mainWindow)
                : new PlaceholderMiniWidgetService();

            trayNotificationService = OperatingSystem.IsWindows()
                ? new WindowsTrayNotificationService()
                : new PlaceholderTrayNotificationService();

            mainWindow.InitializeWindowState(miniWidgetService, trayNotificationService);
            mainWindow.DataContext = new MainWindowViewModel(windowService, networkCaptureService, processIconService, externalLinkService, miniWidgetViewModel, miniWidgetService, startupRegistrationService, themeService);
            desktop.MainWindow = mainWindow;

            trayService = OperatingSystem.IsWindows()
                ? new WindowsTrayService(this, desktop, mainWindow, miniWidgetService)
                : new PlaceholderTrayService();

            if (startMinimized)
                mainWindow.Hide();

            if (OperatingSystem.IsWindows() && SettingsManager.Current.MiniWidgetVisibility)
                miniWidgetService.Show();
        }

        base.OnFrameworkInitializationCompleted();
    }

    private static void RegisterUnhandledExceptionLoggingOnce()
    {
        if (unhandledHandlersRegistered)
            return;

        unhandledHandlersRegistered = true;

        AppDomain.CurrentDomain.UnhandledException += (_, e) =>
        {
            if (e.ExceptionObject is Exception ex)
            {
                EventLogger.Error("Unhandled exception", ex);
            }
            else
            {
                EventLogger.Error($"Unhandled exception object: {e.ExceptionObject}");
            }
        };
    }

    private static bool HasStartMinimizedArgument()
    {
        foreach (string arg in Environment.GetCommandLineArgs())
        {
            if (string.Equals(arg, "/StartMinimized", StringComparison.OrdinalIgnoreCase))
                return true;
        }

        return false;
    }
}


================================================
FILE: OpenNetMeter/Compat/Models/ApplicationDB.cs
================================================
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Data.Sqlite;
using OpenNetMeter.Utilities;

namespace OpenNetMeter.Models;

internal sealed class ApplicationDB : IDisposable
{
    private static readonly object DbLock = new();
    private static SqliteConnection? sharedConnection;
    private static int refCount;

    private readonly string adapterName;
    private bool disposed;

    private static string LogTime() => DateTime.Now.ToString("HH:mm:ss.fff");

    public const string UnifiedDBFileName = "OpenNetMeter";
    public const int DataStoragePeriodInDays = 60;

    public ApplicationDB(string dBFileName)
    {
        adapterName = dBFileName;

        lock (DbLock)
        {
            if (sharedConnection == null)
            {
                Directory.CreateDirectory(Properties.Global.GetFilePath());

                var builder = new SqliteConnectionStringBuilder
                {
                    DataSource = GetUnifiedDBFullPath(),
                    Mode = SqliteOpenMode.ReadWriteCreate,
                    Cache = SqliteCacheMode.Shared
                };

                sharedConnection = new SqliteConnection(builder.ToString());
                sharedConnection.Open();

                RunNonQuery(sharedConnection, "PRAGMA journal_mode=WAL;");
                RunNonQuery(sharedConnection, "PRAGMA busy_timeout=5000;");
                RunNonQuery(sharedConnection, "PRAGMA synchronous=NORMAL;");
                RunNonQuery(sharedConnection, "PRAGMA foreign_keys=ON;");
            }

            refCount++;
        }

        Console.WriteLine($"[{LogTime()}] [SQLite][Avalonia] ApplicationDB created adapter='{dBFileName}'");
    }

    public static string GetUnifiedDBFullPath()
    {
        return Path.Combine(Properties.Global.GetFilePath(), UnifiedDBFileName + ".sqlite");
    }

    public static void CloseSharedConnection()
    {
        lock (DbLock)
        {
            sharedConnection?.Dispose();
            sharedConnection = null;
            refCount = 0;
        }
    }

    public int CreateTable()
    {
        lock (DbLock)
        {
            Console.WriteLine($"[{LogTime()}] [SQLite][Avalonia] CreateTable adapter='{adapterName}' db='{GetUnifiedDBFullPath()}'");
            return CreateAdapterTable() >> 31 |
                   CreateProcessTable() >> 31 |
                   CreateDateTable() >> 31 |
                   CreateProcessDateTable() >> 31;
        }
    }

    public int InsertUniqueRow_AdapterTable(string adapter)
    {
        lock (DbLock)
        {
            Console.WriteLine($"[{LogTime()}] [SQLite][Avalonia] InsertUniqueRow_AdapterTable adapter='{adapter}'");
            return RunNonQuery(
                "INSERT OR IGNORE INTO Adapter(Name) VALUES(@Name)",
                ("@Name", adapter));
        }
    }

    public void UpdateDatesInDB()
    {
        lock (DbLock)
        {
            Console.WriteLine($"[{LogTime()}] [SQLite][Avalonia] UpdateDatesInDB adapter='{adapterName}' today='{DateTime.Today:yyyy-MM-dd}' retentionDays={DataStoragePeriodInDays}");
            InsertUniqueRow_DateTable(DateTime.Today);
            RemoveOldDate();
            RemoveOldProcess();
        }
    }

    public void PushToDB(string processName, long totalDataRecv, long totalDataSend)
    {
        lock (DbLock)
        {
            Console.WriteLine($"[{LogTime()}] [SQLite][Avalonia] PushToDB adapter='{adapterName}' process='{processName}' recv={totalDataRecv} sent={totalDataSend}");

            InsertUniqueRow_ProcessTable(processName);
            InsertUniqueRow_AdapterTable(adapterName);

            long dateID = GetID_DateTable(DateTime.Today);
            long processID = GetID_ProcessTable(processName);
            long adapterID = GetID_AdapterTable_Internal(adapterName);

            if (InsertUniqueRow_ProcessDateTable(processID, dateID, adapterID, totalDataRecv, totalDataSend) < 1)
            {
                UpdateRow_ProcessDateTable(processID, dateID, adapterID, totalDataRecv, totalDataSend);
            }
        }
    }

    public List<List<object>> GetDataSum_ProcessDateTable(DateTime date1, DateTime date2)
    {
        lock (DbLock)
        {
            return GetMultipleCellData(
                "SELECT p1.Name, SUM(pd1.DataReceived), SUM(pd1.DataSent) " +
                "FROM ProcessDate pd1 " +
                "JOIN Process p1 ON p1.ID = pd1.ProcessID " +
                "WHERE DateID IN " +
                "(SELECT ID FROM Date WHERE " +
                "(Year * 10000 + Month * 100 + Day) " +
                "BETWEEN " +
                $"({date1.Year * 10000 + date1.Month * 100 + date1.Day}) " +
                "AND " +
                $"({date2.Year * 10000 + date2.Month * 100 + date2.Day})) " +
                "AND AdapterID = @AdapterID " +
                "GROUP BY ProcessID",
                ("@AdapterID", GetID_AdapterTable_Internal(adapterName).ToString()));
        }
    }

    public (long, long) GetDataSumBetweenDates(DateTime startDate, DateTime endDate)
    {
        DateTime fromDate = startDate.Date;
        DateTime toDate = endDate.Date;

        if (toDate < fromDate)
        {
            (fromDate, toDate) = (toDate, fromDate);
        }

        lock (DbLock)
        {
            var sum = GetMultipleCellData(
                "SELECT SUM(DataReceived), SUM(DataSent) FROM ProcessDate " +
                "WHERE DateID IN " +
                "(SELECT ID FROM Date " +
                "WHERE (Year * 10000 + Month * 100 + Day) " +
                "BETWEEN " +
                $"({fromDate.Year * 10000 + fromDate.Month * 100 + fromDate.Day}) " +
                "AND " +
                $"({toDate.Year * 10000 + toDate.Month * 100 + toDate.Day})) " +
                "AND AdapterID = @AdapterID",
                ("@AdapterID", GetID_AdapterTable_Internal(adapterName).ToString()));

            if (sum.Count == 1 && sum[0].Count == 2)
            {
                long download = Convert.IsDBNull(sum[0][0]) ? 0 : Convert.ToInt64(sum[0][0]);
                long upload = Convert.IsDBNull(sum[0][1]) ? 0 : Convert.ToInt64(sum[0][1]);
                return (download, upload);
            }
        }

        return (0, 0);
    }

    public (long, long) GetTodayDataSum_ProcessDateTable()
    {
        lock (DbLock)
        {
            var sum = GetMultipleCellData(
                "SELECT SUM(DataReceived), SUM(DataSent) FROM ProcessDate " +
                "WHERE DateID IN " +
                "(SELECT ID FROM Date " +
                "WHERE (Year * 10000 + Month * 100 + Day) = (@Year * 10000 + @Month * 100 + @Day)) " +
                "AND AdapterID = @AdapterID",
                ("@Year", DateTime.Today.Year.ToString()),
                ("@Month", DateTime.Today.Month.ToString()),
                ("@Day", DateTime.Today.Day.ToString()),
                ("@AdapterID", GetID_AdapterTable_Internal(adapterName).ToString()));

            if (sum.Count == 1 && sum[0].Count == 2 &&
                !Convert.IsDBNull(sum[0][0]) &&
                !Convert.IsDBNull(sum[0][1]))
            {
                return (Convert.ToInt64(sum[0][0]), Convert.ToInt64(sum[0][1]));
            }
        }

        return (0, 0);
    }

    public long GetID_AdapterTable(string adapter)
    {
        lock (DbLock)
        {
            return GetID_AdapterTable_Internal(adapter);
        }
    }

    public List<string> GetAllAdapters()
    {
        var adapters = new List<string>();

        lock (DbLock)
        {
            var rows = GetMultipleCellData("SELECT Name FROM Adapter ORDER BY Name");
            foreach (var row in rows)
            {
                if (row.Count > 0 && !Convert.IsDBNull(row[0]))
                {
                    adapters.Add(Convert.ToString(row[0])!);
                }
            }
        }

        return adapters;
    }

    public void Dispose()
    {
        if (disposed)
        {
            return;
        }

        disposed = true;

        lock (DbLock)
        {
            refCount--;
            if (refCount == 0 && sharedConnection != null)
            {
                sharedConnection.Dispose();
                sharedConnection = null;
            }
        }
    }

    private static SqliteConnection Connection =>
        sharedConnection ?? throw new InvalidOperationException("Database not initialized");

    private int CreateProcessTable()
    {
        return RunNonQuery(
            "CREATE TABLE IF NOT EXISTS Process(" +
            "ID INTEGER PRIMARY KEY NOT NULL, " +
            "Name TEXT NOT NULL UNIQUE)");
    }

    private int CreateDateTable()
    {
        return RunNonQuery(
            "CREATE TABLE IF NOT EXISTS Date(" +
            "ID INTEGER PRIMARY KEY NOT NULL, " +
            "Year INTEGER NOT NULL, " +
            "Month INTEGER NOT NULL, " +
            "Day INTEGER NOT NULL, " +
            "UNIQUE (Year, Month, Day) ON CONFLICT IGNORE)");
    }

    private int CreateProcessDateTable()
    {
        return RunNonQuery(
            "CREATE TABLE IF NOT EXISTS ProcessDate(" +
            "ID INTEGER PRIMARY KEY NOT NULL, " +
            "ProcessID INTEGER NOT NULL, " +
            "DateID INTEGER NOT NULL, " +
            "AdapterID INTEGER NOT NULL, " +
            "DataReceived INTEGER NOT NULL, " +
            "DataSent INTEGER NOT NULL, " +
            "FOREIGN KEY(ProcessID) REFERENCES Process(ID) ON DELETE CASCADE, " +
            "FOREIGN KEY(DateID) REFERENCES Date(ID) ON DELETE CASCADE, " +
            "FOREIGN KEY(AdapterID) REFERENCES Adapter(ID) ON DELETE CASCADE, " +
            "UNIQUE (ProcessID, DateID, AdapterID) ON CONFLICT IGNORE)");
    }

    private int CreateAdapterTable()
    {
        return RunNonQuery(
            "CREATE TABLE IF NOT EXISTS Adapter(" +
            "ID INTEGER PRIMARY KEY NOT NULL, " +
            "Name TEXT NOT NULL UNIQUE)");
    }

    private int InsertUniqueRow_ProcessTable(string appName)
    {
        return RunNonQuery(
            "INSERT OR IGNORE INTO Process(Name) VALUES(@Name)",
            ("@Name", appName));
    }

    private int InsertUniqueRow_DateTable(DateTime date)
    {
        return RunNonQuery(
            "INSERT OR IGNORE INTO Date(Year, Month, Day) VALUES(@Year, @Month, @Day)",
            ("@Year", date.Year.ToString()),
            ("@Month", date.Month.ToString()),
            ("@Day", date.Day.ToString()));
    }

    private void RemoveOldDate()
    {
        var cutoff = DateTime.Now.AddDays(-DataStoragePeriodInDays);
        Console.WriteLine($"[{LogTime()}] [SQLite][Avalonia] RemoveOldDate cutoff='{cutoff:yyyy-MM-dd}'");
        RunNonQuery(
            "DELETE FROM Date WHERE (Year * 10000 + Month * 100 + Day) < " +
            $"({cutoff.Year * 10000 + cutoff.Month * 100 + cutoff.Day})");
    }

    private void RemoveOldProcess()
    {
        Console.WriteLine($"[{LogTime()}] [SQLite][Avalonia] RemoveOldProcess");
        RunNonQuery(
            "DELETE FROM Process WHERE ID NOT IN " +
            "(SELECT DISTINCT ProcessID FROM ProcessDate)");
    }

    private int InsertUniqueRow_ProcessDateTable(long processID, long dateID, long adapterID, long dataReceived, long dataSent)
    {
        return RunNonQuery(
            "INSERT OR IGNORE INTO ProcessDate(ProcessID, DateID, AdapterID, DataReceived, DataSent) " +
            "VALUES(@ProcessID, @DateID, @AdapterID, @DataReceived, @DataSent)",
            ("@ProcessID", processID.ToString()),
            ("@DateID", dateID.ToString()),
            ("@AdapterID", adapterID.ToString()),
            ("@DataReceived", dataReceived.ToString()),
            ("@DataSent", dataSent.ToString()));
    }

    private int UpdateRow_ProcessDateTable(long processID, long dateID, long adapterID, long dataReceived, long dataSent)
    {
        return RunNonQuery(
            "UPDATE ProcessDate SET " +
            "DataReceived = DataReceived + @DataReceived, " +
            "DataSent = DataSent + @DataSent " +
            "WHERE ProcessID = @ProcessID AND DateID = @DateID AND AdapterID = @AdapterID",
            ("@DataReceived", dataReceived.ToString()),
            ("@DataSent", dataSent.ToString()),
            ("@ProcessID", processID.ToString()),
            ("@DateID", dateID.ToString()),
            ("@AdapterID", adapterID.ToString()));
    }

    private long GetID_DateTable(DateTime time)
    {
        var result = GetSingleCellData(
            "SELECT ID FROM Date WHERE Year = @Year AND Month = @Month AND Day = @Day",
            ("@Year", time.Year.ToString()),
            ("@Month", time.Month.ToString()),
            ("@Day", time.Day.ToString()));

        return Convert.ToInt64(result ?? -1);
    }

    private long GetID_ProcessTable(string appName)
    {
        var result = GetSingleCellData(
            "SELECT ID FROM Process WHERE Name = @Name",
            ("@Name", appName));

        return Convert.ToInt64(result ?? -1);
    }

    private long GetID_AdapterTable_Internal(string adapter)
    {
        var result = GetSingleCellData(
            "SELECT ID FROM Adapter WHERE Name = @Name",
            ("@Name", adapter));

        return Convert.ToInt64(result ?? -1);
    }

    private static int RunNonQuery(string query, params (string Name, string Value)[] parameters)
    {
        return RunNonQuery(Connection, query, parameters);
    }

    private static int RunNonQuery(SqliteConnection connection, string query, params (string Name, string Value)[] parameters)
    {
        try
        {
            using var command = connection.CreateCommand();
            command.CommandText = query;
            foreach (var parameter in parameters)
            {
                command.Parameters.AddWithValue(parameter.Name, parameter.Value);
            }

            return command.ExecuteNonQuery();
        }
        catch (Exception ex)
        {
            EventLogger.Error($"SQLite non-query failed. Query='{query}'", ex);
            return -1;
        }
    }

    private static List<List<object>> GetMultipleCellData(string query, params (string Name, string Value)[] parameters)
    {
        var result = new List<List<object>>();

        try
        {
            using var command = Connection.CreateCommand();
            command.CommandText = query;
            foreach (var parameter in parameters)
            {
                command.Parameters.AddWithValue(parameter.Name, parameter.Value);
            }

            using var reader = command.ExecuteReader();
            while (reader.Read())
            {
                var row = new List<object>();
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    row.Add(reader.GetValue(i));
                }

                result.Add(row);
            }
        }
        catch (Exception ex)
        {
            EventLogger.Error($"SQLite multi-cell read failed. Query='{query}'", ex);
            return result;
        }

        return result;
    }

    private static object? GetSingleCellData(string query, params (string Name, string Value)[] parameters)
    {
        try
        {
            using var command = Connection.CreateCommand();
            command.CommandText = query;
            foreach (var parameter in parameters)
            {
                command.Parameters.AddWithValue(parameter.Name, parameter.Value);
            }

            return command.ExecuteScalar();
        }
        catch (Exception ex)
        {
            EventLogger.Error($"SQLite single-cell read failed. Query='{query}'", ex);
            return null;
        }
    }

}


================================================
FILE: OpenNetMeter/Compat/Models/MyProcess_Small.cs
================================================
namespace OpenNetMeter.Models;

public class MyProcess_Small
{
    public string? Name { get; set; }
    public long CurrentDataRecv { get; set; }
    public long CurrentDataSend { get; set; }

    public MyProcess_Small(string nameP, long currentDataRecvP, long currentDataSendP)
    {
        Name = nameP;
        CurrentDataRecv = currentDataRecvP;
        CurrentDataSend = currentDataSendP;
    }
}


================================================
FILE: OpenNetMeter/Compat/Models/NetworkProcess.TestHooks.cs
================================================
using System.Net;

namespace OpenNetMeter.Models
{
    // Test-only helpers kept internal to avoid exposing implementation details publicly.
    public partial class NetworkProcess
    {
        internal void TestSetLocalIPs(byte[] ipv4, byte[] ipv6)
        {
            localIPv4 = ipv4;
            localIPv6 = ipv6;
        }

        internal void TestInvokeRecvProcess(IPAddress src, IPAddress dest, int size, string name)
        {
            ProcessPacket(src, dest, size, name, isRecv: true);
        }

        internal void TestInvokeSendProcess(IPAddress src, IPAddress dest, int size, string name)
        {
            ProcessPacket(src, dest, size, name, isRecv: false);
        }
    }
}


================================================
FILE: OpenNetMeter/Compat/Models/NetworkProcess.cs
================================================
using System;
using System.Net;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Parsers.Kernel;
using Microsoft.Diagnostics.Tracing.Session;
using System.Threading;
using System.Net.Sockets;
using System.Collections.Generic;
using OpenNetMeter.Utilities;
using OpenNetMeter.Properties;
using System.Diagnostics.Eventing.Reader;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;

namespace OpenNetMeter.Models
{
    [SupportedOSPlatform("windows")]
    public partial class NetworkProcess : IDisposable
    {
        //---------- Constants ------------//

        private const int OneSec = 1000;
        private const int DebounceDelayMs = 300; // Delay before processing network change events

        // Default IP values indicating "not connected"
        private static readonly byte[] DefaultIPv4 = { 0, 0, 0, 0 };
        private static readonly byte[] DefaultIPv6 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

        //---------- Network State ------------//

        /// <summary>
        /// Immutable snapshot of current network connection state.
        /// Used to detect connection changes without race conditions.
        /// </summary>
        private sealed record NetworkSnapshot(
            string AdapterName,  // Display name, includes SSID for Wi-Fi
            string AdapterId,    // GUID for the adapter
            byte[] IPv4,         // Assigned IPv4 address bytes
            byte[] IPv6          // Assigned IPv6 address bytes
        );

        // Lock for thread-safe access to local IP addresses
        private readonly object stateLock = new object();

        // Currently assigned local IP addresses (used for packet filtering)
        private byte[] localIPv4 = DefaultIPv4;
        private byte[] localIPv6 = DefaultIPv6;

        //---------- Debounce & Synchronization ------------//

        // Timer-based debounce: each network change event resets the timer.
        // When it finally fires (after DebounceDelayMs of silence), HandleNetworkChange runs.
        private Timer? debounceTimer;
        private volatile bool isDisposed;

        // Lock to ensure only one HandleNetworkChange runs at a time
        private readonly object networkChangeLock = new object();

        //---------- Periodic Tasks ------------//

        // Periodic task for updating network speed display
        private PeriodicWork? networkSpeedWork;

        // Periodic task for pushing process data to database
        private PeriodicWork? dbPushWork;

        //---------- ETW Session ------------//

        // ETW kernel session for capturing network packets
        private TraceEventSession? kernelSession;

        private const string KernelSessionName = "OpenNetMeter-Kernel";

        // Task running the ETW packet capture loop
        public Task? PacketTask;

        //---------- Public State ------------//

        // Current network adapter name (includes SSID for Wi-Fi connections)
        public string AdapterName { get; private set; } = "";

        // Current adapter's unique ID (GUID) - used for comparison to detect changes
        private string currentAdapterId = "";
        public string CurrentAdapterId => currentAdapterId;

        /// <summary>
        /// Primary buffer for storing process network data.
        /// Alternates with MyProcessesBuffer to allow lock-free reading.
        /// </summary>
        public Dictionary<string, MyProcess_Small?> MyProcesses { get; } = new();

        /// <summary>
        /// Secondary buffer for storing process network data.
        /// While GUI reads from MyProcesses, new data goes here (and vice versa).
        /// This double-buffering prevents lock contention with the UI thread.
        /// </summary>
        public Dictionary<string, MyProcess_Small?> MyProcessesBuffer { get; } = new();

        /// <summary>
        /// Buffer for data pending database write.
        /// Cleared after each successful DB push.
        /// </summary>
        public Dictionary<string, MyProcess_Small?> PushToDBBuffer { get; } = new();

        /// <summary>
        /// Controls which buffer receives incoming packet data.
        /// true = write to MyProcessesBuffer, read from MyProcesses
        /// false = write to MyProcesses, read from MyProcessesBuffer
        /// Toggled by the UI layer when extracting data for display.
        /// </summary>
        public bool IsBufferTime { get; set; }

        // Running totals for current session (reset on adapter change)
        public long CurrentSessionDownloadData;
        public long CurrentSessionUploadData;
        public long UploadSpeed;

        //---------- Properties with Change Notification ------------//

        private long downloadSpeed;
        public long DownloadSpeed
        {
            get => downloadSpeed;
            set { downloadSpeed = value; OnPropertyChanged(nameof(DownloadSpeed)); }
        }

        private string isNetworkOnline = "Disconnected";
        /// <summary>
        /// Current connection status. Either "Disconnected" or the adapter name.
        /// </summary>
        public string IsNetworkOnline
        {
            get => isNetworkOnline;
            private set { isNetworkOnline = value; OnPropertyChanged(nameof(IsNetworkOnline)); }
        }

        //---------- Initialization ------------//

        /// <summary>
        /// Call after subscribing to property handlers in MainWindowVM.
        /// Sets up network change monitoring and performs initial connection check.
        /// </summary>
        public void Initialize()
        {
            IsNetworkOnline = "Disconnected";

            // Create the debounce timer (initially not running)
            debounceTimer = new Timer(_ => HandleNetworkChange(), null, Timeout.Infinite, Timeout.Infinite);

            // Subscribe to network address changes (fires on connect/disconnect/IP change)
            NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;

            // Check current connection state on startup
            HandleNetworkChange();
        }

        //---------- Network Detection (Snapshot-Based) ------------//

        /// <summary>
        /// Queries the system for the current active network connection.
        /// Returns an immutable snapshot of the connection, or null if disconnected.
        /// 
        /// This replaces the old socket-based GetLocalIP() approach which was unreliable
        /// during network transitions (socket connect to 8.8.8.8 would fail transiently).
        /// </summary>
        private NetworkSnapshot? GetCurrentNetworkSnapshot()
        {
            var adapters = NetworkInterface.GetAllNetworkInterfaces();

            foreach (var adapter in adapters)
            {
                // Skip adapters that aren't connected
                if (adapter.OperationalStatus != OperationalStatus.Up)
                    continue;

                // Skip loopback (127.0.0.1)
                if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback)
                    continue;

                var props = adapter.GetIPProperties();

                // No gateway = not a real internet connection (e.g., virtual adapters)
                if (props.GatewayAddresses.Count == 0)
                    continue;

                byte[]? ipv4 = null;
                byte[]? ipv6 = null;

                // Extract assigned IP addresses
                foreach (var unicast in props.UnicastAddresses)
                {
                    var addr = unicast.Address;

                    if (addr.AddressFamily == AddressFamily.InterNetwork)
                    {
                        ipv4 = addr.GetAddressBytes();
                    }
                    else if (addr.AddressFamily == AddressFamily.InterNetworkV6
                             && !addr.IsIPv6LinkLocal) // Skip link-local (fe80::) addresses
                    {
                        ipv6 = addr.GetAddressBytes();
                    }
                }

                // Need at least one usable IP to consider this a valid connection
                if (ipv4 == null && ipv6 == null)
                    continue;

                // Build display name (include SSID for Wi-Fi)
                var name = adapter.Name;
                if (adapter.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
                {
                    var ssid = GetConnectedSsid(adapter.Id);
                    if (!string.IsNullOrEmpty(ssid))
                        name += $"({ssid})";
                }

                Debug.WriteLine($"{adapter.Name} is up, IP: {(ipv4 != null ? new IPAddress(ipv4) : new IPAddress(ipv6!))}");

                return new NetworkSnapshot(
                    name,
                    adapter.Id,
                    ipv4 ?? DefaultIPv4,
                    ipv6 ?? DefaultIPv6
                );
            }

            return null; // No valid connection found
        }

        /// <summary>
        /// Gets the SSID of the currently connected Wi-Fi network by reading
        /// from the Windows WLAN AutoConfig event log.
        /// 
        /// This is more reliable than the WinRT API on Windows 11 which requires
        /// location permissions to retrieve SSID.
        /// </summary>
        private static string? GetConnectedSsid(string adapterGuid)
        {
            try
            {
                // Query for EventID 8001 (successful connection) in WLAN-AutoConfig log
                var query = new EventLogQuery(
                    "Microsoft-Windows-WLAN-AutoConfig/Operational",
                    PathType.LogName,
                    "*[System[EventID=8001]]"
                )
                {
                    ReverseDirection = true // Get most recent first
                };

                using var reader = new EventLogReader(query);
                if (reader.ReadEvent() is EventRecord evt)
                {
                    var message = evt.FormatDescription();
                    var match = Regex.Match(message, @"^SSID:\s*(.+)$", RegexOptions.Multiline);
                    if (match.Success)
                        return match.Groups[1].Value.Trim();
                }
            }
            catch (Exception ex)
            {
                EventLogger.Error("Failed to read connected SSID", ex);
            }

            return null;
        }

        //---------- Network Change Handling ------------//

        /// <summary>
        /// Event handler for NetworkChange.NetworkAddressChanged.
        /// Resets the debounce timer so that HandleNetworkChange only runs
        /// after DebounceDelayMs of silence (no rapid-fire events).
        /// </summary>
        private void OnNetworkAddressChanged(object? sender, EventArgs? e)
        {
            if (isDisposed)
                return;

            // Reset the timer countdown. If another event arrives before it fires,
            // the timer resets again. No allocations, no CTS, no race conditions.
            try
            {
                debounceTimer?.Change(DebounceDelayMs, Timeout.Infinite);
            }
            catch (ObjectDisposedException)
            {
                EventLogger.Warn("Debounce timer was disposed while handling network address change");
            }
        }

        /// <summary>
        /// Core network state machine. Compares current network state to tracked state
        /// and triggers appropriate actions (connect/disconnect/switch/refresh).
        /// 
        /// State transitions:
        /// - Disconnected -> Connected: Start monitoring
        /// - Connected -> Disconnected: Stop monitoring  
        /// - Connected -> Different adapter: Stop then start monitoring
        /// - Connected -> Same adapter:
        ///   - IP/name changed: Refresh tracked snapshot (no restart)
        ///   - No meaningful change: Ignore transient event
        /// 
        /// Protected by networkChangeLock to prevent concurrent execution.
        /// </summary>
        private void HandleNetworkChange()
        {
            if (isDisposed)
                return;

            lock (networkChangeLock)
            {
                if (isDisposed)
                    return;

                var snapshot = GetCurrentNetworkSnapshot();

                if (snapshot == null)
                {
                    // No valid connection found
                    if (IsNetworkOnline != "Disconnected")
                    {
                        Debug.WriteLine("Ash: Connection lost");
                        EndNetworkProcess();
                    }
                    return;
                }

                // Valid connection found - determine if it's new or changed
                if (IsNetworkOnline == "Disconnected")
                {
                    // Was disconnected, now connected
                    Debug.WriteLine("Ash: Connection established");
                    ApplySnapshot(snapshot);
                    StartNetworkProcess();
                }
                else if (currentAdapterId != snapshot.AdapterId)
                {
                    // Was connected to different adapter (e.g., switched Wi-Fi networks)
                    // Compare by ID, not name, because SSID retrieval can race with event log
                    Debug.WriteLine("Ash: Network adapter changed");
                    EndNetworkProcess();
                    ApplySnapshot(snapshot);
                    StartNetworkProcess();
                }
                else
                {
                    // Same adapter can still receive DHCP/IPv6 address changes or an SSID/name update.
                    // Refresh tracked snapshot so packet filtering keeps matching local traffic.
                    bool ipChanged = HasSnapshotAddressChanged(snapshot);
                    bool nameChanged = !string.Equals(AdapterName, snapshot.AdapterName, StringComparison.Ordinal);
                    if (ipChanged || nameChanged)
                    {
                        Debug.WriteLine("Ash: Adapter IP/name changed on same adapter - refreshing snapshot");
                        ApplySnapshot(snapshot);

                        // Keep status text in sync when SSID/name changes without adapter GUID change.
                        if (nameChanged)
                            IsNetworkOnline = AdapterName;
                    }
                }
            }
        }

        private bool HasSnapshotAddressChanged(NetworkSnapshot snapshot)
        {
            lock (stateLock)
            {
                return !ByteArray.Compare(localIPv4, snapshot.IPv4) ||
                       !ByteArray.Compare(localIPv6, snapshot.IPv6);
            }
        }

        /// <summary>
        /// Updates tracked state from a network snapshot.
        /// Called when establishing a new connection, switching adapters,
        /// or refreshing state on same-adapter IP/name changes.
        /// </summary>
        private void ApplySnapshot(NetworkSnapshot snapshot)
        {
            lock (stateLock)
            {
                localIPv4 = snapshot.IPv4;
                localIPv6 = snapshot.IPv6;
            }
            currentAdapterId = snapshot.AdapterId;
            AdapterName = snapshot.AdapterName;
        }

        //---------- Start/Stop Network Monitoring ------------//

        /// <summary>
        /// Starts all network monitoring components:
        /// - Database table setup
        /// - ETW packet capture
        /// - Speed monitoring periodic task
        /// - Database push periodic task
        /// </summary>
        public void StartNetworkProcess()
        {
            // Ensure database table exists for this adapter
            using (var db = new ApplicationDB(AdapterName))
            {
                if (db.CreateTable() < 0)
                {
                    EventLogger.Error("Error: Create table");
                }
                else
                {
                    db.InsertUniqueRow_AdapterTable(AdapterName);
                    db.UpdateDatesInDB();
                }
            }

            TraceEventSession.GetActiveSession(KernelSessionName)?.Stop();
            // Start packet capture and periodic tasks
            CaptureNetworkPackets();
            StartSpeedMonitoring();
            StartDbPush();

            // Update connection status (triggers UI update via PropertyChanged)
            IsNetworkOnline = AdapterName;
        }

        /// <summary>
        /// Stops all network monitoring components and resets state.
        /// Called on disconnect or before switching to a different adapter.
        /// </summary>
        public void EndNetworkProcess()
        {
            // Stop ETW session first (generates most activity)
            StopKernelSession();

            // Stop periodic tasks
            StopPeriodicWork(ref networkSpeedWork, "Network speed");
            StopPeriodicWork(ref dbPushWork, "DB push");

            FlushPendingDbWrites();

            // Clear process data buffers
            lock (MyProcesses) MyProcesses.Clear();
            lock (MyProcessesBuffer) MyProcessesBuffer.Clear();

            // Reset speed counters
            CurrentSessionDownloadData = 0;
            CurrentSessionUploadData = 0;
            UploadSpeed = 0;
            DownloadSpeed = 0;

            // Reset adapter tracking
            currentAdapterId = "";
            IsNetworkOnline = "Disconnected";
        }

        /// <summary>
        /// Stops the ETW kernel session and waits for the capture task to complete.
        /// </summary>
        private void StopKernelSession()
        {
            // Disposing the session causes Source.Process() to return
            kernelSession?.Dispose();
            kernelSession = null;

            var task = PacketTask;
            if (task != null)
            {
                try
                {
                    task.Wait(TimeSpan.FromMilliseconds(OneSec));
                }
                catch (AggregateException ex)
                {
                    EventLogger.Error("Packet capture stop error", ex);
                }
                finally
                {
                    if (task.IsCompleted)
                        task.Dispose();
                    else
                        task.ContinueWith(t => t.Dispose(), TaskScheduler.Default);

                    PacketTask = null;
                }
            }
        }

        /// <summary>
        /// Safely stops a periodic work task with error handling.
        /// </summary>
        private void StopPeriodicWork(ref PeriodicWork? work, string name)
        {
            try
            {
                work?.Stop();
            }
            catch (Exception ex)
            {
                EventLogger.Error($"{name} stop error", ex);
            }
            finally
            {
                work = null;
            }
        }

        private void FlushPendingDbWrites()
        {
            lock (PushToDBBuffer)
            {
                if (PushToDBBuffer.Count <= 0 || string.IsNullOrWhiteSpace(AdapterName))
                {
                    PushToDBBuffer.Clear();
                    return;
                }

                using var db = new ApplicationDB(AdapterName);
                foreach (var (key, proc) in PushToDBBuffer)
                {
                    if (proc != null)
                        db.PushToDB(key, proc.CurrentDataRecv, proc.CurrentDataSend);
                }

                PushToDBBuffer.Clear();
            }
        }

        //---------- Periodic Tasks ------------//

        /// <summary>
        /// Starts the periodic task that calculates current network speed.
        /// Runs every second and computes speed as delta from previous totals.
        /// </summary>
        private void StartSpeedMonitoring()
        {
            long tempDownload = 0;
            long tempUpload = 0;

            networkSpeedWork = new PeriodicWork("Network speed", TimeSpan.FromSeconds(1));
            networkSpeedWork.Start(_ =>
            {
                // Read current totals (atomic reads)
                long currentDown = Interlocked.Read(ref CurrentSessionDownloadData);
                long currentUp = Interlocked.Read(ref CurrentSessionUploadData);

                // Calculate speed based on user's preferred format
                if (SettingsManager.Current.NetworkSpeedFormat == 0)
                {
                    // Bits per second
                    DownloadSpeed = (currentDown - tempDownload) * 8;
                    UploadSpeed = (currentUp - tempUpload) * 8;
                }
                else
                {
                    // Bytes per second
                    DownloadSpeed = currentDown - tempDownload;
                    UploadSpeed = currentUp - tempUpload;
                }

                // Store for next iteration's delta calculation
                tempDownload = currentDown;
                tempUpload = currentUp;

                return Task.CompletedTask;
            });
        }

        /// <summary>
        /// Starts the periodic task that pushes accumulated process data to the database.
        /// Runs every 5 seconds to batch writes and reduce I/O.
        /// </summary>
        private void StartDbPush()
        {
            dbPushWork = new PeriodicWork("DB push", TimeSpan.FromSeconds(5));
            dbPushWork.Start(_ =>
            {
#if DEBUG
                var sw = Stopwatch.StartNew();
#endif
                lock (PushToDBBuffer)
                {
                    if (PushToDBBuffer.Count > 0)
                    {
                        using var db = new ApplicationDB(AdapterName);
                        foreach (var (key, proc) in PushToDBBuffer)
                        {
                            if (proc != null)
                                db.PushToDB(key, proc.CurrentDataRecv, proc.CurrentDataSend);
                        }
                        PushToDBBuffer.Clear();
                    }
                }
#if DEBUG
                sw.Stop();
                Debug.WriteLine($"elapsed time (DBpush): {sw.ElapsedMilliseconds} | time {DateTime.Now:O}");
#endif
                return Task.CompletedTask;
            });
        }

        //---------- ETW Packet Capture ------------//

        /// <summary>
        /// Starts the ETW (Event Tracing for Windows) kernel session to capture
        /// all TCP/IP network packets on the system.
        /// 
        /// Uses the NT Kernel Logger session which requires admin privileges.
        /// Packets are filtered to only count those matching our local IP.
        /// </summary>
        private void CaptureNetworkPackets()
        {
            PacketTask = Task.Run(() =>
            {
                if (kernelSession != null) return;

                try
                {
                    // Create kernel trace session (requires admin)
                    kernelSession = new TraceEventSession(KernelSessionName);
                    kernelSession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP);

                    // Subscribe to all TCP/UDP send/receive events
                    // All events funnel through ProcessPacket for unified handling
                    var kernel = kernelSession.Source.Kernel;

                    // Receive events (download)
                    kernel.TcpIpRecv += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);
                    kernel.TcpIpRecvIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);
                    kernel.UdpIpRecv += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);
                    kernel.UdpIpRecvIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);

                    // Send events (upload)
                    kernel.TcpIpSend += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);
                    kernel.TcpIpSendIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);
                    kernel.UdpIpSend += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);
                    kernel.UdpIpSendIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);

                    // Blocks until session is disposed
                    kernelSession.Source.Process();
                }
                catch (Exception ex)
                {
                    EventLogger.Error("Packet capture loop error", ex);
                }
            });
        }

        /// <summary>
        /// Processes a single network packet captured by ETW.
        /// Filters packets to only count those involving our local IP,
        /// then records to the appropriate buffer based on direction.
        /// </summary>
        /// <param name="src">Source IP address</param>
        /// <param name="dest">Destination IP address</param>
        /// <param name="size">Packet size in bytes</param>
        /// <param name="processName">Name of the process that sent/received the packet</param>
        /// <param name="isRecv">True for download, false for upload</param>
        private void ProcessPacket(IPAddress src, IPAddress dest, int size, string processName, bool isRecv)
        {
            // Get current local IP for this address family
            byte[] localIp;
            lock (stateLock)
            {
                localIp = src.AddressFamily == AddressFamily.InterNetwork ? localIPv4 : localIPv6;
            }

            // Cache address bytes to avoid repeated allocations
            var srcBytes = src.GetAddressBytes();
            var destBytes = dest.GetAddressBytes();

            // Check if packet involves our local IP
            bool isSrc = ByteArray.Compare(srcBytes, localIp);
            bool isDest = ByteArray.Compare(destBytes, localIp);

            // XOR: exactly one should match (either we sent it or received it)
            // If both match (loopback) or neither match (other adapter), skip
            if (!(isSrc ^ isDest))
                return;

            // Apply network type filter (private/public/both)
            if (!ShouldProcessByNetworkType(isSrc, src, dest))
                return;

            // Record the packet to appropriate counter and buffer
            if (isRecv)
                RecordRecv(processName, size);
            else
                RecordSend(processName, size);
        }

        /// <summary>
        /// Checks if a packet should be counted based on the user's network type filter setting.
        /// </summary>
        /// <param name="isLocalSrc">True if local IP is the source (upload), false if destination (download)</param>
        /// <param name="src">Source IP</param>
        /// <param name="dest">Destination IP</param>
        /// <returns>True if packet should be counted</returns>
        private bool ShouldProcessByNetworkType(bool isLocalSrc, IPAddress src, IPAddress dest)
        {
            // Remote IP is the one that isn't ours
            var remoteIp = isLocalSrc ? dest : src;

            return SettingsManager.Current.NetworkType switch
            {
                0 => IsPrivateIP(remoteIp),   // Private only (LAN traffic)
                1 => !IsPrivateIP(remoteIp),  // Public only (internet traffic)
                2 => true,                     // Both
                _ => false
            };
        }

        /// <summary>
        /// Records a received (download) packet.
        /// </summary>
        private void RecordRecv(string name, int size)
        {
            // Thread-safe increment of running total
            Interlocked.Add(ref CurrentSessionDownloadData, size);
            RecordToBuffer(name, size, isRecv: true);
        }

        /// <summary>
        /// Records a sent (upload) packet.
        /// </summary>
        private void RecordSend(string name, int size)
        {
            // Thread-safe increment of running total
            Interlocked.Add(ref CurrentSessionUploadData, size);
            RecordToBuffer(name, size, isRecv: false);
        }

        /// <summary>
        /// Records packet data to the active process buffer.
        /// Uses double-buffering to minimize lock contention with UI reads.
        /// </summary>
        private void RecordToBuffer(string name, int size, bool isRecv)
        {
            // Empty process name means kernel/system traffic
            if (string.IsNullOrEmpty(name))
                name = "System";

            // Select buffer based on current phase
            var dict = IsBufferTime ? MyProcessesBuffer : MyProcesses;

            lock (dict)
            {
                // Get or create process entry (single lookup)
                if (!dict.TryGetValue(name, out var proc))
                {
                    proc = new MyProcess_Small(name, 0, 0);
                    dict[name] = proc;
                }

                // Accumulate data
                if (isRecv)
                    proc!.CurrentDataRecv += size;
                else
                    proc!.CurrentDataSend += size;
            }
        }

        //---------- IP Classification ------------//

        /// <summary>
        /// Determines if an IP address is in a private (non-routable) range.
        /// Used for filtering traffic by network type (LAN vs internet).
        /// 
        /// Private ranges:
        /// - IPv4: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16
        /// - IPv6: Link-local (fe80::), site-local, unique-local (fc00::/7)
        /// </summary>
        private bool IsPrivateIP(IPAddress ip)
        {
            // Handle IPv4-mapped IPv6 addresses (::ffff:x.x.x.x)
            if (ip.IsIPv4MappedToIPv6)
                ip = ip.MapToIPv4();

            // Loopback is always private
            if (IPAddress.IsLoopback(ip))
                return true;

            if (ip.AddressFamily == AddressFamily.InterNetwork)
            {
                var bytes = ip.GetAddressBytes();
                return bytes[0] == 10 ||                                        // 10.0.0.0/8 (Class A)
                       (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) || // 172.16.0.0/12 (Class B)
                       (bytes[0] == 192 && bytes[1] == 168) ||                  // 192.168.0.0/16 (Class C)
                       (bytes[0] == 169 && bytes[1] == 254);                    // 169.254.0.0/16 (link-local/APIPA)
            }

            if (ip.AddressFamily == AddressFamily.InterNetworkV6)
            {
                return ip.IsIPv6LinkLocal ||     // fe80::/10
#if NET6_0_OR_GREATER
                       ip.IsIPv6UniqueLocal ||   // fc00::/7 (only available in .NET 6+)
#endif
                       ip.IsIPv6SiteLocal;       // fec0::/10 (deprecated but still checked)
            }

            return false;
        }

        //---------- Property Changed ------------//

        public event PropertyChangedEventHandler? PropertyChanged;

        private void OnPropertyChanged(string propName) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));

        //---------- Dispose ------------//

        /// <summary>
        /// Cleans up all resources: unsubscribes from events, stops monitoring,
        /// and flushes any pending data to the database.
        /// </summary>
        public void Dispose()
        {
            isDisposed = true;

            // Unsubscribe from network change events
            NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged;

            // Dispose the debounce timer (prevents any pending callback from firing)
            debounceTimer?.Dispose();
            debounceTimer = null;

            // Stop monitoring if active
            if (IsNetworkOnline != "Disconnected")
                EndNetworkProcess();

            // Flush any remaining buffered data to database
            FlushPendingDbWrites();
        }
    }
}


================================================
FILE: OpenNetMeter/Compat/Properties/AppSettings.cs
================================================
namespace OpenNetMeter.Properties;

public class AppSettings
{
    public bool DarkMode { get; set; }
    public bool StartWithWin { get; set; }
    public bool MinimizeOnStart { get; set; } = true;
    public int WinPosX { get; set; }
    public int WinPosY { get; set; }
    public int WinWidth { get; set; } = 900;
    public int WinHeight { get; set; } = 600;
    public bool MainWindowPositionInitialized { get; set; }
    public bool MiniWidgetVisibility { get; set; } = true;
    public bool MiniWidgetPinned { get; set; }
    public int MiniWidgetPosX { get; set; }
    public int MiniWidgetPosY { get; set; }
    public bool MiniWidgetPositionInitialized { get; set; }
    public int MiniWidgetTransparentSlider { get; set; } = 20;

    public int NetworkType { get; set; } = 2;
    public int NetworkSpeedFormat { get; set; } = 0;
    public int NetworkSpeedMagnitude { get; set; } = 0;
}


================================================
FILE: OpenNetMeter/Compat/Properties/Global.cs
================================================
using System;
using System.IO;

namespace OpenNetMeter.Properties;

internal class Global
{
    public const string AppName = "OpenNetMeter";

    public static string GetFilePath()
    {
        var path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        path = Path.Combine(path, AppName);
        Directory.CreateDirectory(path);
        return path;
    }
}


================================================
FILE: OpenNetMeter/Compat/Properties/SettingsManager.cs
================================================
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Nodes;
using OpenNetMeter.Utilities;

namespace OpenNetMeter.Properties;

internal static class SettingsManager
{
    private static readonly string filePath;
    private static readonly object sync = new();
    public static AppSettings Current { get; } = new();

    static SettingsManager()
    {
        filePath = Path.Combine(Global.GetFilePath(), "settings.json");
        Load();
    }

    public static void Load()
    {
        lock (sync)
        {
            if (!File.Exists(filePath))
                return;

            try
            {
                var json = File.ReadAllText(filePath);
                var root = JsonNode.Parse(json) as JsonObject;
                if (root == null)
                    return;

                Current.DarkMode = GetBool(root, nameof(AppSettings.DarkMode), Current.DarkMode);
                Current.StartWithWin = GetBool(root, nameof(AppSettings.StartWithWin), Current.StartWithWin);
                Current.MinimizeOnStart = GetBool(root, nameof(AppSettings.MinimizeOnStart), Current.MinimizeOnStart);
                Current.WinPosX = GetInt(root, nameof(AppSettings.WinPosX), Current.WinPosX);
                Current.WinPosY = GetInt(root, nameof(AppSettings.WinPosY), Current.WinPosY);
                Current.WinWidth = GetInt(root, nameof(AppSettings.WinWidth), Current.WinWidth);
                Current.WinHeight = GetInt(root, nameof(AppSettings.WinHeight), Current.WinHeight);
                Current.MainWindowPositionInitialized = GetBool(root, nameof(AppSettings.MainWindowPositionInitialized), Current.MainWindowPositionInitialized);
                Current.MiniWidgetVisibility = GetBool(root, nameof(AppSettings.MiniWidgetVisibility), Current.MiniWidgetVisibility);
                Current.MiniWidgetPinned = GetBool(root, nameof(AppSettings.MiniWidgetPinned), Current.MiniWidgetPinned);
                Current.MiniWidgetPosX = GetInt(root, nameof(AppSettings.MiniWidgetPosX), Current.MiniWidgetPosX);
                Current.MiniWidgetPosY = GetInt(root, nameof(AppSettings.MiniWidgetPosY), Current.MiniWidgetPosY);
                Current.MiniWidgetPositionInitialized = GetBool(root, nameof(AppSettings.MiniWidgetPositionInitialized), Current.MiniWidgetPositionInitialized);
                Current.MiniWidgetTransparentSlider = GetInt(root, nameof(AppSettings.MiniWidgetTransparentSlider), Current.MiniWidgetTransparentSlider);
                Current.NetworkType = GetInt(root, nameof(AppSettings.NetworkType), Current.NetworkType);
                Current.NetworkSpeedFormat = GetInt(root, nameof(AppSettings.NetworkSpeedFormat), Current.NetworkSpeedFormat);
                Current.NetworkSpeedMagnitude = GetInt(root, nameof(AppSettings.NetworkSpeedMagnitude), Current.NetworkSpeedMagnitude);
            }
            catch (Exception ex)
            {
                EventLogger.Error("Error loading settings", ex);
            }
        }
    }

    public static void Save()
    {
        lock (sync)
        {
            JsonObject root;
            if (File.Exists(filePath))
            {
                try
                {
                    root = JsonNode.Parse(File.ReadAllText(filePath)) as JsonObject ?? [];
                }
                catch (Exception ex)
                {
                    EventLogger.Error("Error parsing existing settings file before save; creating new settings root", ex);
                    root = [];
                }
            }
            else
            {
                root = [];
            }

            root[nameof(AppSettings.DarkMode)] = Current.DarkMode;
            root[nameof(AppSettings.StartWithWin)] = Current.StartWithWin;
            root[nameof(AppSettings.MinimizeOnStart)] = Current.MinimizeOnStart;
            root[nameof(AppSettings.WinPosX)] = Current.WinPosX;
            root[nameof(AppSettings.WinPosY)] = Current.WinPosY;
            root[nameof(AppSettings.WinWidth)] = Current.WinWidth;
            root[nameof(AppSettings.WinHeight)] = Current.WinHeight;
            root[nameof(AppSettings.MainWindowPositionInitialized)] = Current.MainWindowPositionInitialized;
            root[nameof(AppSettings.MiniWidgetVisibility)] = Current.MiniWidgetVisibility;
            root[nameof(AppSettings.MiniWidgetPinned)] = Current.MiniWidgetPinned;
            root[nameof(AppSettings.MiniWidgetPosX)] = Current.MiniWidgetPosX;
            root[nameof(AppSettings.MiniWidgetPosY)] = Current.MiniWidgetPosY;
            root[nameof(AppSettings.MiniWidgetPositionInitialized)] = Current.MiniWidgetPositionInitialized;
            root[nameof(AppSettings.MiniWidgetTransparentSlider)] = Current.MiniWidgetTransparentSlider;
            root[nameof(AppSettings.NetworkType)] = Current.NetworkType;
            root[nameof(AppSettings.NetworkSpeedFormat)] = Current.NetworkSpeedFormat;
            root[nameof(AppSettings.NetworkSpeedMagnitude)] = Current.NetworkSpeedMagnitude;

            var json = root.ToJsonString(new JsonSerializerOptions
            {
                WriteIndented = true
            });
            File.WriteAllText(filePath, json);
        }
    }

    private static bool GetBool(JsonObject root, string key, bool fallback)
    {
        if (!root.TryGetPropertyValue(key, out var node) || node == null)
            return fallback;

        try
        {
            return node.GetValue<bool>();
        }
        catch (Exception ex)
        {
            EventLogger.Error($"Error reading bool setting '{key}'", ex);
            return fallback;
        }
    }

    private static int GetInt(JsonObject root, string key, int fallback)
    {
        if (!root.TryGetPropertyValue(key, out var node) || node == null)
            return fallback;

        try
        {
            return node.GetValue<int>();
        }
        catch (Exception ex)
        {
            EventLogger.Error($"Error reading int setting '{key}'", ex);
            return fallback;
        }
    }
}


================================================
FILE: OpenNetMeter/Compat/Utilities/ByteArray.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OpenNetMeter.Utilities
{
    internal class ByteArray
    {
        public static bool Compare(ReadOnlySpan<byte> a1, ReadOnlySpan<byte> a2)
        {
            return a1.SequenceEqual(a2);
        }
    }
}


================================================
FILE: OpenNetMeter/Compat/Utilities/EventLogger.cs
================================================
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

namespace OpenNetMeter.Utilities;

public static class EventLogger
{
    private const string EventSourceName = "OpenNetMeter";
    private const int MaxEventLogMessageLength = 31839;

    private enum LogLevel
    {
        Information,
        Warning,
        Error
    }

    public static void Info(string message, int eventId = 1000, short category = 0)
    {
        WriteEntrySafe(message, LogLevel.Information, eventId, category);
    }

    public static void Warn(string message, int eventId = 2000, short category = 0)
    {
        WriteEntrySafe(message, LogLevel.Warning, eventId, category);
    }

    public static void Error(
        string message,
        int eventId = 3000,
        short category = 0,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string filePath = "",
        [CallerLineNumber] int lineNumber = 0)
    {
        string logMessage = AddCallerContext(message, memberName, filePath, lineNumber);
        WriteEntrySafe(logMessage, LogLevel.Error, eventId, category);
    }

    public static void Error(
        Exception ex,
        int eventId = 3001,
        short category = 0,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string filePath = "",
        [CallerLineNumber] int lineNumber = 0)
    {
        Error("Unhandled exception", ex, eventId, category, memberName, filePath, lineNumber);
    }

    public static void Error(
        string message,
        Exception ex,
        int eventId = 3001,
        short category = 0,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string filePath = "",
        [CallerLineNumber] int lineNumber = 0)
    {
        string exceptionText = ex is AggregateException aggregateEx
            ? aggregateEx.Flatten().ToString()
            : ex.ToString();
        string errorMessage = $"{message}{Environment.NewLine}{exceptionText}";
        string logMessage = AddCallerContext(errorMessage, memberName, filePath, lineNumber);
        WriteEntrySafe(logMessage, LogLevel.Error, eventId, category);
    }

    private static string AddCallerContext(string message, string memberName, string filePath, int lineNumber)
    {
        string fileName = string.IsNullOrWhiteSpace(filePath) ? "unknown" : Path.GetFileName(filePath);
        return $"[{fileName}:{lineNumber} in {memberName}] {message}";
    }

    private static void WriteEntrySafe(string message, LogLevel level, int eventId, short category)
    {
        string safeMessage = Truncate(message);
        Debug.WriteLine(safeMessage);

        if (!OperatingSystem.IsWindows())
            return;

        try
        {
            WriteEntryWindows(safeMessage, level, eventId, category);
        }
        catch (Exception writeEx)
        {
            Debug.WriteLine($"[EventLogger fallback] Failed to write to Windows Event Log: {writeEx}");
        }
    }

    [SupportedOSPlatform("windows")]
    private static void WriteEntryWindows(string message, LogLevel level, int eventId, short category)
    {
        var entryType = level switch
        {
            LogLevel.Information => EventLogEntryType.Information,
            LogLevel.Warning => EventLogEntryType.Warning,
            _ => EventLogEntryType.Error
        };

        EventLog.WriteEntry(EventSourceName, message, entryType, eventId, category);
    }

    private static string Truncate(string message)
    {
        if (message.Length <= MaxEventLogMessageLength)
            return message;

        const string suffix = "... [truncated]";
        int maxLength = MaxEventLogMessageLength - suffix.Length;
        return message[..Math.Max(0, maxLength)] + suffix;
    }
}


================================================
FILE: OpenNetMeter/Compat/Utilities/PeriodicWork.cs
================================================
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace OpenNetMeter.Utilities
{
    internal sealed class PeriodicWork : IDisposable, IAsyncDisposable
    {
        private readonly string name;
        private readonly TimeSpan interval;
        private readonly SemaphoreSlim stopLock = new SemaphoreSlim(1, 1);

        private Task? runTask;
        private PeriodicTimer? timer;
        private CancellationTokenSource? cts;

        public PeriodicWork(string name, TimeSpan interval)
        {
            this.name = name;
            this.interval = interval;
        }

        public void Start(Func<CancellationToken, Task> onTick)
        {
            if (runTask != null)
                return; // already running

            cts = new CancellationTokenSource();
            timer = new PeriodicTimer(interval);
            runTask = RunAsync(onTick, cts.Token, timer);
        }

        private async Task RunAsync(Func<CancellationToken, Task> onTick, CancellationToken token, PeriodicTimer timer)
        {
            try
            {
                while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))
                {
                    try
                    {
                        await onTick(token).ConfigureAwait(false);
                    }
                    catch (OperationCanceledException)
                    {
                        EventLogger.Warn($"{name} tick cancelled");
                        throw;
                    }
                    catch (Exception ex)
                    {
                        EventLogger.Error($"{name} tick error", ex);
                    }
                }
            }
            catch (OperationCanceledException)
            {
                Debug.WriteLine($"{name} task cancelled");
                EventLogger.Warn($"{name} task cancelled");
            }
        }

        public async Task StopAsync()
        {
            await stopLock.WaitAsync().ConfigureAwait(false);
            try
            {
                if (runTask == null)
                    return;

                cts?.Cancel();
                try
                {
                    await runTask.ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    EventLogger.Warn($"{name} stop cancelled");
                }
            }
            finally
            {
                timer?.Dispose();
                cts?.Dispose();
                runTask = null;
                timer = null;
                cts = null;
                stopLock.Release();
            }
        }

        public void Stop()
        {
            StopAsync().GetAwaiter().GetResult();
        }

        public void Dispose()
        {
            Stop();
        }

        public ValueTask DisposeAsync()
        {
            return new ValueTask(StopAsync());
        }
    }
}


================================================
FILE: OpenNetMeter/OpenNetMeter.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <Version>$(ProductVersion)</Version>
    <AssemblyName>$(ProductName)</AssemblyName>
    <ApplicationManifest>app.manifest</ApplicationManifest>
    <ApplicationIcon>..\Resources\AppIcon.ico</ApplicationIcon>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Avalonia" Version="11.3.12" />
    <PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.12" />
    <PackageReference Include="Avalonia.Desktop" Version="11.3.12" />
    <PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.12" />
    <PackageReference Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc6.1" />
    <PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.8" />
    <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.6" />
    <PackageReference Include="System.Diagnostics.EventLog" Version="8.0.0" />
    <PackageReference Include="System.Drawing.Common" Version="8.0.12" />
    <PackageReference Include="TaskScheduler" Version="2.11.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\OpenNetMeter.Core\OpenNetMeter.Core.csproj" />
    <ProjectReference Include="..\OpenNetMeter.PlatformAbstractions\OpenNetMeter.PlatformAbstractions.csproj" />
  </ItemGroup>

  <ItemGroup>
    <AvaloniaResource Include="Assets\**" />
  </ItemGroup>
</Project>


================================================
FILE: OpenNetMeter/Program.cs
================================================
using System;
using Avalonia;

namespace OpenNetMeter;

internal sealed class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
        BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
    }

    public static AppBuilder BuildAvaloniaApp()
    {
        return AppBuilder.Configure<App>()
            .UsePlatformDetect()
            .LogToTrace();
    }
}


================================================
FILE: OpenNetMeter/Services/AvaloniaThemeService.cs
================================================
using Avalonia;
using Avalonia.Styling;

namespace OpenNetMeter.Services;

public sealed class AvaloniaThemeService : IThemeService
{
    private readonly Application application;

    public AvaloniaThemeService(Application application)
    {
        this.application = application;
    }

    public void ApplyDarkMode(bool enabled)
    {
        application.RequestedThemeVariant = enabled
            ? ThemeVariant.Dark
            : ThemeVariant.Light;
    }
}


================================================
FILE: OpenNetMeter/Services/AvaloniaWindowService.cs
================================================
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using OpenNetMeter.PlatformAbstractions;

namespace OpenNetMeter.Services;

public sealed class AvaloniaWindowService : IWindowService
{
    public void MinimizeMainWindow()
    {
        if (GetMainWindow() is { } window)
            window.WindowState = WindowState.Minimized;
    }

    public void CloseMainWindow()
    {
        if (GetMainWindow() is { } window)
            window.Close();
    }

    public void ShowAbout()
    {
        // Placeholder until About dialog content is migrated to Avalonia.
    }

    private static Window? GetMainWindow()
    {
        return (Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow;
    }
}



================================================
FILE: OpenNetMeter/Services/ExternalLinkService.cs
================================================
using System;
using System.Diagnostics;
using OpenNetMeter.PlatformAbstractions;
using OpenNetMeter.Utilities;

namespace OpenNetMeter.Services;

public sealed class ExternalLinkService : IExternalLinkService
{
    public void Open(string uri)
    {
        if (string.IsNullOrWhiteSpace(uri))
            return;

        try
        {
            Process.Start(new ProcessStartInfo
            {
                FileName = uri,
                UseShellExecute = true
            });
        }
        catch (Exception ex)
        {
            EventLogger.Error($"Failed to open external link '{uri}'", ex);
        }
    }
}



================================================
FILE: OpenNetMeter/Services/IMiniWidgetService.cs
================================================
using Avalonia.Controls;

namespace OpenNetMeter.Services;

public interface IMiniWidgetService : System.IDisposable
{
    event System.Action<bool>? VisibilityChanged;

    void Show();
    void Hide();
    void RefreshAppearance(bool darkMode, int transparency);
    void ResetPosition(Window mainWindow);
    void EnsurePositionOnScreen(Window mainWindow);
}



================================================
FILE: OpenNetMeter/Services/IThemeService.cs
================================================
namespace OpenNetMeter.Services;

public interface IThemeService
{
    void ApplyDarkMode(bool enabled);
}


================================================
FILE: OpenNetMeter/Services/ITrayNotificationService.cs
================================================
using System;
using Avalonia.Controls;

namespace OpenNetMeter.Services;

public interface ITrayNotificationService : IDisposable
{
    void ShowMinimizedToTrayOnce(Window mainWindow);
}


================================================
FILE: OpenNetMeter/Services/ITrayService.cs
================================================
namespace OpenNetMeter.Services;

public interface ITrayService : System.IDisposable
{
}



================================================
FILE: OpenNetMeter/Services/NoOpThemeService.cs
================================================
namespace OpenNetMeter.Services;

public sealed class NoOpThemeService : IThemeService
{
    public void ApplyDarkMode(bool enabled)
    {
    }
}


================================================
FILE: OpenNetMeter/Services/PlaceholderMiniWidgetService.cs
================================================
using Avalonia.Controls;

namespace OpenNetMeter.Services;

public sealed class PlaceholderMiniWidgetService : IMiniWidgetService
{
    public event System.Action<bool>? VisibilityChanged
    {
        add { }
        remove { }
    }

    public void Show()
    {
    }

    public void Hide()
    {
    }

    public void RefreshAppearance(bool darkMode, int transparency)
    {
    }

    public void ResetPosition(Window mainWindow)
    {
    }

    public void EnsurePositionOnScreen(Window mainWindow)
    {
    }

    public void Dispose()
    {
    }
}



================================================
FILE: OpenNetMeter/Services/PlaceholderNetworkCaptureService.cs
================================================
using System;
using OpenNetMeter.PlatformAbstractions;

namespace OpenNetMeter.Services;

public sealed class PlaceholderNetworkCaptureService : INetworkCaptureService
{
    private bool started;

    public event EventHandler<NetworkSnapshotChangedEventArgs>? NetworkChanged;
    public event EventHandler<NetworkTrafficEventArgs>? TrafficObserved
    {
        add { }
        remove { }
    }

    public void Start()
    {
        if (started)
            return;

        started = true;
        NetworkChanged?.Invoke(this, new NetworkSnapshotChangedEventArgs(string.Empty, string.Empty));
    }

    public void Stop()
    {
        started = false;
    }

    public void Dispose()
    {
        Stop();
    }
}



================================================
FILE: OpenNetMeter/Services/PlaceholderProcessIconService.cs
================================================
using OpenNetMeter.PlatformAbstractions;

namespace OpenNetMeter.Services;

public sealed class PlaceholderProcessIconService : IProcessIconService
{
    public object? GetProcessIcon(string processName) => null;
}



================================================
FILE: OpenNetMeter/Services/PlaceholderStartupRegistrationService.cs
================================================
using OpenNetMeter.PlatformAbstractions;

namespace OpenNetMeter.Services;

public sealed class PlaceholderStartupRegistrationService : IStartupRegistrationService
{
    public bool IsEnabled() => false;

    public void SetEnabled(bool enabled, bool startMinimized)
    {
    }
}



================================================
FILE: OpenNetMeter/Services/PlaceholderTrayNotificationService.cs
================================================
using Avalonia.Controls;

namespace OpenNetMeter.Services;

public sealed class PlaceholderTrayNotificationService : ITrayNotificationService
{
    public void ShowMinimizedToTrayOnce(Window mainWindow)
    {
    }

    public void Dispose()
    {
    }
}


================================================
FILE: OpenNetMeter/Services/PlaceholderTrayService.cs
================================================
namespace OpenNetMeter.Services;

public sealed class PlaceholderTrayService : ITrayService
{
    public void Dispose()
    {
    }
}



================================================
FILE: OpenNetMeter/Services/UpdateChecker.cs
================================================
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

namespace OpenNetMeter.Services;

public static class UpdateChecker
{
    public static async Task<(Version? latestVersion, string? downloadUrl)> CheckForUpdatesAsync()
    {
        using var client = new HttpClient();
        client.DefaultRequestHeaders.Add("User-Agent", "OpenNetMeter");

        using var response = await client.GetAsync($"https://api.github.com/repos/Ashfaaq18/OpenNetMeter/releases/latest");
        if (!response.IsSuccessStatusCode)
        {
            return (null, null);
        }

        await using var stream = await response.Content.ReadAsStreamAsync();
        using var document = await JsonDocument.ParseAsync(stream);
        JsonElement root = document.RootElement;

        if (!root.TryGetProperty("tag_name", out JsonElement tagElement))
        {
            return (null, null);
        }

        string? latestTag = tagElement.GetString();
        if (string.IsNullOrWhiteSpace(latestTag))
        {
            return (null, null);
        }

        string normalizedVersion = latestTag.Trim();
        if (normalizedVersion.StartsWith('v') || normalizedVersion.StartsWith('V'))
        {
            normalizedVersion = normalizedVersion[1..];
        }

        if (!Version.TryParse(normalizedVersion, out Version? latestVersion))
        {
            return (null, null);
        }

        string? downloadUrl = null;
        if (root.TryGetProperty("assets", out JsonElement assetsElement) &&
            assetsElement.ValueKind == JsonValueKind.Array &&
            assetsElement.GetArrayLength() > 0)
        {
            JsonElement firstAsset = assetsElement[0];
            if (firstAsset.TryGetProperty("browser_download_url", out JsonElement downloadUrlElement))
            {
                downloadUrl = downloadUrlElement.GetString();
            }
        }

        return (latestVersion, downloadUrl);
    }
}



================================================
FILE: OpenNetMeter/Services/WindowsMiniWidgetService.cs
================================================
using System;
using Avalonia;
using Avalonia.Controls;
using OpenNetMeter.ViewModels;
using OpenNetMeter.Views;
using OpenNetMeter.Properties;
using OpenNetMeter.Utilities;

namespace OpenNetMeter.Services;

public sealed class WindowsMiniWidgetService : IMiniWidgetService
{
    private readonly Window mainWindow;
    private readonly MiniWidgetViewModel viewModel;
    private readonly MiniWidgetWindow window;
    private readonly WindowsWidgetZOrderHelper zOrderHelper;
    private bool positionTrackingEnabled;
    private bool restoringPosition;

    public event Action<bool>? VisibilityChanged;

    public WindowsMiniWidgetService(MiniWidgetViewModel viewModel, Window mainWindow)
    {
        this.mainWindow = mainWindow;
        this.viewModel = viewModel;
        window = new MiniWidgetWindow
        {
            DataContext = viewModel
        };
        zOrderHelper = new WindowsWidgetZOrderHelper(window);

        viewModel.SetActions(OpenMainWindow, Hide);
        window.Opened += Window_Opened;
        window.PositionChanged += Window_PositionChanged;
    }

    public void Show()
    {
        try
        {
            if (!window.IsVisible)
                window.Show();
            else
                window.Activate();

            zOrderHelper.Start();
            VisibilityChanged?.Invoke(true);
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to show mini widget window", ex);
        }
    }

    public void Hide()
    {
        try
        {
            if (window.IsVisible)
                window.Hide();

            zOrderHelper.Stop();
            VisibilityChanged?.Invoke(false);
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to hide mini widget window", ex);
        }
    }

    public void RefreshAppearance(bool darkMode, int transparency)
    {
        viewModel.RefreshBackground(darkMode, transparency);
    }

    public void ResetPosition(Window mainWindow)
    {
        try
        {
            restoringPosition = true;

            var mainWidth = Math.Max(1, (int)Math.Round(mainWindow.Bounds.Width));
            var mainHeight = Math.Max(1, (int)Math.Round(mainWindow.Bounds.Height));
            var widgetWidth = Math.Max(1, (int)Math.Round(window.Bounds.Width > 0 ? window.Bounds.Width : window.Width));
            var widgetHeight = Math.Max(1, (int)Math.Round(window.Bounds.Height > 0 ? window.Bounds.Height : window.Height));

            var x = mainWindow.Position.X + (mainWidth / 2) - (widgetWidth / 2);
            var y = mainWindow.Position.Y + (mainHeight / 2) - (widgetHeight / 2);

            window.Position = new PixelPoint(x, y);
            SaveWindowPosition();
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to reset mini widget window position", ex);
        }
        finally
        {
            restoringPosition = false;
        }
    }

    public void EnsurePositionOnScreen(Window mainWindow)
    {
        try
        {
            if (!SettingsManager.Current.MiniWidgetPositionInitialized || !IsWindowInBounds(window))
                ResetPosition(mainWindow);
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to validate mini widget window position", ex);
        }
    }

    public void Dispose()
    {
        try
        {
            zOrderHelper.Dispose();
            window.Close();
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to dispose mini widget window", ex);
        }
    }

    private void OpenMainWindow()
    {
        try
        {
            if (!mainWindow.IsVisible)
                mainWindow.Show();

            if (mainWindow.WindowState == WindowState.Minimized)
                mainWindow.WindowState = WindowState.Normal;

            mainWindow.Activate();
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to open main window from mini widget", ex);
        }
    }

    private void Window_Opened(object? sender, EventArgs e)
    {
        try
        {
            if (SettingsManager.Current.MiniWidgetPositionInitialized)
            {
                restoringPosition = true;
                window.Position = new PixelPoint(SettingsManager.Current.MiniWidgetPosX, SettingsManager.Current.MiniWidgetPosY);
                restoringPosition = false;
            }
            else
            {
                SaveWindowPosition();
            }

            positionTrackingEnabled = true;
        }
        catch (Exception ex)
        {
            restoringPosition = false;
            EventLogger.Error("Failed to restore mini widget window position", ex);
        }
    }

    private void Window_PositionChanged(object? sender, PixelPointEventArgs e)
    {
        if (restoringPosition || !positionTrackingEnabled)
            return;

        try
        {
            SaveWindowPosition();
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to save mini widget window position", ex);
        }
    }

    private void SaveWindowPosition()
    {
        SettingsManager.Current.MiniWidgetPosX = window.Position.X;
        SettingsManager.Current.MiniWidgetPosY = window.Position.Y;
        SettingsManager.Current.MiniWidgetPositionInitialized = true;
        SettingsManager.Save();
    }

    private static bool IsWindowInBounds(Window target)
    {
        var screens = target.Screens?.All;
        if (screens is null || screens.Count == 0)
            return true;

        var width = Math.Max(1, (int)Math.Round(target.Bounds.Width > 0 ? target.Bounds.Width : target.Width));
        var height = Math.Max(1, (int)Math.Round(target.Bounds.Height > 0 ? target.Bounds.Height : target.Height));
        const int margin = 32;

        foreach (var screen in screens)
        {
            // Use full screen bounds here, not WorkingArea, so a widget intentionally
            // positioned over the taskbar is still considered a valid persisted position.
            var area = screen.Bounds;
            var areaRight = area.X + area.Width;
            var areaBottom = area.Y + area.Height;
            var targetRight = target.Position.X + width;
            var targetBottom = target.Position.Y + height;

            if (area.X < targetRight - margin &&
                areaRight > target.Position.X + margin &&
                area.Y < targetBottom - margin &&
                areaBottom > target.Position.Y + margin)
            {
                return true;
            }
        }

        return false;
    }
}



================================================
FILE: OpenNetMeter/Services/WindowsNetworkCaptureService.cs
================================================
using System;
using System.ComponentModel;
using System.Runtime.Versioning;
using OpenNetMeter.Models;
using OpenNetMeter.PlatformAbstractions;

namespace OpenNetMeter.Services;

[SupportedOSPlatform("windows")]
public sealed class WindowsNetworkCaptureService : INetworkCaptureService
{
    private NetworkProcess? networkProcess;
    private readonly object syncLock = new();
    private bool disposed;

    public event EventHandler<NetworkSnapshotChangedEventArgs>? NetworkChanged;
    public event EventHandler<NetworkTrafficEventArgs>? TrafficObserved;

    public void Start()
    {
        lock (syncLock)
        {
            ThrowIfDisposed();
            if (networkProcess != null)
                return;

            networkProcess = new NetworkProcess();
            networkProcess.PropertyChanged += NetworkProcess_PropertyChanged;
            networkProcess.Initialize();
        }
    }

    public void Stop()
    {
        lock (syncLock)
        {
            if (networkProcess == null)
                return;

            networkProcess.PropertyChanged -= NetworkProcess_PropertyChanged;
            networkProcess.Dispose();
            networkProcess = null;
        }
    }

    public void Dispose()
    {
        lock (syncLock)
        {
            if (disposed)
                return;

            Stop();
            disposed = true;
        }
    }

    private void NetworkProcess_PropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        if (networkProcess == null)
            return;

        switch (e.PropertyName)
        {
            case nameof(NetworkProcess.IsNetworkOnline):
                var isDisconnected = string.Equals(
                    networkProcess.IsNetworkOnline,
                    "Disconnected",
                    StringComparison.OrdinalIgnoreCase);

                NetworkChanged?.Invoke(
                    this,
                    new NetworkSnapshotChangedEventArgs(
                        isDisconnected ? string.Empty : networkProcess.IsNetworkOnline,
                        isDisconnected ? string.Empty : networkProcess.CurrentAdapterId));
                break;
            case nameof(NetworkProcess.DownloadSpeed):
                EmitProcessTraffic();
                break;
        }
    }

    private void EmitProcessTraffic()
    {
        if (networkProcess == null)
            return;

        networkProcess.IsBufferTime = true;
        lock (networkProcess.MyProcesses)
        {
            foreach (var app in networkProcess.MyProcesses)
            {
                if (app.Value == null)
                    continue;

                if (app.Value.CurrentDataRecv > 0)
                    TrafficObserved?.Invoke(this, new NetworkTrafficEventArgs(app.Key, app.Value.CurrentDataRecv, isReceive: true));

                if (app.Value.CurrentDataSend > 0)
                    TrafficObserved?.Invoke(this, new NetworkTrafficEventArgs(app.Key, app.Value.CurrentDataSend, isReceive: false));

                StageForDatabase(app.Key, app.Value.CurrentDataRecv, app.Value.CurrentDataSend);
            }

            networkProcess.MyProcesses.Clear();
        }

        networkProcess.IsBufferTime = false;
        lock (networkProcess.MyProcessesBuffer)
        {
            foreach (var app in networkProcess.MyProcessesBuffer)
            {
                if (app.Value == null)
                    continue;

                if (app.Value.CurrentDataRecv > 0)
                    TrafficObserved?.Invoke(this, new NetworkTrafficEventArgs(app.Key, app.Value.CurrentDataRecv, isReceive: true));

                if (app.Value.CurrentDataSend > 0)
                    TrafficObserved?.Invoke(this, new NetworkTrafficEventArgs(app.Key, app.Value.CurrentDataSend, isReceive: false));

                StageForDatabase(app.Key, app.Value.CurrentDataRecv, app.Value.CurrentDataSend);
            }

            networkProcess.MyProcessesBuffer.Clear();
        }
    }

    private void StageForDatabase(string processName, long receivedBytes, long sentBytes)
    {
        if (networkProcess == null)
            return;

        if (receivedBytes <= 0 && sentBytes <= 0)
            return;

        lock (networkProcess.PushToDBBuffer)
        {
            if (!networkProcess.PushToDBBuffer.TryGetValue(processName, out var pending) || pending == null)
            {
                pending = new MyProcess_Small(processName, 0, 0);
                networkProcess.PushToDBBuffer[processName] = pending;
            }

            pending.CurrentDataRecv += receivedBytes;
            pending.CurrentDataSend += sentBytes;
        }
    }

    private void ThrowIfDisposed()
    {
        if (disposed)
            throw new ObjectDisposedException(nameof(WindowsNetworkCaptureService));
    }
}



================================================
FILE: OpenNetMeter/Services/WindowsProcessIconService.cs
================================================
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.Versioning;
using Avalonia.Media.Imaging;
using OpenNetMeter.PlatformAbstractions;
using OpenNetMeter.Utilities;
using AvaloniaBitmap = Avalonia.Media.Imaging.Bitmap;

namespace OpenNetMeter.Services;

[SupportedOSPlatform("windows")]
public sealed class WindowsProcessIconService : IProcessIconService
{
    private readonly ConcurrentDictionary<string, AvaloniaBitmap?> cache = new(StringComparer.OrdinalIgnoreCase);
    private static readonly AvaloniaBitmap? DefaultIcon = CreateDefaultIcon();

    public object? GetProcessIcon(string processName)
    {
        if (string.IsNullOrWhiteSpace(processName))
            return DefaultIcon;

        return cache.GetOrAdd(processName, LoadIcon);
    }

    private static AvaloniaBitmap? LoadIcon(string processName)
    {
        var nameWithoutExtension = Path.GetFileNameWithoutExtension(processName);
        if (string.IsNullOrWhiteSpace(nameWithoutExtension))
            return DefaultIcon;

        try
        {
            var processes = Process.GetProcessesByName(nameWithoutExtension);
            try
            {
                foreach (var process in processes)
                {
                    try
                    {
                        var path = process.MainModule?.FileName;
                        if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
                            continue;

                        using Icon? icon = Icon.ExtractAssociatedIcon(path);
                        if (icon == null)
                            continue;

                        using var bitmap = icon.ToBitmap();
                        using var ms = new MemoryStream();
                        bitmap.Save(ms, ImageFormat.Png);
                        ms.Position = 0;
                        return new AvaloniaBitmap(ms);
                    }
                    catch (Exception ex)
                    {
                        EventLogger.Error($"Failed to fetch icon for process '{processName}' from candidate instance", ex);
                    }
                }
            }
            finally
            {
                foreach (var process in processes)
                    process.Dispose();
            }
        }
        catch (Exception ex)
        {
            EventLogger.Error($"Failed to resolve process icon for '{processName}'", ex);
        }

        return DefaultIcon;
    }

    private static AvaloniaBitmap? CreateDefaultIcon()
    {
        try
        {
            using var icon = (Icon)SystemIcons.Application.Clone();
            using var bitmap = icon.ToBitmap();
            using var ms = new MemoryStream();
            bitmap.Save(ms, ImageFormat.Png);
            ms.Position = 0;
            return new AvaloniaBitmap(ms);
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to create default process icon", ex);
            return null;
        }
    }
}



================================================
FILE: OpenNetMeter/Services/WindowsStartupRegistrationService.cs
================================================
using System;
using System.IO;
using System.Runtime.Versioning;
using OpenNetMeter.PlatformAbstractions;
using OpenNetMeter.Utilities;
using TaskScheduler = Microsoft.Win32.TaskScheduler;

namespace OpenNetMeter.Services;

[SupportedOSPlatform("windows")]
public sealed class WindowsStartupRegistrationService : IStartupRegistrationService
{
    private const string TaskFolder = "OpenNetMeter";
    private const string TaskName = "OpenNetMeterLogon";

    public bool IsEnabled()
    {
        try
        {
            TaskScheduler.TaskFolder folder = TaskScheduler.TaskService.Instance.RootFolder.SubFolders[TaskFolder];
            return folder.Tasks.Exists(TaskName);
        }
        catch
        {
            return false;
        }
    }

    public void SetEnabled(bool enabled, bool startMinimized)
    {
        try
        {
            TaskScheduler.TaskFolder folder = TaskScheduler.TaskService.Instance.RootFolder.SubFolders[TaskFolder];
            if (!enabled)
            {
                for (int i = 0; i < folder.Tasks.Count; i++)
                {
                    folder.DeleteTask(folder.Tasks[i].Name);
                }

                TaskScheduler.TaskService.Instance.RootFolder.DeleteFolder(TaskFolder);
                return;
            }
        }
        catch (Exception ex)
        {
            EventLogger.Error("Error while updating startup task registration", ex);
        }

        if (enabled)
        {
            try
            {
                TaskScheduler.TaskService.Instance.RootFolder.CreateFolder(TaskFolder);
                CreateTask(startMinimized);
            }
            catch (Exception ex)
            {
                EventLogger.Error("Error creating startup task folder/definition", ex);
            }
        }
    }

    private static void CreateTask(bool startMinimized)
    {
        try
        {
            TaskScheduler.TaskDefinition td = TaskScheduler.TaskService.Instance.NewTask();
            td.RegistrationInfo.Description = "Run OpenNetMeter Avalonia on system log on";
            td.Principal.RunLevel = TaskScheduler.TaskRunLevel.Highest;
            td.Principal.LogonType = TaskScheduler.TaskLogonType.InteractiveToken;
            td.Settings.DisallowStartIfOnBatteries = false;
            td.Settings.StopIfGoingOnBatteries = false;
            td.Settings.Compatibility = TaskScheduler.TaskCompatibility.V2_3;

            TaskScheduler.LogonTrigger logonTrigger = new TaskScheduler.LogonTrigger
            {
                Enabled = true,
                UserId = null
            };
            td.Triggers.Add(logonTrigger);

            (string path, string? arguments) = ResolveLaunchCommand(startMinimized);

            TaskScheduler.ExecAction action = new TaskScheduler.ExecAction
            {
                Path = path,
                Arguments = arguments ?? string.Empty
            };

            td.Actions.Add(action);

            TaskScheduler.TaskService.Instance.RootFolder.SubFolders[TaskFolder].RegisterTaskDefinition(
                TaskName,
                td,
                TaskScheduler.TaskCreation.CreateOrUpdate,
                null,
                null,
                td.Principal.LogonType);
        }
        catch (Exception ex)
        {
            EventLogger.Error("Error creating startup task", ex);
        }
    }

    private static (string path, string? arguments) ResolveLaunchCommand(bool startMinimized)
    {
        string? processPath = Environment.ProcessPath;
        string minimizedArgument = startMinimized ? " /StartMinimized" : string.Empty;

        if (!string.IsNullOrWhiteSpace(processPath) &&
            !string.Equals(Path.GetFileName(processPath), "dotnet.exe", StringComparison.OrdinalIgnoreCase) &&
            !string.Equals(Path.GetFileName(processPath), "dotnet", StringComparison.OrdinalIgnoreCase))
        {
            return (processPath, startMinimized ? "/StartMinimized" : null);
        }

        string baseDirectory = AppContext.BaseDirectory;
        string assemblyFileName = $"{AppDomain.CurrentDomain.FriendlyName}.dll";
        string managedEntryPoint = Path.Combine(baseDirectory, assemblyFileName);

        if (File.Exists(managedEntryPoint))
        {
            return ("dotnet", $"\"{managedEntryPoint}\"{minimizedArgument}");
        }

        string fallbackExe = Path.Combine(AppContext.BaseDirectory, "OpenNetMeter.exe");
        return (fallbackExe, startMinimized ? "/StartMinimized" : null);
    }
}



================================================
FILE: OpenNetMeter/Services/WindowsTrayNotificationService.cs
================================================
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using Avalonia.Controls;
using OpenNetMeter.Utilities;

namespace OpenNetMeter.Services;

[SupportedOSPlatform("windows")]
public sealed class WindowsTrayNotificationService : ITrayNotificationService
{
    private const uint NIM_ADD = 0x00000000;
    private const uint NIM_MODIFY = 0x00000001;
    private const uint NIM_DELETE = 0x00000002;

    private const uint NIF_ICON = 0x00000002;
    private const uint NIF_TIP = 0x00000004;
    private const uint NIF_INFO = 0x00000010;

    private const uint NIIF_NONE = 0x00000000;
    private const uint BalloonIconId = 0x4F4E4D;

    private bool hasShownMinimizedNotification;
    private Icon? balloonIcon;

    public void ShowMinimizedToTrayOnce(Window mainWindow)
    {
        if (hasShownMinimizedNotification)
            return;

        try
        {
            var handle = mainWindow.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero;
            if (handle == IntPtr.Zero)
                return;

            balloonIcon?.Dispose();
            balloonIcon = LoadNotificationIcon();

            var addData = CreateNotifyIconData(handle, balloonIcon.Handle);
            addData.uFlags = NIF_ICON | NIF_TIP;
            addData.szTip = "OpenNetMeter";

            if (!Shell_NotifyIcon(NIM_ADD, ref addData))
                return;

            var infoData = CreateNotifyIconData(handle, balloonIcon.Handle);
            infoData.uFlags = NIF_INFO;
            infoData.szInfo = "Minimized to system tray";
            infoData.szInfoTitle = string.Empty;
            infoData.dwInfoFlags = NIIF_NONE;
            infoData.uTimeoutOrVersion = 1000;

            Shell_NotifyIcon(NIM_MODIFY, ref infoData);
            hasShownMinimizedNotification = true;

            _ = RemoveTemporaryIconAsync(handle);
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to show minimized-to-tray notification", ex);
        }
    }

    public void Dispose()
    {
        try
        {
            balloonIcon?.Dispose();
            balloonIcon = null;
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to dispose tray notification icon", ex);
        }
    }

    private async Task RemoveTemporaryIconAsync(IntPtr handle)
    {
        try
        {
            await Task.Delay(TimeSpan.FromSeconds(5));

            var deleteData = CreateNotifyIconData(handle, IntPtr.Zero);
            Shell_NotifyIcon(NIM_DELETE, ref deleteData);
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to remove tray notification icon", ex);
        }
    }

    private static Icon LoadNotificationIcon()
    {
        string? processPath = Environment.ProcessPath;
        if (!string.IsNullOrWhiteSpace(processPath) && System.IO.File.Exists(processPath))
        {
            var icon = Icon.ExtractAssociatedIcon(processPath);
            if (icon != null)
                return (Icon)icon.Clone();
        }

        return (Icon)SystemIcons.Application.Clone();
    }

    private static NOTIFYICONDATA CreateNotifyIconData(IntPtr handle, IntPtr iconHandle)
    {
        return new NOTIFYICONDATA
        {
            cbSize = (uint)Marshal.SizeOf<NOTIFYICONDATA>(),
            hWnd = handle,
            uID = BalloonIconId,
            hIcon = iconHandle
        };
    }

    [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
    private static extern bool Shell_NotifyIcon(uint dwMessage, ref NOTIFYICONDATA lpData);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct NOTIFYICONDATA
    {
        public uint cbSize;
        public IntPtr hWnd;
        public uint uID;
        public uint uFlags;
        public uint uCallbackMessage;
        public IntPtr hIcon;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string szTip;

        public uint dwState;
        public uint dwStateMask;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string szInfo;

        public uint uTimeoutOrVersion;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
        public string szInfoTitle;

        public uint dwInfoFlags;
        public Guid guidItem;
        public IntPtr hBalloonIcon;
    }
}


================================================
FILE: OpenNetMeter/Services/WindowsTrayService.cs
================================================
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform;
using OpenNetMeter.Views;
using OpenNetMeter.Utilities;

namespace OpenNetMeter.Services;

public sealed class WindowsTrayService : ITrayService
{
    private readonly TrayIcon trayIcon;

    public WindowsTrayService(
        Application application,
        IClassicDesktopStyleApplicationLifetime desktop,
        MainWindow mainWindow,
        IMiniWidgetService miniWidgetService)
    {
        var menu = new NativeMenu();

        var resetPositionsItem = new NativeMenuItem("Reset all window positions");
        resetPositionsItem.Click += (_, _) => mainWindow.ResetWindowPositions();
        menu.Add(resetPositionsItem);

        var showMiniWidgetItem = new NativeMenuItem("Show Mini Widget");
        showMiniWidgetItem.Click += (_, _) => miniWidgetService.Show();
        menu.Add(showMiniWidgetItem);

        menu.Add(new NativeMenuItemSeparator());

        var openItem = new NativeMenuItem("Open");
        openItem.Click += (_, _) => mainWindow.OpenFromTray();
        menu.Add(openItem);

        var exitItem = new NativeMenuItem("Exit");
        exitItem.Click += (_, _) =>
        {
            mainWindow.PrepareForExit();
            desktop.Shutdown();
        };
        menu.Add(exitItem);

        using var iconStream = AssetLoader.Open(new Uri("avares://OpenNetMeter/Assets/x48.png"));
        trayIcon = new TrayIcon
        {
            ToolTipText = "OpenNetMeter",
            Menu = menu,
            Icon = new WindowIcon(iconStream),
            IsVisible = true
        };

        trayIcon.Clicked += (_, _) => mainWindow.OpenFromTray();
        TrayIcon.SetIcons(application, new TrayIcons { trayIcon });
    }

    public void Dispose()
    {
        try
        {
            trayIcon.IsVisible = false;
            trayIcon.Dispose();
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to dispose tray icon", ex);
        }
    }
}



================================================
FILE: OpenNetMeter/Services/WindowsWidgetZOrderHelper.cs
================================================
using System;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Threading;
using OpenNetMeter.Utilities;

namespace OpenNetMeter.Services;

internal sealed class WindowsWidgetZOrderHelper : IDisposable
{
    private static readonly IntPtr HwndTopMost = new(-1);
    private const int GwlpHwndParent = -8;
    private const string ShellTrayWindowClass = "Shell_TrayWnd";

    private readonly Window window;
    private readonly DispatcherTimer timer;

    public WindowsWidgetZOrderHelper(Window window)
    {
        this.window = window;
        timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };
        timer.Tick += Timer_Tick;
    }

    public void Start()
    {
        timer.Start();
    }

    public void Stop()
    {
        timer.Stop();
    }

    public void Dispose()
    {
        timer.Stop();
        timer.Tick -= Timer_Tick;
    }

    private void Timer_Tick(object? sender, EventArgs e)
    {
        try
        {
            var windowHandle = window.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero;
            if (windowHandle == IntPtr.Zero)
                return;

            var shellTray = FindWindowEx(IntPtr.Zero, IntPtr.Zero, ShellTrayWindowClass, string.Empty);
            if (shellTray == IntPtr.Zero)
                return;

            var owner = GetWindowLongPtr(windowHandle, GwlpHwndParent);
            if (owner != shellTray)
                SetWindowLongPtr(windowHandle, GwlpHwndParent, shellTray);

            for (var current = windowHandle; current != IntPtr.Zero; current = GetWindow(current, 3))
            {
                if (current != shellTray)
                    continue;

                SetWindowPos(
                    windowHandle,
                    HwndTopMost,
                    0,
                    0,
                    0,
                    0,
                    0x4000 | 0x0010 | 0x0002 | 0x0001);
                break;
            }
        }
        catch (Exception ex)
        {
            EventLogger.Error("Failed to maintain mini widget z-order", ex);
        }
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr GetWindow(IntPtr hwnd, uint cmd);

    [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    private static extern IntPtr SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);

    [DllImport("user32.dll", EntryPoint = "GetWindowLongPtrW", SetLastError = true)]
    private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll", EntryPoint = "GetWindowLongW", SetLastError = true)]
    private static extern int GetWindowLong32(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW", SetLastError = true)]
    private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

    [DllImport("user32.dll", EntryPoint = "SetWindowLongW", SetLastError = true)]
    private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, int dwNewLong);

    private static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
    {
        return IntPtr.Size == 8
            ? GetWindowLongPtr64(hWnd, nIndex)
            : new IntPtr(GetWindowLong32(hWnd, nIndex));
    }

    private static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
    {
        return IntPtr.Size == 8
            ? SetWindowLongPtr64(hWnd, nIndex, dwNewLong)
            : new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32()));
    }
}



================================================
FILE: OpenNetMeter/ViewModels/ByteSizeFormatter.cs
================================================
namespace OpenNetMeter.ViewModels;

internal static class ByteSizeFormatter
{
    public static string FormatBytes(long value)
    {
        string[] suffix = { "B", "KB", "MB", "GB", "TB" };
        var current = (double)value;
        var unit = 0;

        while (current >= 1024 && unit < suffix.Length - 1)
        {
            current /= 1024;
            unit++;
        }

        return $"{current:0.##} {suffix[unit]}";
    }
}



================================================
FILE: OpenNetMeter/ViewModels/HistoryViewModel.cs
================================================
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows.Input;
using Avalonia.Media;
using Microsoft.Data.Sqlite;
using OpenNetMeter.Models;
using OpenNetMeter.PlatformAbstractions;
using OpenNetMeter.Utilities;

namespace OpenNetMeter.ViewModels;

public sealed class HistoryViewModel : INotifyPropertyChanged
{
    private readonly string dbPath;
    private readonly IProcessIconService processIconService;
    private string? selectedProfile;
    private DateTimeOffset? dateStart;
    private DateTimeOffset? dateEnd;
    private long totalDownload;
    private long totalUpload;
    private string? currentSortColumn;
    private bool sortDescending;

    public HistoryViewModel()
        : this(new NoOpProcessIconService())
    {
    }

    public HistoryViewModel(IProcessIconService processIconService)
    {
        this.processIconService = processIconService;
        dbPath = ResolveDatabasePath();

        Profiles = [];
        Rows = [];
        FilterCommand = new RelayCommand(ApplyFilter);
        SortRowsCommand = new ParameterRelayCommand(parameter =>
        {
            var column = parameter?.ToString();
            if (string.IsNullOrWhiteSpace(column))
                return;

            SortRows(column);
        });

        DateStart = DateTimeOffset.Now.Date.AddDays(-7);
        DateEnd = DateTimeOffset.Now.Date;

        LoadProfiles();
        if (Profiles.Count > 0)
        {
            SelectedProfile = Profiles[0];
            ApplyFilter();
        }
    }

    public ObservableCollection<string> Profiles { get; }

    public string? SelectedProfile
    {
        get => selectedProfile;
        set
        {
            if (selectedProfile == value)
                return;
            selectedProfile = value;
            OnPropertyChanged(nameof(SelectedProfile));
        }
    }

    public DateTimeOffset? DateStart
    {
        get => dateStart;
        set
        {
            if (dateStart == value)
                return;
            dateStart = value;
            OnPropertyChanged(nameof(DateStart));
        }
    }

    public DateTimeOffset? DateEnd
    {
        get => dateEnd;
        set
        {
            if (dateEnd == value)
                return;
            dateEnd = value;
            OnPropertyChanged(nameof(DateEnd));
        }
    }

    public ObservableCollection<HistoryRowViewModel> Rows { get; }

    public long TotalDownload
    {
        get => totalDownload;
        private set
        {
            if (totalDownload == value)
                return;
            totalDownload = value;
            OnPropertyChanged(nameof(TotalDownload));
            OnPropertyChanged(nameof(TotalDownloadText));
        }
    }

    public long TotalUpload
    {
        get => totalUpload;
        private set
        {
            if (totalUpload == value)
                return;
            totalUpload = value;
            OnPropertyChanged(nameof(TotalUpload));
            OnPropertyChanged(nameof(TotalUploadText));
        }
    }

    public string TotalDownloadText => ByteSizeFormatter.FormatBytes(TotalDownload);
    public string TotalUploadText => ByteSizeFormatter.FormatBytes(TotalUpload);

    public ICommand FilterCommand { get; }
    public ICommand SortRowsCommand { get; }

    public event PropertyChangedEventHandler? PropertyChanged;

    public void ReloadProfiles()
    {
        string? previousSelection = SelectedProfile;
        LoadProfiles();

        if (!string.IsNullOrWhiteSpace(previousSelection) && Profiles.Contains(previousSelection))
        {
            SelectedProfile = previousSelection;
        }
        else
        {
            SelectedProfile = Profiles.Count > 0 ? Profiles[0] : null;
        }
    }

    public void DeleteAllDbFiles()
    {
        try
        {
            ApplicationDB.CloseSharedConnection();
            DeleteIfExists(dbPath);
            DeleteIfExists($"{dbPath}-wal");
            DeleteIfExists($"{dbPath}-shm");
            DeleteIfExists($"{dbPath}-journal");

            Profiles.Clear();
            Rows.Clear();
            SelectedProfile = null;
            TotalDownload = 0;
            TotalUpload = 0;
        }
        catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
        {
            EventLogger.Error("Failed to delete usage database file", ex);
        }
    }

    private void LoadProfiles()
    {
        Profiles.Clear();

        if (!File.Exists(dbPath))
            return;

        using var connection = OpenReadOnlyConnection(dbPath);
        connection.Open();
        using var command = connection.CreateCommand();
        command.CommandText = "SELECT Name FROM Adapter ORDER BY Name";
        using var reader = command.ExecuteReader();
        while (reader.Read())
        {
            if (!reader.IsDBNull(0))
                Profiles.Add(reader.GetString(0));
        }
    }

    private void ApplyFilter()
    {
        Rows.Clear();
        TotalDownload = 0;
        TotalUpload = 0;

        if (string.IsNullOrWhiteSpace(SelectedProfile) || !File.Exists(dbPath))
            return;

        var start = (DateStart ?? DateTimeOffset.Now.Date).Date;
        var end = (DateEnd ?? DateTimeOffset.Now.Date).Date;
        if (end < start)
            (start, end) = (end, start);

        using var connection = OpenReadOnlyConnection(dbPath);
        connection.Open();

        using var command = connection.CreateCommand();
        command.CommandText =
            "SELECT p.Name, SUM(pd.DataReceived) AS TotalRecv, SUM(pd.DataSent) AS TotalSent " +
            "FROM ProcessDate pd " +
            "JOIN Process p ON p.ID = pd.ProcessID " +
            "JOIN Adapter a ON a.ID = pd.AdapterID " +
            "JOIN Date d ON d.ID = pd.DateID " +
            "WHERE a.Name = @AdapterName " +
            "AND (d.Year * 10000 + d.Month * 100 + d.Day) BETWEEN @StartDate AND @EndDate " +
            "GROUP BY p.ID, p.Name " +
            "ORDER BY p.Name";
        command.Parameters.AddWithValue("@AdapterName", SelectedProfile);
        command.Parameters.AddWithValue("@StartDate", ToDateInt(start));
        command.Parameters.AddWithValue("@EndDate", ToDateInt(end));

        using var reader = command.ExecuteReader();
        while (reader.Read())
        {
            var processName = reader.IsDBNull(0) ? string.Empty : reader.GetString(0);
            var download = reader.IsDBNull(1) ? 0 : reader.GetInt64(1);
            var upload = reader.IsDBNull(2) ? 0 : reader.GetInt64(2);

            var icon = processIconService.GetProcessIcon(processName) as IImage;
            Rows.Add(new HistoryRowViewModel(processName, download, upload, icon));
            TotalDownload += download;
            TotalUpload += upload;
        }
    }

    private void SortRows(string column)
    {
        if (string.Equals(currentSortColumn, column, StringComparison.Ordinal))
        {
            sortDescending = !sortDescending;
        }
        else
        {
            currentSortColumn = column;
            sortDescending = false;
        }

        var sorted = column switch
        {
            "Download" => sortDescending
                ? Rows.OrderByDescending(r => r.DownloadBytes).ToList()
                : Rows.OrderBy(r => r.DownloadBytes).ToList(),
            "Upload" => sortDescending
                ? Rows.OrderByDescending(r => r.UploadBytes).ToList()
                : Rows.OrderBy(r => r.UploadBytes).ToList(),
            _ => sortDescending
                ? Rows.OrderByDescending(r => r.ProcessName).ToList()
                : Rows.OrderBy(r => r.ProcessName).ToList()
        };

        Rows.Clear();
        foreach (var row in sorted)
            Rows.Add(row);
    }

    private static SqliteConnection OpenReadOnlyConnection(string path)
    {
        var csb = new SqliteConnectionStringBuilder
        {
            DataSource = path,
            Mode = SqliteOpenMode.ReadOnly
        };
        return new SqliteConnection(csb.ToString());
    }

    private static int ToDateInt(DateTime date)
    {
        return (date.Year * 10000) + (date.Month * 100) + date.Day;
    }

    private static string ResolveDatabasePath()
    {
        var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        var appFolder = Path.Combine(localAppData, "OpenNetMeter");
        return Path.Combine(appFolder, "OpenNetMeter.sqlite");
    }

    private static void DeleteIfExists(string path)
    {
        if (File.Exists(path))
            File.Delete(path);
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private sealed class NoOpProcessIconService : IProcessIconService
    {
        public object? GetProcessIcon(string processName) => null;
    }
}

public sealed class HistoryRowViewModel
{
    public HistoryRowViewModel(string processName, long downloadBytes, long uploadBytes, IImage? icon = null)
    {
        ProcessName = processName;
        DownloadBytes = downloadBytes;
        UploadBytes = uploadBytes;
        Icon = icon;
    }

    public IImage? Icon { get; }
    public string ProcessName { get; }
    public long DownloadBytes { get; }
    public long UploadBytes { get; }
    public string DownloadText => ByteSizeFormatter.FormatBytes(DownloadBytes);
    public string UploadText => ByteSizeFormatter.FormatBytes(UploadBytes);
}



================================================
FILE: OpenNetMeter/ViewModels/MainWindowViewModel.cs
================================================
using System;
using System.Reflection;
using System.Windows.Input;
using OpenNetMeter.Services;
using OpenNetMeter.Core.ViewModels;
using OpenNetMeter.PlatformAbstractions;

namespace OpenNetMeter.ViewModels;

public sealed class MainWindowViewModel : MainShellTabsViewModel, IDisposable
{
    private const string AboutRepositoryUri = "https://github.com/Ashfaaq18/OpenNetMeter";

    private readonly IWindowService windowService;
    private readonly INetworkCaptureService networkCaptureService;
    private readonly IExternalLinkService externalLinkService;
    private readonly MiniWidgetViewModel miniWidget;
    private string networkStatus = "Disconnected";
    private bool isAboutOpen;

    public MainWindowViewModel()
        : this(new NoOpWindowService(), new NoOpNetworkCaptureService(), new NoOpProcessIconService(), new NoOpExternalLinkService(), new MiniWidgetViewModel(), new PlaceholderMiniWidgetService(), new PlaceholderStartupRegistrationService(), new NoOpThemeService())
    {
    }

    public MainWindowViewModel(
        IWindowService windowService,
        INetworkCaptureService networkCaptureService,
        IProcessIconService processIconService,
        IExternalLinkService externalLinkService,
        MiniWidgetViewModel miniWidget,
        IMiniWidgetService miniWidgetService,
        IStartupRegistrationService startupRegistrationService,
        IThemeService themeService)
    {
        this.windowService = windowService;
        this.networkCaptureService = networkCaptureService;
        this.externalLinkService = externalLinkService;
        this.miniWidget = miniWidget;

        Summary = new SummaryViewModel(this.networkCaptureService, processIconService);
        History = new HistoryViewModel(processIconService);
        Settings = new SettingsViewModel(miniWidget, miniWidgetService, startupRegistrationService, externalLinkService, themeService);
        Settings.PropertyChanged += Settings_PropertyChanged;
        Settings.DeleteAllDataConfirmed += Settings_DeleteAllDataConfirmed;
        Summary.PropertyChanged += Summary_PropertyChanged;

        SwitchTabCommand = new ParameterRelayCommand(parameter =>
        {
            if (parameter is null)
                return;

            if (int.TryParse(parameter.ToString(), out var nextIndex))
                SelectedTabIndex = nextIndex;
        });

        AboutCommand = new RelayCommand(() => IsAboutOpen = true);
        CloseAboutCommand = new RelayCommand(() => IsAboutOpen = false);
        OpenAboutRepositoryCommand = new RelayCommand(() => this.externalLinkService.Open(AboutRepositoryUri));
        MinimizeWindowCommand = new RelayCommand(() => this.windowService.MinimizeMainWindow());
        CloseWindowCommand = new RelayCommand(() => this.windowService.CloseMainWindow());

        this.networkCaptureService.NetworkChanged += OnNetworkChanged;
        this.networkCaptureService.Start();
        History.ReloadProfiles();
        SyncMiniWidgetFromSummary();
    }

    public SummaryViewModel Summary { get; }
    public HistoryViewModel History { get; }
    public SettingsViewModel Settings { get; }
    public string AboutVersionText { get; } = $"Version: {Assembly.GetExecutingAssembly()?.GetName().Version}";
    public string AboutRepositoryUrl { get; } = AboutRepositoryUri;

    public ICommand SwitchTabCommand { get; }
    public ICommand AboutCommand { get; }
    public ICommand CloseAboutCommand { get; }
    public ICommand OpenAboutRepositoryCommand { get; }
    public ICommand MinimizeWindowCommand { get; }
    public ICommand CloseWindowCommand { get; }

    public bool IsSummaryTab => SelectedTabIndex == 0;
    public bool IsHistoryTab => SelectedTabIndex == 1;
    public bool IsSettingsTab => SelectedTabIndex == 2;

    public string NetworkStatus
    {
        get => networkStatus;
        private set
        {
            if (networkStatus == value)
                return;
            networkStatus = value;
            OnPropertyChanged(nameof(NetworkStatus));
        }
    }

    public bool IsAboutOpen
    {
        get => isAboutOpen;
        private set
        {
            if (isAboutOpen == value)
                return;

            isAboutOpen = value;
            OnPropertyChanged(nameof(IsAboutOpen));
        }
    }

    public override int SelectedTabIndex
    {
        get => base.SelectedTabIndex;
        set
        {
            if (base.SelectedTabIndex == value)
                return;

            base.SelectedTabIndex = value;
            OnPropertyChanged(nameof(IsSummaryTab));
            OnPropertyChanged(nameof(IsHistoryTab));
            OnPropertyChanged(nameof(IsSettingsTab));
        }
    }

    public void Dispose()
    {
        Settings.PropertyChanged -= Settings_PropertyChanged;
        Settings.DeleteAllDataConfirmed -= Settings_DeleteAllDataConfirmed;
        Summary.PropertyChanged -= Summary_PropertyChanged;
        Summary.Dispose();
        networkCaptureService.NetworkChanged -= OnNetworkChanged;
        networkCaptureService.Dispose();
    }

    private void OnNetworkChanged(object? sender, NetworkSnapshotChangedEventArgs e)
    {
        if (string.IsNullOrWhiteSpace(e.AdapterName))
        {
            NetworkStatus = "Disconnected";
            Summary.ClearOnDisconnect();
            return;
        }

        NetworkStatus = $"Connected : {e.AdapterName}";
        Summary.SetActiveAdapter(e.AdapterName);
        History.ReloadProfiles();
    }

    private void Settings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(SettingsViewModel.SelectedSpeedMagnitudeIndex) ||
            e.PropertyName == nameof(SettingsViewModel.SelectedSpeedUnitIndex))
        {
            Summary.RefreshSpeedDisplayFormat();
        }
    }

    private void Settings_DeleteAllDataConfirmed()
    {
        bool wasConnected = !string.Equals(NetworkStatus, "Disconnected", StringComparison.OrdinalIgnoreCase);

        if (wasConnected)
        {
            networkCaptureService.Stop();
            Summary.ClearOnDisconnect();
            NetworkStatus = "Disconnected";
        }

        History.DeleteAllDbFiles();

        if (wasConnected)
        {
            networkCaptureService.Start();
        }

        History.ReloadProfiles();
    }

    private void Summary_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case nameof(SummaryViewModel.DownloadSpeedText):
            case nameof(SummaryViewModel.UploadSpeedText):
            case nameof(SummaryViewModel.CurrentSessionDownloadText):
            case nameof(SummaryViewModel.CurrentSessionUploadText):
                SyncMiniWidgetFromSummary();
                break;
        }
    }

    private void SyncMiniWidgetFromSummary()
    {
        miniWidget.DownloadSpeedText = Summary.DownloadSpeedText;
        miniWidget.UploadSpeedText = Summary.UploadSpeedText;
        miniWidget.CurrentSessionDownloadText = Summary.CurrentSessionDownloadText;
        miniWidget.CurrentSessionUploadText = Summary.CurrentSessionUploadText;
    }

    private sealed class NoOpWindowService : IWindowService
    {
        public void MinimizeMainWindow() { }
        public void CloseMainWindow() { }
        public void ShowAbout() { }
    }

    private sealed class NoOpNetworkCaptureService : INetworkCaptureService
    {
        public event EventHandler<NetworkSnapshotChangedEventArgs>? NetworkChanged
        {
            add { }
            remove { }
        }
        public event EventHandler<NetworkTrafficEventArgs>? TrafficObserved
        {
            add { }
            remove { }
        }

        public void Start() { }
        public void Stop() { }
        public void Dispose() { }
    }

    private sealed class NoOpProcessIconService : IProcessIconService
    {
        public object? GetProcessIcon(string processName) => null;
    }

    private sealed class NoOpExternalLinkService : IExternalLinkService
    {
        public void Open(string uri) { }
    }
}



================================================
FILE: OpenNetMeter/ViewModels/MiniWidgetViewModel.cs
================================================
using System;
using System.ComponentModel;
using System.Windows.Input;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using OpenNetMeter.Properties;
using OpenNetMeter.Utilities;

namespace OpenNetMeter.ViewModels;

public sealed class MiniWidgetViewModel : INotifyPropertyChanged
{
    private static readonly Bitmap PinLightImage = LoadBitmap("avares://OpenNetMeter/Assets/pin/pin.png");
    private static readonly Bitmap PinDarkImage = LoadBitmap("avares://OpenNetMeter/Assets/pin/pin-dark.png");
    private static readonly Bitmap UnpinLightImage = LoadBitmap("avares://OpenNetMeter/Assets/pin/unpin.png");
    private static readonly Bitmap UnpinDarkImage = LoadBitmap("avares://OpenNetMeter/Assets/pin/unpin-dark.png");

    private string downloadSpeedText = "35.2 Mbps";
    private string uploadSpeedText = "4.8 Mbps";
    private string currentSessionDownloadText = "1.24 GB";
    private string currentSessionUploadText = "98.4 MB";
    private string backgroundColor = "#cc252525";
    private bool isPinned;
    private bool darkMode;
    private ICommand togglePinnedCommand;

    public string DownloadSpeedText
    {
        get => downloadSpeedText;
        set
        {
            if (downloadSpeedText == value)
                return;
            downloadSpeedText = value;
            OnPropertyChanged(nameof(DownloadSpeedText));
        }
    }

    public string UploadSpeedText
    {
        get => uploadSpeedText;
        set
        {
            if (uploadSpeedText == value)
                return;
            uploadSpeedText = value;
            OnPropertyChanged(nameof(UploadSpeedText));
        }
    }

    public string CurrentSessionDownloadText
    {
        get => currentSessionDownloadText;
        set
        {
            if (currentSessionDownloadText == value)
                return;
            currentSessionDownloadText = value;
            OnPropertyChanged(nameof(CurrentSessionDownloadText));
        }
    }

    public string CurrentSessionUploadText
    {
        get => currentSessionUploadText;
        set
        {
            if (currentSessionUploadText == value)
                return;
            currentSessionUploadText = value;
            OnPropertyChanged(nameof(CurrentSessionUploadText));
        }
    }

    public bool IsPinned
    {
        get => isPinned;
        set
        {
            if (isPinned == value)
                return;
            isPinned = value;
            SettingsManager.Current.MiniWidgetPinned = value;
            SettingsManager.Save();
            OnPropertyChanged(nameof(IsPinned));
            OnPropertyChanged(nameof(PinIconSource));
            OnPropertyChanged(nameof(PinToolTip));
        }
    }

    public string BackgroundColor
    {
        get => backgroundColor;
        private set
        {
            if (backgroundColor == value)
                return;
            backgroundColor = value;
            OnPropertyChanged(nameof(BackgroundColor));
        }
    }

    public IImage PinIconSource => IsPinned
        ? (darkMode ? UnpinDarkImage : UnpinLightImage)
        : (darkMode ? PinDarkImage : PinLightImage);

    public string PinToolTip => IsPinned ? "Unpin" : "Pin";

    public ICommand OpenMainWindowCommand { get; private set; } = new RelayCommand(() => { });
    public ICommand HideWidgetCommand { get; private set; } = new RelayCommand(() => { });
    public ICommand TogglePinnedCommand => togglePinnedCommand;

    public MiniWidgetViewModel()
    {
        togglePinnedCommand = new RelayCommand(() => IsPinned = !IsPinned);
        isPinned = SettingsManager.Current.MiniWidgetPinned;
        RefreshBackground(SettingsManager.Current.DarkMode, SettingsManager.Current.MiniWidgetTransparentSlider);
    }

    public void SetActions(Action openMainWindow, Action hideWidget)
    {
        OpenMainWindowCommand = new RelayCommand(openMainWindow);
        HideWidgetCommand = new RelayCommand(hideWidget);
        OnPropertyChanged(nameof(OpenMainWindowCommand));
        OnPropertyChanged(nameof(HideWidgetCommand));
    }

    public void RefreshBackground(bool darkMode, int transparency)
    {
        var clampedTransparency = Math.Clamp(transparency, 0, 100);
        var alpha = ((100 - clampedTransparency) * 255) / 100;
        this.darkMode = darkMode;
        BackgroundColor = darkMode
            ? $"#{alpha:x2}252525"
            : $"#{alpha:x2}f1f1f1";
        OnPropertyChanged(nameof(PinIconSource));
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    private static Bitmap LoadBitmap(string assetUri)
    {
        try
        {
            using var assetStream = AssetLoader.Open(new Uri(assetUri));
            return new Bitmap(assetStream);
        }
        catch (Exception ex)
        {
            EventLogger.Error($"Failed to load mini widget pin asset '{assetUri}'", ex);
            throw;
        }
    }

    private void OnPropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}



================================================
FILE: OpenNetMeter/ViewModels/RelayCommand.cs
================================================
using System;
using System.Windows.Input;

namespace OpenNetMeter.ViewModels;

public sealed class RelayCommand : ICommand
{
    private readonly Action execute;
    private readonly Func<bool>? canExecute;

    public RelayCommand(Action execute, Func<bool>? canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public event EventHandler? CanExecuteChanged;

    public bool CanExecute(object? parameter)
    {
        return canExecute?.Invoke() ?? true;
    }

    public void Execute(object? parameter)
    {
        execute();
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

public sealed class ParameterRelayCommand : ICommand
{
    private readonly Action<object?> execute;
    private readonly Func<object?, bool>? canExecute;

    public ParameterRelayCommand(Action<object?> execute, Func<object?, bool>? canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public event EventHandler? CanExecuteChanged;

    public bool CanExecute(object? parameter)
    {
        return canExecute?.Invoke(parameter) ?? true;
    }

    public void Execute(object? parameter)
    {
        execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}



================================================
FILE: OpenNetMeter/ViewModels/SettingsViewModel.cs
================================================
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Input;
using OpenNetMeter.Services;
using OpenNetMeter.PlatformAbstractions;
using OpenNetMeter.Properties;
using OpenNetMeter.Utilities;

namespace OpenNetMeter.ViewModels;

public sealed class SettingsViewModel : INotifyPropertyChanged
{
    private readonly IMiniWidgetService miniWidgetService;
    private readonly IStartupRegistrationService startupRegistrationService;
    private readonly IExternalLinkService externalLinkService;
    private readonly IThemeService themeService;
    private bool startWithWindows;
    private bool minimizeOnStart;
    private bool darkMode;
    private bool miniWidgetVisible = true;
    private double miniWidgetTransparency = 20;
    private int selectedNetworkTargetIndex = 2;
    private int selectedSpeedMagnitudeIndex;
    private int selectedSpeedUnitIndex;
    private bool isCheckingForUpdates;
    private bool isUpdateAvailable;
    private string updateStatusMessage = "Click here to check for new updates";
    private string downloadUrl = string.Empty;
    private bool isDeleteConfirmationOpen;

    public SettingsViewModel(MiniWidgetViewModel miniWidgetViewModel, IMiniWidgetService miniWidgetService, IStartupRegistrationService startupRegistrationService, IExternalLinkService externalLinkService, IThemeService themeService)
    {
        this.miniWidgetService = miniWidgetService;
        this.startupRegistrationService = startupRegistrationService;
        this.externalLinkService = externalLinkService;
        this.themeService = themeService;
        var settings = SettingsManager.Current;
        startWithWindows = settings.StartWithWin;
        minimizeOnStart = settings.MinimizeOnStart;
        darkMode = settings.DarkMode;
        miniWidgetVisible = settings.MiniWidgetVisibility;
        miniWidgetTransparency = settings.MiniWidgetTransparentSlider;
        selectedNetworkTargetIndex = settings.NetworkType;
        selectedSpeedMagnitudeIndex = settings.NetworkSpeedMagnitude;
        selectedSpeedUnitIndex = settings.NetworkSpeedFormat;

        ResetDataCommand = new RelayCommand(ResetData);
        CheckForUpdatesCommand = new RelayCommand(async () => await CheckForUpdatesAsync());
        DownloadUpdateCommand = new RelayCommand(DownloadUpdate);
        ConfirmDeleteAllDataCommand = new RelayCommand(ConfirmDeleteAllData);
        CancelDeleteAllDataCommand = new RelayCommand(CancelDeleteAllData);

        this.miniWidgetService.VisibilityChanged += SyncMiniWidgetVisibility;
        this.themeService.ApplyDarkMode(darkMode);
        this.miniWidgetService.RefreshAppearance(darkMode, (int)Math.Round(miniWidgetTransparency));
    }

    public SettingsViewModel()
        : this(new MiniWidgetViewModel(), new PlaceholderMiniWidgetService(), new PlaceholderStartupRegistrationService(), new NoOpExternalLinkService(), new NoOpThemeService())
    {
    }

    public bool StartWithWindows
    {
        get => startWithWindows;
        set
        {
            if (startWithWindows == value)
                return;

            startWithWindows = value;
            SettingsManager.Current.StartWithWin = value;
            SettingsManager.Save();
            startupRegistrationService.SetEnabled(value, MinimizeOnStart);
            OnPropertyChanged(nameof(StartWithWindows));
            OnPropertyChanged(nameof(CanChangeMinimizeOnStart));
        }
    }

    public bool MinimizeOnStart
    {
        get => minimizeOnStart;
        set
        {
            if (minimizeOnStart == value)
                return;
            minimizeOnStart = value;
            SettingsManager.Current.MinimizeOnStart = value;
            SettingsManager.Save();
            OnPropertyChanged(nameof(MinimizeOnStart));
        }
    }

    public bool CanChangeMinimizeOnStart => !StartWithWindows;

    public bool DarkMode
    {
        get => darkMode;
        set
        {
            if (darkMode == value)
                return;
            darkMode = value;
            SettingsManager.Current.DarkMode = value;
            SettingsManager.Save();
            themeService.ApplyDarkMode(value);
            miniWidgetService.RefreshAppearance(value, (int)Math.Round(MiniWidgetTransparency));
            OnPropertyChanged(nameof(DarkMode));
        }
    }

    public bool MiniWidgetVisible
    {
        get => miniWidgetVisible;
        set
        {
            if (miniWidgetVisible == value)
                return;
            miniWidgetVisible = value;
            SettingsManager.Current.MiniWidgetVisibility = value;
            SettingsManager.Save();
            if (value)
                miniWidgetService.Show();
            else
                miniWidgetService.Hide();
            OnPropertyChanged(nameof(MiniWidgetVisible));
        }
    }

    public double MiniWidgetTransparency
    {
        get => miniWidgetTransparency;
        set
        {
            if (miniWidgetTransparency.Equals(value))
                return;
            miniWidgetTransparency = value;
            SettingsManager.Current.MiniWidgetTransparentSlider = (int)Math.Round(value);
            SettingsManager.Save();
            miniWidgetService.RefreshAppearance(DarkMode, (int)Math.Round(value));
            OnPropertyChanged(nameof(MiniWidgetTransparency));
        }
    }

    public string[] NetworkTargets { get; } = ["Private", "Public", "Both"];

    public int SelectedNetworkTargetIndex
    {
        get => selectedNetworkTargetIndex;
        set
        {
            if (selectedNetworkTargetIndex == value)
                return;
            selectedNetworkTargetIndex = value;
            SettingsManager.Current.NetworkType = value;
            SettingsManager.Save();
            OnPropertyChanged(nameof(SelectedNetworkTargetIndex));
            OnPropertyChanged(nameof(IsNetworkTargetPrivate));
            OnPropertyChanged(nameof(IsNetworkTargetPublic));
            OnPropertyChanged(nameof(IsNetworkTargetBoth));
        }
    }

    public bool IsNetworkTargetPrivate
    {
        get => SelectedNetworkTargetIndex == 0;
        set
        {
            if (value)
                SelectedNetworkTargetIndex = 0;
        }
    }

    public bool IsNetworkTargetPublic
    {
        get => SelectedNetworkTargetIndex == 1;
        set
        {
            if (value)
                SelectedNetworkTargetIndex = 1;
        }
    }

    public bool IsNetworkTargetBoth
    {
        get => SelectedNetworkTargetIndex == 2;
        set
        {
            if (value)
                SelectedNetworkTargetIndex = 2;
        }
    }

    public string[] SpeedMagnitudes { get; } = ["Auto", "Kilo", "Mega", "Giga"];

    public int SelectedSpeedMagnitudeIndex
    {
        get => selectedSpeedMagnitudeIndex;
        set
        {
            if (selectedSpeedMagnitudeIndex == value)
                return;
            selectedSpeedMagnitudeIndex = value;
            SettingsManager.Current.NetworkSpeedMagnitude = value;
            SettingsManager.Save();
            OnPropertyChanged(nameof(SelectedSpeedMagnitudeIndex));
        }
    }

    public string[] SpeedUnits { get; } = ["bps (bits/sec)", "Bps (bytes/sec)"];

    public int SelectedSpeedUnitIndex
    {
        get => selectedSpeedUnitIndex;
        set
        {
            if (selectedSpeedUnitIndex == value)
                return;
            selectedSpeedUnitIndex = value;
            SettingsManager.Current.NetworkSpeedFormat = value;
            SettingsManager.Save();
            OnPropertyChanged(nameof(SelectedSpeedUnitIndex));
        }
    }

    public bool IsCheckingForUpdates
    {
        get => isCheckingForUpdates;
        private set
        {
            if (isCheckingForUpdates == value)
                return;
            isCheckingForUpdates = value;
            OnPropertyChanged(nameof(IsCheckingForUpdates));
        }
    }

    public bool IsUpdateAvailable
    {
        get => isUpdateAvailable;
        private set
        {
            if (isUpdateAvailable == value)
                return;
            isUpdateAvailable = value;
            OnPropertyChanged(nameof(IsUpdateAvailable));
        }
    }

    public string UpdateStatusMessage
    {
        get => updateStatusMessage;
        private set
        {
            if (updateStatusMessage == value)
                return;
            updateStatusMessage = value;
            OnPropertyChanged(nameof(UpdateStatusMessage));
        }
    }

    public bool IsDeleteConfirmationOpen
    {
        get => isDeleteConfirmationOpen;
        private set
        {
            if (isDeleteConfirmationOpen == value)
                return;
            isDeleteConfirmationOpen = value;
            OnPropertyChanged(nameof(IsDeleteConfirmationOpen));
        }
    }

    public string DeleteConfirmationMessage { get; } =
        "Warning!!! This will delete all saved profiles.\nDo you still want to continue?";

    public ICommand ResetDataCommand { get; }
    public ICommand CheckForUpdatesCommand { get; }
    public ICommand DownloadUpdateCommand { get; }
    public ICommand ConfirmDeleteAllDataCommand { get; }
    public ICommand CancelDeleteAllDataCommand { get; }

    public event PropertyChangedEventHandler? PropertyChanged;
    public event Action? DeleteAllDataConfirmed;

    public void SyncMiniWidgetVisibility(bool isVisible)
    {
        if (miniWidgetVisible == isVisible)
            return;

        miniWidgetVisible = isVisible;
        SettingsManager.Current.MiniWidgetVisibility = isVisible;
        SettingsManager.Save();
        OnPropertyChanged(nameof(MiniWidgetVisible));
    }

    private void ResetData()
    {
        IsDeleteConfirmationOpen = true;
    }

    private async Task CheckForUpdatesAsync()
    {
        IsCheckingForUpdates = true;
        UpdateStatusMessage = "Checking for updates...";
        IsUpdateAvailable = false;
        downloadUrl = string.Empty;
        string statusMessage = string.Empty;

        const int minDisplayTimeMs = 2000;
        var stopwatch = Stopwatch.StartNew();

        try
        {
            (Version? latestVersion, string? nextDownloadUrl) = await UpdateChecker.CheckForUpdatesAsync();
            if (latestVersion != null && nextDownloadUrl != null)
            {
                Version? currentVersion = Assembly.GetExecutingAssembly()?.GetName()?.Version;
                if (currentVersion != null && latestVersion > currentVersion)
                {
                    downloadUrl = nextDownloadUrl;
                    statusMessage = $"A new version {latestVersion} is available!";
                    IsUpdateAvailable = true;
                }
                else
                {
                    statusMessage = "You have the latest version.";
                }
            }
            else
            {
                statusMessage = "Error checking for updates.";
            }
        }
        catch (Exception ex)
        {
            statusMessage = "Error checking for updates.";
            EventLogger.Error("Error checking for updates", ex);
        }
        finally
        {
            stopwatch.Stop();

            int remainingTime = minDisplayTimeMs - (int)stopwatch.ElapsedMilliseconds;
            if (remainingTime > 0)
            {
                await Task.Delay(remainingTime);
            }

            IsCheckingForUpdates = false;
            UpdateStatusMessage = statusMessage;
        }
    }

    private void DownloadUpdate()
    {
        if (string.IsNullOrWhiteSpace(downloadUrl))
        {
            UpdateStatusMessage = "No update download is available.";
            return;
        }

        try
        {
            externalLinkService.Open(downloadUrl);
        }
        catch (Exception ex)
        {
            EventLogger.Error("Error launching update download URL", ex);
            UpdateStatusMessage = "Error launching update download URL.";
        }
    }

    private void ConfirmDeleteAllData()
    {
        IsDeleteConfirmationOpen = false;
        DeleteAllDataConfirmed?.Invoke();
    }

    private void CancelDeleteAllData()
    {
        IsDeleteConfirmationOpen = false;
    }

    private void OnPropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    private sealed class NoOpExternalLinkService : IExternalLinkService
    {
        public void Open(string uri)
        {
        }
    }
}



================================================
FILE: OpenNetMeter/ViewModels/SummaryViewModel.cs
================================================
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows.Input;
using Avalonia.Threading;
using Avalonia.Media;
using LiveChartsCore;
using LiveChartsCore.Defaults;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Painting;
using Microsoft.Data.Sqlite;
using OpenNetMeter.Models;
using OpenNetMeter.PlatformAbstractions;
using OpenNetMeter.Properties;
using OpenNetMeter.Utilities;
using SkiaSharp;

namespace OpenNetMeter.ViewModels;

public sealed class SummaryViewModel : INotifyPropertyChanged, IDisposable
{
    private const double GraphLogBase = 10d;
    private const int MaxSpeedMagnitude = 6;
    private readonly INetworkCaptureService networkCaptureService;
    private readonly IProcessIconService processIconService;
    private readonly ObservableCollection<ObservablePoint> dlValues = new();
    private readonly ObservableCollection<ObservablePoint> ulValues = new();
    private readonly Dictionary<string, SummaryProcessRowViewModel> processIndex = new(StringComparer.OrdinalIgnoreCase);
    private readonly object pendingLock = new();
    private readonly Dictionary<string, PendingTraffic> pendingByProcess = new(StringComparer.OrdinalIgnoreCase);
    private readonly DispatcherTimer flushTimer;
    private string? currentSortColumn;
    private bool sortDescending;
    private string activeAdapterName = string.Empty;
    private long sinceDateDbDownloadBaseline;
    private long sinceDateDbUploadBaseline;
    private long sinceDateSessionDownloadBaseline;
    private long sinceDateSessionUploadBaseline;

    private const int WindowSize = 35;
    private int tickCount;
    private long currentSessionDownload;
    private long currentSessionUpload;
    private long totalFromDateDownload;
    private long totalFromDateUpload;
    private double latestDownloadMbps;
    private double latestUploadMbps;
    private DateTimeOffset? sinceDate;
    private long pendingDownloadBytes;
    private long pendingUploadBytes;
    private long latestDownloadBytesPerSecond;
    private long latestUploadBytesPerSecond;
    private int graphAxisMagnitude;

    public SummaryViewModel(INetworkCaptureService networkCaptureService, IProcessIconService processIconService)
    {
        this.networkCaptureService = networkCaptureService;
        this.processIconService = processIconService;

        // Match WPF dark theme accents:
        // Download -> #367061, Upload -> #D98868
        var dlColor = new SKColor(0x36, 0x70, 0x61);
        var ulColor = new SKColor(0xD9, 0x88, 0x68);

        GraphSeries =
        [
            new LineSeries<ObservablePoint>
            {
                Values = dlValues,
                Stroke = new SolidColorPaint(dlColor, 2),
                GeometrySize = 0,
                GeometryStroke = null,
                GeometryFill = null,
                Fill = new SolidColorPaint(dlColor.WithAlpha(0x33)),
                LineSmoothness = 0.3,
                Name = "Download"
            },
            new LineSeries<ObservablePoint>
            {
                Values = ulValues,
                Stroke = new SolidColorPaint(ulColor, 2),
                GeometrySize = 0,
                GeometryStroke = null,
                GeometryFill = null,
                Fill = new SolidColorPaint(ulColor.WithAlpha(0x33)),
                LineSmoothness = 0.3,
                Name = "Upload"
            }
        ];

        GraphXAxes =
        [
            new Axis
            {
                ShowSeparatorLines = false,
                IsVisible = false,
                MinLimit = 0,
                MaxLimit = WindowSize
            }
        ];

        GraphYAxes = CreateGraphYAxes();

        ActiveProcesses = [];
        SortProcessesCommand = new ParameterRelayCommand(parameter =>
        {
            var column = parameter?.ToString();
            if (string.IsNullOrWhiteSpace(column))
                return;

            SortProcesses(column);
        });

        DateMax = DateTime.Today;
        DateMin = DateMax.AddDays(-ApplicationDB.DataStoragePeriodInDays);
        sinceDate = DateMax;
        this.networkCaptureService.TrafficObserved += OnTrafficObserved;

        flushTimer = new DispatcherTimer
        {
            Interval = TimeSpan.FromSeconds(1)
        };
        flushTimer.Tick += (_, _) => FlushPendingTraffic();
        flushTimer.Start();
    }

    public ISeries[] GraphSeries { get; }
    public Axis[] GraphXAxes { get; }
    public Axis[] GraphYAxes { get; private set; }
    public ObservableCollection<SummaryProcessRowViewModel> ActiveProcesses { get; }
    public ICommand SortProcessesCommand { get; }

    public string CurrentSessionDownloadText => ByteSizeFormatter.FormatBytes(currentSessionDownload);
    public string CurrentSessionUploadText => ByteSizeFormatter.FormatBytes(currentSessionUpload);
    public string TotalFromDateDownloadText => ByteSizeFormatter.FormatBytes(totalFromDateDownload);
    public string TotalFromDateUploadText => ByteSizeFormatter.FormatBytes(totalFromDateUpload);
    public string DownloadSpeedText => $"{FormatSpeed(latestDownloadBytesPerSecond)}ps";
    public string UploadSpeedText => $"{FormatSpeed(latestUploadBytesPerSecond)}ps";
    public int ProcessCount => ActiveProcesses.Count;
    public DateTime DateMin { get; }
    public DateTime DateMax { get; }

    public DateTimeOffset? SinceDate
    {
        get => sinceDate;
        set
        {
            var normalized = NormalizeSinceDate(value);
            if (sinceDate == normalized)
                return;

            sinceDate = normalized;
            RefreshSinceDateBaseline();
            OnPropertyChanged(nameof(SinceDate));
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    public void Dispose()
    {
        networkCaptureService.TrafficObserved -= OnTrafficObserved;
        flushTimer.Stop();
    }

    public void ClearOnDisconnect()
    {
        lock (pendingLock)
        {
            pendingDownloadBytes = 0;
            pendingUploadBytes = 0;
            pendingByProcess.Clear();
        }

        latestDownloadMbps = 0;
        latestUploadMbps = 0;
        latestDownloadBytesPerSecond = 0;
        latestUploadBytesPerSecond = 0;
        currentSessionDownload = 0;
        currentSessionUpload = 0;
        totalFromDateDownload = 0;
        totalFromDateUpload = 0;
        sinceDateDbDownloadBaseline = 0;
        sinceDateDbUploadBaseline = 0;
        sinceDateSessionDownloadBaseline = 0;
        sinceDateSessionUploadBaseline = 0;
        activeAdapterName = string.Empty;

        dlValues.Clear();
        ulValues.Clear();
        tickCount = 0;
        GraphXAxes[0].MinLimit = 0;
        GraphXAxes[0].MaxLimit = WindowSize;

        ActiveProcesses.Clear();
        processIndex.Clear();
        OnPropertyChanged(nameof(ProcessCount));
        UpdateGraphAxisLabelScale();
        OnPropertyChanged(nameof(CurrentSessionDownloadText));
        OnPropertyChanged(nameof(CurrentSessionUploadText));
        OnPropertyChanged(nameof(TotalFromDateDownloadText));
        OnPropertyChanged(nameof(TotalFromDateUploadText));
        OnPropertyChanged(nameof(DownloadSpeedText));
        OnPropertyChanged(nameof(UploadSpeedText));
    }

    public void SetActiveAdapter(string adapterName)
    {
        var normalized = adapterName?.Trim() ?? string.Empty;
        if (string.Equals(activeAdapterName, normalized, StringComparison.Ordinal))
            return;

        activeAdapterName = normalized;
        RefreshSinceDateBaseline();
    }

    private void OnTrafficObserved(object? sender, NetworkTrafficEventArgs e)
    {
        lock (pendingLock)
        {
            if (!pendingByProcess.TryGetValue(e.ProcessName, out var pending))
            {
                pending = new PendingTraffic();
                pendingByProcess[e.ProcessName] = pending;
            }

            if (e.IsReceive)
            {
                pending.DownloadBytes += e.Bytes;
                pendingDownloadBytes += e.Bytes;
            }
            else
            {
                pending.UploadBytes += e.Bytes;
                pendingUploadBytes += e.Bytes;
            }
        }
    }

    private void FlushPendingTraffic()
    {
        Dictionary<string, PendingTraffic> pendingSnapshot;
        long secondDownloadBytes;
        long secondUploadBytes;

        lock (pendingLock)
        {
            secondDownloadBytes = pendingDownloadBytes;
            secondUploadBytes = pendingUploadBytes;
            pendingDownloadBytes = 0;
            pendingUploadBytes = 0;

            pendingSnapshot = new Dictionary<string, PendingTraffic>(pendingByProcess, StringComparer.OrdinalIgnoreCase);
            pendingByProcess.Clear();
        }

        latestDownloadMbps = secondDownloadBytes * 8d / 1_000_000d;
        latestUploadMbps = secondUploadBytes * 8d / 1_000_000d;
        latestDownloadBytesPerSecond = secondDownloadBytes;
        latestUploadBytesPerSecond = secondUploadBytes;
        currentSessionDownload += secondDownloadBytes;
        currentSessionUpload += secondUploadBytes;
        UpdateTotalFromDateFromBaselines();

        AppendGraphPoint();
        ApplyProcessTick(pendingSnapshot);

        OnPropertyChanged(nameof(CurrentSessionDownloadText));
        OnPropertyChanged(nameof(CurrentSessionUploadText));
        OnPropertyChanged(nameof(TotalFromDateDownloadText));
        OnPropertyChanged(nameof(TotalFromDateUploadText));
        OnPropertyChanged(nameof(DownloadSpeedText));
        OnPropertyChanged(nameof(UploadSpeedText));
    }

    private void RefreshSinceDateBaseline()
    {
        sinceDateSessionDownloadBaseline = currentSessionDownload;
        sinceDateSessionUploadBaseline = currentSessionUpload;

        if (string.IsNullOrWhiteSpace(activeAdapterName))
        {
            sinceDateDbDownloadBaseline = 0;
            sinceDateDbUploadBaseline = 0;
            totalFromDateDownload = 0;
            totalFromDateUpload = 0;
        }
        else
        {
            var fromDate = (sinceDate ?? DateTimeOffset.Now.Date).Date;
            var totals = ReadDbTotals(activeAdapterName, fromDate, DateTime.Today);
            sinceDateDbDownloadBaseline = totals.download;
            sinceDateDbUploadBaseline = totals.upload;
            UpdateTotalFromDateFromBaselines();
        }

        OnPropertyChanged(nameof(TotalFromDateDownloadText));
        OnPropertyChanged(nameof(TotalFromDateUploadText));
    }

    private void UpdateTotalFromDateFromBaselines()
    {
        var sessionDownloadDelta = currentSessionDownload - sinceDateSessionDownloadBaseline;
        var sessionUploadDelta = currentSessionUpload - sinceDateSessionUploadBaseline;

        if (sessionDownloadDelta < 0)
            sessionDownloadDelta = 0;
        if (sessionUploadDelta < 0)
            sessionUploadDelta = 0;

        totalFromDateDownload = sinceDateDbDownloadBaseline + sessionDownloadDelta;
        totalFromDateUpload = sinceDateDbUploadBaseline + sessionUploadDelta;
    }

    private static (long download, long upload) ReadDbTotals(string adapterName, DateTime startDate, DateTime endDate)
    {
        try
        {
            var dbPath = ResolveDatabasePath();
            if (!File.Exists(dbPath))
                return (0, 0);

            var fromDate = startDate.Date;
            var toDate = endDate.Date;
            if (toDate < fromDate)
                (fromDate, toDate) = (toDate, fromDate);

            using var connection = OpenReadOnlyConnection(dbPath);
            connection.Open();
            using var command = connection.CreateCommand();
            command.CommandText =
                "SELECT SUM(pd.DataReceived) AS TotalRecv, SUM(pd.DataSent) AS TotalSent " +
                "FROM ProcessDate pd " +
                "JOIN Adapter a ON a.ID = pd.AdapterID " +
                "JOIN Date d ON d.ID = pd.DateID " +
                "WHERE a.Name = @AdapterName " +
                "AND (d.Year * 10000 + d.Month * 100 + d.Day) BETWEEN @StartDate AND @EndDate";
            command.Parameters.AddWithValue("@AdapterName", adapterName);
            command.Parameters.AddWithValue("@StartDate", ToDateInt(fromDate));
            command.Parameters.AddWithValue("@EndDate", ToDateInt(toDate));

            using var reader = command.ExecuteReader();
            if (reader.Read())
            {
                var download = reader.IsDBNull(0) ? 0 : reader.GetInt64(0);
                var upload = reader.IsDBNull(1) ? 0 : reader.GetInt64(1);
                return (download, upload);
            }
        }
        catch (Exception ex)
        {
            EventLogger.Error($"Failed to read summary totals from database for adapter '{adapterName}'", ex);
        }

        return (0, 0);
    }

    private static SqliteConnection OpenReadOnlyConnection(string path)
    {
        var csb = new SqliteConnectionStringBuilder
        {
            DataSource = path,
            Mode = SqliteOpenMode.ReadOnly
        };
        return new SqliteConnection(csb.ToString());
    }

    private static int ToDateInt(DateTime date)
    {
        return (date.Year * 10000) + (date.Month * 100) + date.Day;
    }

    private static string ResolveDatabasePath()
    {
        var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        var appFolder = Path.Combine(localAppData, "OpenNetMeter");
        return Path.Combine(appFolder, "OpenNetMeter.sqlite");
    }

    private DateTimeOffset NormalizeSinceDate(DateTimeOffset? value)
    {
        var candidate = (value ?? DateTimeOffset.Now.Date).Date;
        if (candidate > DateMax)
            candidate = DateMax;
        if (candidate < DateMin)
            candidate = DateMin;
        return candidate;
    }

    private void AppendGraphPoint()
    {
        dlValues.Add(new ObservablePoint(tickCount, MbpsToGraphValue(latestDownloadMbps)));
        ulValues.Add(new ObservablePoint(tickCount, MbpsToGraphValue(latestUploadMbps)));

        while (dlValues.Count > WindowSize)
            dlValues.RemoveAt(0);
        while (ulValues.Count > WindowSize)
            ulValues.RemoveAt(0);

        if (tickCount >= WindowSize)
        {
            GraphXAxes[0].MinLimit = tickCount - WindowSize;
            GraphXAxes[0].MaxLimit = tickCount;
        }

        UpdateGraphAxisLabelScale();
        tickCount++;
    }

    private Axis[] CreateGraphYAxes()
    {
        return
        [
            new Axis
            {
                MinLimit = 0,
                ShowSeparatorLines = true,
                // Match dark divider/text tones from MainWindow resources
                SeparatorsPaint = new SolidColorPaint(new SKColor(0x55, 0x55, 0x55)) { StrokeThickness = 1 },
                LabelsPaint = new SolidColorPaint(new SKColor(0xA9, 0xAB, 0xAB)),
 
Download .txt
gitextract_0pgbfx2f/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       └── dotnet.yml
├── .gitignore
├── .vscode/
│   └── launch.json
├── Directory.Build.props
├── Installer/
│   ├── OpenNetMeter-Installer.wixproj
│   ├── Product.wxs
│   ├── Strings_en-us.wxl
│   └── WixUIFeatureTree.wxs
├── LICENSE
├── NOTICE
├── OpenNetMeter/
│   ├── App.axaml
│   ├── App.axaml.cs
│   ├── Compat/
│   │   ├── Models/
│   │   │   ├── ApplicationDB.cs
│   │   │   ├── MyProcess_Small.cs
│   │   │   ├── NetworkProcess.TestHooks.cs
│   │   │   └── NetworkProcess.cs
│   │   ├── Properties/
│   │   │   ├── AppSettings.cs
│   │   │   ├── Global.cs
│   │   │   └── SettingsManager.cs
│   │   └── Utilities/
│   │       ├── ByteArray.cs
│   │       ├── EventLogger.cs
│   │       └── PeriodicWork.cs
│   ├── OpenNetMeter.csproj
│   ├── Program.cs
│   ├── Services/
│   │   ├── AvaloniaThemeService.cs
│   │   ├── AvaloniaWindowService.cs
│   │   ├── ExternalLinkService.cs
│   │   ├── IMiniWidgetService.cs
│   │   ├── IThemeService.cs
│   │   ├── ITrayNotificationService.cs
│   │   ├── ITrayService.cs
│   │   ├── NoOpThemeService.cs
│   │   ├── PlaceholderMiniWidgetService.cs
│   │   ├── PlaceholderNetworkCaptureService.cs
│   │   ├── PlaceholderProcessIconService.cs
│   │   ├── PlaceholderStartupRegistrationService.cs
│   │   ├── PlaceholderTrayNotificationService.cs
│   │   ├── PlaceholderTrayService.cs
│   │   ├── UpdateChecker.cs
│   │   ├── WindowsMiniWidgetService.cs
│   │   ├── WindowsNetworkCaptureService.cs
│   │   ├── WindowsProcessIconService.cs
│   │   ├── WindowsStartupRegistrationService.cs
│   │   ├── WindowsTrayNotificationService.cs
│   │   ├── WindowsTrayService.cs
│   │   └── WindowsWidgetZOrderHelper.cs
│   ├── ViewModels/
│   │   ├── ByteSizeFormatter.cs
│   │   ├── HistoryViewModel.cs
│   │   ├── MainWindowViewModel.cs
│   │   ├── MiniWidgetViewModel.cs
│   │   ├── RelayCommand.cs
│   │   ├── SettingsViewModel.cs
│   │   └── SummaryViewModel.cs
│   ├── Views/
│   │   ├── MainWindow/
│   │   │   ├── History.axaml
│   │   │   ├── History.axaml.cs
│   │   │   ├── Settings.axaml
│   │   │   ├── Settings.axaml.cs
│   │   │   ├── Summary.axaml
│   │   │   └── Summary.axaml.cs
│   │   ├── MainWindow.axaml
│   │   ├── MainWindow.axaml.cs
│   │   ├── MiniWidgetWindow.axaml
│   │   ├── MiniWidgetWindow.axaml.cs
│   │   └── Themes.axaml
│   └── app.manifest
├── OpenNetMeter.Core/
│   ├── OpenNetMeter.Core.csproj
│   └── ViewModels/
│       ├── ConfirmationDialogVM.cs
│       └── MainShellTabsViewModel.cs
├── OpenNetMeter.Old/
│   ├── DatabaseEngine/
│   │   ├── Connection.cs
│   │   ├── Database.cs
│   │   └── DatabaseEngine.csproj
│   └── OpenNetMeter/
│       ├── App.xaml
│       ├── App.xaml.cs
│       ├── AssemblyInfo.cs
│       ├── Models/
│       │   ├── ApplicationDB.cs
│       │   ├── MyProcess.cs
│       │   ├── NativeMethods.cs
│       │   ├── NetworkProcess.TestHooks.cs
│       │   ├── NetworkProcess.cs
│       │   └── SpeedGraph.cs
│       ├── OpenNetMeter.Old.csproj
│       ├── Properties/
│       │   ├── AppSettings.cs
│       │   ├── Global.cs
│       │   ├── InternalsVisibleTo.cs
│       │   ├── Resources.Designer.cs
│       │   ├── Resources.resx
│       │   └── SettingsManager.cs
│       ├── Utilities/
│       │   ├── BaseCommand.cs
│       │   ├── ByteArray.cs
│       │   ├── DataSizeSuffix.cs
│       │   ├── EventLogger.cs
│       │   ├── IconToImgSource.cs
│       │   ├── JsonHelper.cs
│       │   ├── PeriodicWork.cs
│       │   ├── ProcessIconCache.cs
│       │   ├── UIMeasure.cs
│       │   ├── UpdateChecker.cs
│       │   ├── WindowsNetworkCaptureService.cs
│       │   ├── WindowsProcessIconService.cs
│       │   ├── WindowsStartupRegistrationService.cs
│       │   └── WpfUiDispatcher.cs
│       ├── ViewModels/
│       │   ├── DataUsageHistoryVM.cs
│       │   ├── DataUsageSummaryVM.cs
│       │   ├── MainWindowVM.cs
│       │   ├── MiniWidgetVM.cs
│       │   └── SettingsVM.cs
│       ├── Views/
│       │   ├── AboutWindow.xaml
│       │   ├── AboutWindow.xaml.cs
│       │   ├── ConfirmationDialog.xaml
│       │   ├── ConfirmationDialog.xaml.cs
│       │   ├── Converters/
│       │   │   ├── BitmapToImageConverter.cs
│       │   │   ├── NetSpeedFormatConverter.cs
│       │   │   ├── RadioBoolToIntConverter.cs
│       │   │   ├── UiVisibilityToWpfVisibilityConverter.cs
│       │   │   └── UnitConverterBytes.cs
│       │   ├── CustomSystemTray.cs
│       │   ├── MainWindow.xaml
│       │   ├── MainWindow.xaml.cs
│       │   ├── MainWindowTabs/
│       │   │   ├── DataUsageHistoryV.xaml
│       │   │   ├── DataUsageHistoryV.xaml.cs
│       │   │   ├── DataUsageSummaryV.xaml
│       │   │   ├── DataUsageSummaryV.xaml.cs
│       │   │   ├── SettingsV.xaml
│       │   │   └── SettingsV.xaml.cs
│       │   ├── MiniWidgetV.xaml
│       │   ├── MiniWidgetV.xaml.cs
│       │   └── ResourceDictionaries/
│       │       ├── CustomDatePicker.xaml
│       │       ├── Theme.Colors.xaml
│       │       ├── Theme.ComboBox.xaml
│       │       ├── Theme.ContextMenu.xaml
│       │       ├── Theme.DataGrid.xaml
│       │       ├── Theme.MainWindowTabs.xaml
│       │       ├── Theme.Styles.xaml
│       │       ├── Theme.SummaryPage.xaml
│       │       └── ThemeResources.xaml
│       └── app.manifest
├── OpenNetMeter.PlatformAbstractions/
│   ├── IExternalLinkService.cs
│   ├── INetworkCaptureService.cs
│   ├── IProcessIconService.cs
│   ├── IStartupRegistrationService.cs
│   ├── IUiDispatcher.cs
│   ├── IWindowService.cs
│   ├── OpenNetMeter.PlatformAbstractions.csproj
│   └── UiVisibility.cs
├── OpenNetMeter.Tests/
│   ├── NetworkProcessTests.cs
│   └── OpenNetMeter.Tests.csproj
├── OpenNetMeter.sln
├── README.md
├── Resources/
│   └── documentation/
│       └── README.md
└── scripts/
    ├── build-avalonia-msi.ps1
    └── publish-avalonia-rc.ps1
Download .txt
SYMBOL INDEX (667 symbols across 101 files)

FILE: OpenNetMeter.Core/ViewModels/ConfirmationDialogVM.cs
  class ConfirmationDialogVM (line 7) | public class ConfirmationDialogVM : INotifyPropertyChanged
    method ConfirmationDialogVM (line 25) | public ConfirmationDialogVM()
    method OnPropertyChanged (line 32) | private void OnPropertyChanged(string propName) =>

FILE: OpenNetMeter.Core/ViewModels/MainShellTabsViewModel.cs
  class MainShellTabsViewModel (line 5) | public class MainShellTabsViewModel : INotifyPropertyChanged
    method OnPropertyChanged (line 24) | protected void OnPropertyChanged(string propertyName) =>

FILE: OpenNetMeter.Old/DatabaseEngine/Connection.cs
  class Connection (line 6) | internal class Connection
    method Connection (line 9) | public Connection(string path, string dbFileName)

FILE: OpenNetMeter.Old/DatabaseEngine/Database.cs
  class Database (line 8) | public class Database : IDisposable
    method Database (line 19) | public Database(string path, string dbFileName, string[]? extraParams ...
    method ConfigureForConcurrency (line 40) | public void ConfigureForConcurrency()
    method RunSQLiteNonQuery (line 57) | public int RunSQLiteNonQuery(string query)
    method RunSQLiteNonQuery (line 79) | public int RunSQLiteNonQuery(string query, string[,] paramAndValue)
    method RunInTransaction (line 108) | public bool RunInTransaction(Action<Database> action)
    method GetMultipleCellData (line 130) | public List<List<object>> GetMultipleCellData(string query)
    method GetMultipleCellData (line 162) | public List<List<object>> GetMultipleCellData(string query, string[,] ...
    method GetSingleCellData (line 201) | public object? GetSingleCellData(string query, string[,] paramAndValue)
    method Dispose (line 224) | public void Dispose()

FILE: OpenNetMeter.Old/OpenNetMeter/App.xaml.cs
  class App (line 12) | public partial class App : Application
    method Application_Startup (line 14) | private void Application_Startup(object sender, StartupEventArgs e)
    method Application_Exit (line 37) | private void Application_Exit(object sender, ExitEventArgs e)

FILE: OpenNetMeter.Old/OpenNetMeter/Models/ApplicationDB.cs
  class ApplicationDB (line 15) | internal class ApplicationDB : IDisposable
    method LogTime (line 17) | private static string LogTime() => DateTime.Now.ToString("HH:mm:ss.fff");
    method ApplicationDB (line 62) | public ApplicationDB(string dBFileName, string[]? extraParams = null)
    method GetUnifiedDBFullPath (line 78) | public static string GetUnifiedDBFullPath()
    method Dispose (line 83) | public void Dispose()
    method PushToDB (line 121) | public void PushToDB(string processName, long totalDataRecv, long tota...
    method CreateTable (line 145) | public int CreateTable()
    method UpdateDatesInDB (line 162) | public void UpdateDatesInDB()
    method InsertUniqueRow_AdapterTable (line 177) | public int InsertUniqueRow_AdapterTable(string adapter)
    method GetDataSum_ProcessDateTable (line 191) | public List<List<object>> GetDataSum_ProcessDateTable(DateTime date1, ...
    method GetDataSumBetweenDates (line 212) | public (long, long) GetDataSumBetweenDates(DateTime startDate, DateTim...
    method GetTodayDataSum_ProcessDateTable (line 247) | public (long, long) GetTodayDataSum_ProcessDateTable()
    method GetID_AdapterTable (line 275) | public long GetID_AdapterTable(string adapter)
    method GetAllAdapters (line 283) | public List<string> GetAllAdapters()
    method CreateProcessTable (line 303) | private int CreateProcessTable()
    method CreateDateTable (line 311) | private int CreateDateTable()
    method CreateProcessDateTable (line 322) | private int CreateProcessDateTable()
    method CreateAdapterTable (line 338) | private int CreateAdapterTable()
    method InsertUniqueRow_ProcessTable (line 349) | private int InsertUniqueRow_ProcessTable(string appName)
    method InsertUniqueRow_DateTable (line 356) | private int InsertUniqueRow_DateTable(DateTime date)
    method RemoveOldDate (line 368) | private void RemoveOldDate()
    method RemoveOldProcess (line 377) | private void RemoveOldProcess()
    method InsertUniqueRow_ProcessDateTable (line 385) | private int InsertUniqueRow_ProcessDateTable(long processID, long date...
    method UpdateRow_ProcessDateTable (line 400) | private int UpdateRow_ProcessDateTable(long processID, long dateID, lo...
    method GetID_DateTable (line 420) | private long GetID_DateTable(DateTime time)
    method GetID_ProcessTable (line 434) | private long GetID_ProcessTable(string appName)
    method GetID_AdapterTable_Internal (line 443) | private long GetID_AdapterTable_Internal(string adapter)

FILE: OpenNetMeter.Old/OpenNetMeter/Models/MyProcess.cs
  class MyProcess_Small (line 7) | public class MyProcess_Small
    method MyProcess_Small (line 17) | public MyProcess_Small(string nameP, long currentDataRecvP, long curre...
  class MyProcess_Big (line 27) | public class MyProcess_Big : INotifyPropertyChanged
    method MyProcess_Big (line 106) | public MyProcess_Big(string nameP, long currentDataRecvP, long current...
    method OnPropertyChanged (line 117) | private void OnPropertyChanged(string propName) => PropertyChanged?.In...

FILE: OpenNetMeter.Old/OpenNetMeter/Models/NativeMethods.cs
  class NativeMethods (line 7) | public static class NativeMethods
    method GetWindowByClassName (line 10) | internal static IntPtr GetWindowByClassName(IntPtr parentHandle, strin...
    method FindWindowEx (line 12) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    type GW (line 15) | public enum GW : uint
    method GetWindow (line 26) | [DllImport("user32.dll")]
    type SWP (line 29) | public enum SWP
    method SetWindowPos (line 37) | [DllImport("user32.dll", EntryPoint = "SetWindowPos")]

FILE: OpenNetMeter.Old/OpenNetMeter/Models/NetworkProcess.TestHooks.cs
  class NetworkProcess (line 6) | public partial class NetworkProcess
    method TestSetLocalIPs (line 8) | internal void TestSetLocalIPs(byte[] ipv4, byte[] ipv6)
    method TestInvokeRecvProcess (line 14) | internal void TestInvokeRecvProcess(IPAddress src, IPAddress dest, int...
    method TestInvokeSendProcess (line 19) | internal void TestInvokeSendProcess(IPAddress src, IPAddress dest, int...

FILE: OpenNetMeter.Old/OpenNetMeter/Models/NetworkProcess.cs
  class NetworkProcess (line 21) | public partial class NetworkProcess : IDisposable
    type NetworkSnapshot (line 38) | private sealed record NetworkSnapshot(
    method Initialize (line 146) | public void Initialize()
    method GetCurrentNetworkSnapshot (line 169) | private NetworkSnapshot? GetCurrentNetworkSnapshot()
    method GetConnectedSsid (line 241) | private static string? GetConnectedSsid(string adapterGuid)
    method OnNetworkAddressChanged (line 279) | private void OnNetworkAddressChanged(object? sender, EventArgs? e)
    method HandleNetworkChange (line 310) | private void HandleNetworkChange()
    method HasSnapshotAddressChanged (line 369) | private bool HasSnapshotAddressChanged(NetworkSnapshot snapshot)
    method ApplySnapshot (line 383) | private void ApplySnapshot(NetworkSnapshot snapshot)
    method StartNetworkProcess (line 403) | public void StartNetworkProcess()
    method EndNetworkProcess (line 433) | public void EndNetworkProcess()
    method StopKernelSession (line 460) | private void StopKernelSession()
    method StopPeriodicWork (line 492) | private void StopPeriodicWork(ref PeriodicWork? work, string name)
    method StartSpeedMonitoring (line 514) | private void StartSpeedMonitoring()
    method StartDbPush (line 552) | private void StartDbPush()
    method CaptureNetworkPackets (line 590) | private void CaptureNetworkPackets()
    method ProcessPacket (line 638) | private void ProcessPacket(IPAddress src, IPAddress dest, int size, st...
    method ShouldProcessByNetworkType (line 678) | private bool ShouldProcessByNetworkType(bool isLocalSrc, IPAddress src...
    method RecordRecv (line 695) | private void RecordRecv(string name, int size)
    method RecordSend (line 705) | private void RecordSend(string name, int size)
    method RecordToBuffer (line 716) | private void RecordToBuffer(string name, int size, bool isRecv)
    method IsPrivateIP (line 752) | private bool IsPrivateIP(IPAddress ip)
    method OnPropertyChanged (line 787) | private void OnPropertyChanged(string propName) =>
    method Dispose (line 796) | public void Dispose()

FILE: OpenNetMeter.Old/OpenNetMeter/Models/SpeedGraph.cs
  class MyLine (line 15) | public class MyLine : INotifyPropertyChanged
    method OnPropertyChanged (line 40) | private void OnPropertyChanged(string propName) => PropertyChanged?.In...
  class SpeedGraph (line 42) | public class SpeedGraph
    method SpeedGraph (line 69) | public SpeedGraph(int XlineCount, int YlineCount)
    method Init (line 86) | public void Init()
    method DrawPoints (line 161) | public void DrawPoints(long downloadSpeed, long uploadSpeed)
    method DrawClear (line 266) | public void DrawClear()
    method ChangeYLabel (line 293) | public void ChangeYLabel()
    method ConvToGraphCoords (line 319) | public double ConvToGraphCoords(double value, double height)

FILE: OpenNetMeter.Old/OpenNetMeter/Properties/AppSettings.cs
  class AppSettings (line 7) | public class AppSettings : INotifyPropertyChanged
    method OnPropertyChanged (line 54) | private void OnPropertyChanged(string propName) {

FILE: OpenNetMeter.Old/OpenNetMeter/Properties/Global.cs
  class Global (line 7) | internal class Global
    method GetFilePath (line 11) | public static string GetFilePath()

FILE: OpenNetMeter.Old/OpenNetMeter/Properties/Resources.Designer.cs
  class Resources (line 22) | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resource...
    method Resources (line 31) | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Mic...

FILE: OpenNetMeter.Old/OpenNetMeter/Properties/SettingsManager.cs
  class SettingsManager (line 9) | internal static class SettingsManager
    method SettingsManager (line 17) | static SettingsManager()
    method Load (line 24) | public static void Load()
    method Save (line 46) | public static void Save()

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/BaseCommand.cs
  class BaseCommand (line 6) | internal class BaseCommand : ICommand
    method BaseCommand (line 10) | public BaseCommand(Action<object?> action, bool canExecute)
    method CanExecute (line 16) | public bool CanExecute(object? parameter)
    method Execute (line 27) | public void Execute(object? parameter)

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/ByteArray.cs
  class ByteArray (line 9) | internal class ByteArray
    method Compare (line 11) | public static bool Compare(ReadOnlySpan<byte> a1, ReadOnlySpan<byte> a2)

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/DataSizeSuffix.cs
  type SpeedMagnitude (line 5) | public enum SpeedMagnitude
  class DataSizeSuffix (line 13) | internal static class DataSizeSuffix
    method InStr (line 16) | internal static string InStr(long value, int decimalPlaces = 1, bool b...
    method InInt (line 26) | internal static (double, int) InInt(long value, int decimalPlaces = 1,...
    method NormalizeMagnitude (line 33) | internal static SpeedMagnitude NormalizeMagnitude(int magnitude)
    method GetAdjustedSize (line 38) | private static (decimal adjustedSize, int mag) GetAdjustedSize(long va...
    method InBytes (line 68) | private static string InBytes(int value)
    method InBits (line 73) | private static string InBits(int value)

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/EventLogger.cs
  class EventLogger (line 8) | public static class EventLogger
    method Info (line 13) | public static void Info(string message, int eventId = 1000, short cate...
    method Warn (line 18) | public static void Warn(string message, int eventId = 2000, short cate...
    method Error (line 23) | public static void Error(
    method Error (line 35) | public static void Error(
    method Error (line 46) | public static void Error(
    method AddCallerContext (line 63) | private static string AddCallerContext(string message, string memberNa...
    method WriteEntrySafe (line 69) | private static void WriteEntrySafe(string message, EventLogEntryType e...
    method Truncate (line 84) | private static string Truncate(string message)

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/IconToImgSource.cs
  class IconToImgSource (line 9) | public static class IconToImgSource
    method ToImageSource (line 11) | public static ImageSource ToImageSource(this Icon icon)

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/JsonHelper.cs
  class JsonHelper (line 7) | public static class JsonHelper
    method PrettyPrint (line 15) | public static string PrettyPrint(object obj, JsonSerializerSettings? s...

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/PeriodicWork.cs
  class PeriodicWork (line 8) | internal sealed class PeriodicWork : IDisposable, IAsyncDisposable
    method PeriodicWork (line 18) | public PeriodicWork(string name, TimeSpan interval)
    method Start (line 24) | public void Start(Func<CancellationToken, Task> onTick)
    method RunAsync (line 34) | private async Task RunAsync(Func<CancellationToken, Task> onTick, Canc...
    method StopAsync (line 60) | public async Task StopAsync()
    method Stop (line 89) | public void Stop()
    method Dispose (line 94) | public void Dispose()
    method DisposeAsync (line 99) | public ValueTask DisposeAsync()

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/ProcessIconCache.cs
  class ProcessIconCache (line 12) | public static class ProcessIconCache
    method GetIcon (line 17) | public static ImageSource? GetIcon(string processName)
    method FetchIcon (line 25) | private static ImageSource? FetchIcon(string processName)
    method CreateDefaultIcon (line 68) | private static ImageSource? CreateDefaultIcon()

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/UIMeasure.cs
  class UIMeasure (line 11) | public static class UIMeasure
    method Shape (line 13) | public static Size Shape(TextBlock tb)

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/UpdateChecker.cs
  class UpdateChecker (line 10) | public class UpdateChecker
    method CheckForUpdates (line 15) | public static async Task<(Version? latestVersion, string? downloadUrl)...

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/WindowsNetworkCaptureService.cs
  class WindowsNetworkCaptureService (line 8) | public sealed class WindowsNetworkCaptureService : INetworkCaptureService
    method Start (line 17) | public void Start()
    method Stop (line 31) | public void Stop()
    method Dispose (line 44) | public void Dispose()
    method NetworkProcess_PropertyChanged (line 56) | private void NetworkProcess_PropertyChanged(object? sender, PropertyCh...
    method ThrowIfDisposed (line 88) | private void ThrowIfDisposed()

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/WindowsProcessIconService.cs
  class WindowsProcessIconService (line 5) | public sealed class WindowsProcessIconService : IProcessIconService
    method GetProcessIcon (line 7) | public object? GetProcessIcon(string processName)

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/WindowsStartupRegistrationService.cs
  class WindowsStartupRegistrationService (line 8) | public sealed class WindowsStartupRegistrationService : IStartupRegistra...
    method IsEnabled (line 13) | public bool IsEnabled()
    method SetEnabled (line 26) | public void SetEnabled(bool enabled, bool startMinimized)
    method CreateTask (line 61) | private static void CreateTask(bool startMinimized)

FILE: OpenNetMeter.Old/OpenNetMeter/Utilities/WpfUiDispatcher.cs
  class WpfUiDispatcher (line 7) | public sealed class WpfUiDispatcher : IUiDispatcher
    method WpfUiDispatcher (line 11) | public WpfUiDispatcher(Dispatcher? dispatcher = null)
    method CheckAccess (line 16) | public bool CheckAccess() => dispatcher.CheckAccess();
    method Post (line 18) | public void Post(Action action)

FILE: OpenNetMeter.Old/OpenNetMeter/ViewModels/DataUsageHistoryVM.cs
  class DataUsageHistoryVM (line 16) | public class DataUsageHistoryVM : INotifyPropertyChanged
    method DataUsageHistoryVM (line 73) | public DataUsageHistoryVM()
    method DataUsageHistoryVM (line 78) | public DataUsageHistoryVM(IUiDispatcher uiDispatcher, IProcessIconServ...
    method UpdateDates (line 98) | public void UpdateDates()
    method DataUsageHistoryVM_PropertyChanged (line 106) | private void DataUsageHistoryVM_PropertyChanged(object? sender, Proper...
    method Filter (line 120) | private void Filter(object? obj)
    method GetAllDBFiles (line 151) | public void GetAllDBFiles()
    method DeleteAllDBFiles (line 177) | public void DeleteAllDBFiles()
    method OnPropertyChanged (line 194) | private void OnPropertyChanged(string propName) => PropertyChanged?.In...

FILE: OpenNetMeter.Old/OpenNetMeter/ViewModels/DataUsageSummaryVM.cs
  class DataUsageSummaryVM (line 8) | public class DataUsageSummaryVM : INotifyPropertyChanged
    method DataUsageSummaryVM (line 107) | public DataUsageSummaryVM()
    method RefreshDateBounds (line 123) | public void RefreshDateBounds()
    method OnPropertyChanged (line 145) | private void OnPropertyChanged(string propName) => PropertyChanged?.In...

FILE: OpenNetMeter.Old/OpenNetMeter/ViewModels/MainWindowVM.cs
  class MainWindowVM (line 15) | public class MainWindowVM : INotifyPropertyChanged, IDisposable
    type TabPage (line 65) | private enum TabPage
    method MainWindowVM (line 72) | public MainWindowVM(MiniWidgetVM mw_DataContext, ConfirmationDialogVM ...
    method MainWindowVM (line 77) | public MainWindowVM(MiniWidgetVM mw_DataContext, ConfirmationDialogVM ...
    method Svm_PropertyChanged (line 128) | private void Svm_PropertyChanged(object? sender, PropertyChangedEventA...
    method UpdateMainWinSpeed (line 157) | private void UpdateMainWinSpeed()
    method UpdateMiniWidgetValues (line 163) | private void UpdateMiniWidgetValues()
    method RefreshSummaryBaseline (line 171) | private void RefreshSummaryBaseline()
    method UpdateTodayTotals (line 186) | private void UpdateTodayTotals()
    method UpdateSummaryTab (line 200) | private void UpdateSummaryTab()
    method UpdateMyProcessTable (line 214) | private void UpdateMyProcessTable()
    method EnsureProcessEntry (line 311) | private void EnsureProcessEntry(string processName)
    method UpdateData (line 324) | private void UpdateData()
    method NetProc_PropertyChanged (line 335) | private void NetProc_PropertyChanged(object? sender, PropertyChangedEv...
    method Dusvm_PropertyChanged (line 378) | private void Dusvm_PropertyChanged(object? sender, PropertyChangedEven...
    method SwitchTab (line 386) | private void SwitchTab(object? obj)
    method OnPropertyChanged (line 425) | private void OnPropertyChanged(string propName) => PropertyChanged?.In...
    method Dispose (line 427) | public void Dispose()

FILE: OpenNetMeter.Old/OpenNetMeter/ViewModels/MiniWidgetVM.cs
  class MiniWidgetVM (line 9) | public class MiniWidgetVM : INotifyPropertyChanged
    method MiniWidgetVM (line 103) | public MiniWidgetVM()
    method OnPropertyChanged (line 127) | private void OnPropertyChanged(string propName) => PropertyChanged?.In...

FILE: OpenNetMeter.Old/OpenNetMeter/ViewModels/SettingsVM.cs
  class SettingsVM (line 15) | public class SettingsVM : INotifyPropertyChanged
    method SyncMiniWidgetVisibility (line 215) | public void SyncMiniWidgetVisibility(bool isVisible)
    method SettingsVM (line 263) | public SettingsVM(MiniWidgetVM mw_ref, ConfirmationDialogVM cdvm_ref)
    method SettingsVM (line 268) | public SettingsVM(MiniWidgetVM mw_ref, ConfirmationDialogVM cdvm_ref, ...
    method SetMiniWidgetBackgroundColor (line 304) | private void SetMiniWidgetBackgroundColor(bool darkMode, int transpare...
    method ResetData (line 316) | private void ResetData(object? obj)
    method UpdateCheck (line 322) | private async void UpdateCheck(object? obj)
    method DownloadUpdate (line 376) | private void DownloadUpdate(object? obj)
    method ResetDataYesOrNo (line 393) | private void ResetDataYesOrNo(object? obj)
    method OnPropertyChanged (line 408) | private void OnPropertyChanged(string propName) => PropertyChanged?.In...

FILE: OpenNetMeter.Old/OpenNetMeter/Views/AboutWindow.xaml.cs
  class AboutWindow (line 12) | public partial class AboutWindow : Window
    method AboutWindow (line 15) | public AboutWindow(Rect parentWindowRect_param)
    method Grid_MouseDown (line 21) | private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
    method CloseWin (line 26) | public void CloseWin()
    method Exit_Button_Click (line 30) | private void Exit_Button_Click(object sender, RoutedEventArgs e)
    method Hyperlink_RequestNavigate (line 35) | private void Hyperlink_RequestNavigate(object sender, RequestNavigateE...
    method SetParentWindowRect (line 43) | public void SetParentWindowRect(Rect parentWindowRect_param)
    method Window_Loaded (line 48) | private void Window_Loaded(object sender, RoutedEventArgs e)
    method Window_IsVisibleChanged (line 54) | private void Window_IsVisibleChanged(object sender, DependencyProperty...

FILE: OpenNetMeter.Old/OpenNetMeter/Views/ConfirmationDialog.xaml.cs
  class ConfirmationDialog (line 10) | public partial class ConfirmationDialog : Window
    method ConfirmationDialog (line 13) | public ConfirmationDialog(Rect parentWindowRect_param)
    method Grid_MouseDown (line 20) | private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
    method SetParentWindowRect (line 25) | public void SetParentWindowRect(Rect parentWindowRect_param)
    method Exit_Button_Click (line 30) | private void Exit_Button_Click(object sender, RoutedEventArgs e)
    method Window_IsVisibleChanged (line 35) | private void Window_IsVisibleChanged(object sender, DependencyProperty...
    method Window_Loaded (line 44) | private void Window_Loaded(object sender, RoutedEventArgs e)

FILE: OpenNetMeter.Old/OpenNetMeter/Views/Converters/BitmapToImageConverter.cs
  class BitmapToImageConverter (line 9) | class BitmapToImageConverter : IValueConverter
    method Convert (line 11) | public object Convert(object value, Type targetType, object parameter,...
    method ConvertBack (line 24) | public object ConvertBack(object value, Type targetType, object parame...

FILE: OpenNetMeter.Old/OpenNetMeter/Views/Converters/NetSpeedFormatConverter.cs
  class NetSpeedFormatConverter (line 9) | public class NetSpeedFormatConverter : IValueConverter
    method Convert (line 11) | public object Convert(object value, Type targetType, object parameter,...
    method ConvertBack (line 19) | public object ConvertBack(object value, Type targetType, object parame...

FILE: OpenNetMeter.Old/OpenNetMeter/Views/Converters/RadioBoolToIntConverter.cs
  class RadioBoolToIntConverter (line 7) | public class RadioBoolToIntConverter : IValueConverter
    method Convert (line 9) | public object Convert(object value, Type targetType, object parameter,...
    method ConvertBack (line 23) | public object ConvertBack(object value, Type targetType, object parame...

FILE: OpenNetMeter.Old/OpenNetMeter/Views/Converters/UiVisibilityToWpfVisibilityConverter.cs
  class UiVisibilityToWpfVisibilityConverter (line 9) | public class UiVisibilityToWpfVisibilityConverter : IValueConverter
    method Convert (line 11) | public object Convert(object value, Type targetType, object parameter,...
    method ConvertBack (line 24) | public object ConvertBack(object value, Type targetType, object parame...

FILE: OpenNetMeter.Old/OpenNetMeter/Views/Converters/UnitConverterBytes.cs
  class UnitConverterBytes (line 8) | public class UnitConverterBytes : IValueConverter
    method Convert (line 10) | public object Convert(object value, Type targetType, object parameter,...
    method ConvertBack (line 15) | public object ConvertBack(object value, Type targetType, object parame...

FILE: OpenNetMeter.Old/OpenNetMeter/Views/CustomSystemTray.cs
  class MyColorTable (line 7) | public class MyColorTable : ProfessionalColorTable
  class CustomSystemTray (line 72) | public class CustomSystemTray : ToolStripProfessionalRenderer
    method CustomSystemTray (line 74) | public CustomSystemTray() : base(new MyColorTable()) { }

FILE: OpenNetMeter.Old/OpenNetMeter/Views/MainWindow.xaml.cs
  class MainWindow (line 21) | public partial class MainWindow : Window
    method IsSingleInstance (line 24) | public bool IsSingleInstance()
    method MainWindow (line 55) | public MainWindow()
    method MainWindow_Loaded (line 92) | private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    method ResetWinPos_Click (line 100) | private void ResetWinPos_Click(object? sender, EventArgs e)
    method MiniWidget_Show_Click (line 116) | private void MiniWidget_Show_Click(object? sender, EventArgs e)
    method ShowMiniWidget (line 121) | public void ShowMiniWidget()
    method HideMiniWidget (line 130) | public void HideMiniWidget()
    method SetMiniWidgetVisibility (line 139) | private void SetMiniWidgetVisibility(bool isVisible)
    method MainWindow_Closing (line 148) | private void MainWindow_Closing(object? sender, CancelEventArgs e)
    method Ni_MouseClick (line 153) | private void Ni_MouseClick(object? sender, Forms.MouseEventArgs e)
    method AllWinPosAndSizeInit (line 170) | private void AllWinPosAndSizeInit()
    method Cm_Open_Click (line 225) | private void Cm_Open_Click(object? sender, EventArgs e)
    method Cm_Exit_Click (line 231) | private void Cm_Exit_Click(object? sender, EventArgs e)
    method Ni_DoubleClick (line 243) | private void Ni_DoubleClick(object? sender, EventArgs e)
    method Grid_MouseDown (line 249) | private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
    method Minimize_Button_Click (line 263) | private void Minimize_Button_Click(object sender, RoutedEventArgs e)
    method Exit_Button_Click (line 268) | public void Exit_Button_Click(object? sender, RoutedEventArgs? e)
    method About_Button_Click (line 284) | private void About_Button_Click(object sender, RoutedEventArgs e)
    method ResizeTimer_Tick (line 291) | private void ResizeTimer_Tick(object? sender, EventArgs e)
    method MyWindow_SizeChanged (line 304) | private void MyWindow_SizeChanged(object? sender, SizeChangedEventArgs e)
    method RelocationTimer_Tick (line 311) | private void RelocationTimer_Tick(object? sender, EventArgs e)
    method SaveWinPos (line 319) | private void SaveWinPos(int x, int y)
    method MyWindow_LocationChanged (line 329) | private void MyWindow_LocationChanged(object sender, EventArgs e)

FILE: OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/DataUsageHistoryV.xaml.cs
  class DataUsageHistoryV (line 21) | public partial class DataUsageHistoryV : UserControl
    method DataUsageHistoryV (line 23) | public DataUsageHistoryV()

FILE: OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/DataUsageSummaryV.xaml.cs
  class DataUsageSummaryV (line 19) | public partial class DataUsageSummaryV : UserControl
    method DataUsageSummaryV (line 27) | public DataUsageSummaryV()
    method ResizeTimer_Tick (line 48) | private void ResizeTimer_Tick(object? sender, EventArgs e)
    method ShapeMeasure (line 62) | public Size ShapeMeasure(TextBlock tb)
    method Graph_SizeChanged (line 73) | private void Graph_SizeChanged(object? sender, System.Windows.SizeChan...

FILE: OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/SettingsV.xaml.cs
  class SettingsV (line 8) | public partial class SettingsV : UserControl
    method SettingsV (line 10) | public SettingsV()

FILE: OpenNetMeter.Old/OpenNetMeter/Views/MiniWidgetV.xaml.cs
  class MiniWidgetV (line 15) | public partial class MiniWidgetV : Window
    method MiniWidgetV (line 21) | public MiniWidgetV(Window mainWindow_ref)
    method MiniWidgetV_Loaded (line 37) | private void MiniWidgetV_Loaded(object sender, RoutedEventArgs e)
    method FixZorderTimer_Tick (line 46) | private void FixZorderTimer_Tick(object? sender, EventArgs e)
    method Window_MouseLeftButtonDown (line 79) | private void Window_MouseLeftButtonDown(object sender, System.Windows....
    method MenuItem_Hide_Click (line 89) | private void MenuItem_Hide_Click(object sender, RoutedEventArgs e)
    method MenuItem_Open_Click (line 96) | private void MenuItem_Open_Click(object sender, RoutedEventArgs e)
    method RelocationTimer_Tick (line 110) | private void RelocationTimer_Tick(object? sender, EventArgs e)
    method SaveWinPos (line 117) | public void SaveWinPos(int x, int y)
    method Window_LocationChanged (line 123) | private void Window_LocationChanged(object sender, System.EventArgs e)
    method ShowMiniWidget (line 130) | public void ShowMiniWidget()
    method HideMiniWidget (line 140) | public void HideMiniWidget()

FILE: OpenNetMeter.PlatformAbstractions/IExternalLinkService.cs
  type IExternalLinkService (line 3) | public interface IExternalLinkService
    method Open (line 5) | void Open(string uri);

FILE: OpenNetMeter.PlatformAbstractions/INetworkCaptureService.cs
  type INetworkCaptureService (line 5) | public interface INetworkCaptureService : IDisposable
    method Start (line 10) | void Start();
    method Stop (line 11) | void Stop();
  class NetworkSnapshotChangedEventArgs (line 14) | public sealed class NetworkSnapshotChangedEventArgs : EventArgs
    method NetworkSnapshotChangedEventArgs (line 16) | public NetworkSnapshotChangedEventArgs(string adapterName, string adap...
  class NetworkTrafficEventArgs (line 26) | public sealed class NetworkTrafficEventArgs : EventArgs
    method NetworkTrafficEventArgs (line 28) | public NetworkTrafficEventArgs(string processName, long bytes, bool is...

FILE: OpenNetMeter.PlatformAbstractions/IProcessIconService.cs
  type IProcessIconService (line 3) | public interface IProcessIconService
    method GetProcessIcon (line 5) | object? GetProcessIcon(string processName);

FILE: OpenNetMeter.PlatformAbstractions/IStartupRegistrationService.cs
  type IStartupRegistrationService (line 3) | public interface IStartupRegistrationService
    method IsEnabled (line 5) | bool IsEnabled();
    method SetEnabled (line 6) | void SetEnabled(bool enabled, bool startMinimized);

FILE: OpenNetMeter.PlatformAbstractions/IUiDispatcher.cs
  type IUiDispatcher (line 5) | public interface IUiDispatcher
    method CheckAccess (line 7) | bool CheckAccess();
    method Post (line 8) | void Post(Action action);

FILE: OpenNetMeter.PlatformAbstractions/IWindowService.cs
  type IWindowService (line 3) | public interface IWindowService
    method MinimizeMainWindow (line 5) | void MinimizeMainWindow();
    method CloseMainWindow (line 6) | void CloseMainWindow();
    method ShowAbout (line 7) | void ShowAbout();

FILE: OpenNetMeter.PlatformAbstractions/UiVisibility.cs
  type UiVisibility (line 3) | public enum UiVisibility

FILE: OpenNetMeter.Tests/NetworkProcessTests.cs
  class NetworkProcessTests (line 8) | public class NetworkProcessTests
    method RecvProcess_AccumulatesDownloadPerProcess (line 13) | [Fact]
    method SendProcess_TracksUploadWhileBuffering (line 29) | [Fact]
    method RecvProcess_PublicOnlyDropsPrivatePeer (line 46) | [Fact]
    method RecvProcess_PrivateOnlyAcceptsPrivateTraffic (line 58) | [Fact]
    method CreateNetworkProcess (line 73) | private static NetworkProcess CreateNetworkProcess()
    class NetworkTypeScope (line 80) | private sealed class NetworkTypeScope : IDisposable
      method NetworkTypeScope (line 84) | public NetworkTypeScope(int networkType)
      method Dispose (line 90) | public void Dispose()

FILE: OpenNetMeter/App.axaml.cs
  class App (line 14) | public partial class App : Application
    method Initialize (line 18) | public override void Initialize()
    method OnFrameworkInitializationCompleted (line 23) | public override void OnFrameworkInitializationCompleted()
    method RegisterUnhandledExceptionLoggingOnce (line 87) | private static void RegisterUnhandledExceptionLoggingOnce()
    method HasStartMinimizedArgument (line 107) | private static bool HasStartMinimizedArgument()

FILE: OpenNetMeter/Compat/Models/ApplicationDB.cs
  class ApplicationDB (line 9) | internal sealed class ApplicationDB : IDisposable
    method LogTime (line 18) | private static string LogTime() => DateTime.Now.ToString("HH:mm:ss.fff");
    method ApplicationDB (line 23) | public ApplicationDB(string dBFileName)
    method GetUnifiedDBFullPath (line 55) | public static string GetUnifiedDBFullPath()
    method CloseSharedConnection (line 60) | public static void CloseSharedConnection()
    method CreateTable (line 70) | public int CreateTable()
    method InsertUniqueRow_AdapterTable (line 82) | public int InsertUniqueRow_AdapterTable(string adapter)
    method UpdateDatesInDB (line 93) | public void UpdateDatesInDB()
    method PushToDB (line 104) | public void PushToDB(string processName, long totalDataRecv, long tota...
    method GetDataSum_ProcessDateTable (line 124) | public List<List<object>> GetDataSum_ProcessDateTable(DateTime date1, ...
    method GetDataSumBetweenDates (line 145) | public (long, long) GetDataSumBetweenDates(DateTime startDate, DateTim...
    method GetTodayDataSum_ProcessDateTable (line 180) | public (long, long) GetTodayDataSum_ProcessDateTable()
    method GetID_AdapterTable (line 206) | public long GetID_AdapterTable(string adapter)
    method GetAllAdapters (line 214) | public List<string> GetAllAdapters()
    method Dispose (line 233) | public void Dispose()
    method CreateProcessTable (line 256) | private int CreateProcessTable()
    method CreateDateTable (line 264) | private int CreateDateTable()
    method CreateProcessDateTable (line 275) | private int CreateProcessDateTable()
    method CreateAdapterTable (line 291) | private int CreateAdapterTable()
    method InsertUniqueRow_ProcessTable (line 299) | private int InsertUniqueRow_ProcessTable(string appName)
    method InsertUniqueRow_DateTable (line 306) | private int InsertUniqueRow_DateTable(DateTime date)
    method RemoveOldDate (line 315) | private void RemoveOldDate()
    method RemoveOldProcess (line 324) | private void RemoveOldProcess()
    method InsertUniqueRow_ProcessDateTable (line 332) | private int InsertUniqueRow_ProcessDateTable(long processID, long date...
    method UpdateRow_ProcessDateTable (line 344) | private int UpdateRow_ProcessDateTable(long processID, long dateID, lo...
    method GetID_DateTable (line 358) | private long GetID_DateTable(DateTime time)
    method GetID_ProcessTable (line 369) | private long GetID_ProcessTable(string appName)
    method GetID_AdapterTable_Internal (line 378) | private long GetID_AdapterTable_Internal(string adapter)
    method RunNonQuery (line 387) | private static int RunNonQuery(string query, params (string Name, stri...
    method RunNonQuery (line 392) | private static int RunNonQuery(SqliteConnection connection, string que...
    method GetMultipleCellData (line 412) | private static List<List<object>> GetMultipleCellData(string query, pa...
    method GetSingleCellData (line 446) | private static object? GetSingleCellData(string query, params (string ...

FILE: OpenNetMeter/Compat/Models/MyProcess_Small.cs
  class MyProcess_Small (line 3) | public class MyProcess_Small
    method MyProcess_Small (line 9) | public MyProcess_Small(string nameP, long currentDataRecvP, long curre...

FILE: OpenNetMeter/Compat/Models/NetworkProcess.TestHooks.cs
  class NetworkProcess (line 6) | public partial class NetworkProcess
    method TestSetLocalIPs (line 8) | internal void TestSetLocalIPs(byte[] ipv4, byte[] ipv6)
    method TestInvokeRecvProcess (line 14) | internal void TestInvokeRecvProcess(IPAddress src, IPAddress dest, int...
    method TestInvokeSendProcess (line 19) | internal void TestInvokeSendProcess(IPAddress src, IPAddress dest, int...

FILE: OpenNetMeter/Compat/Models/NetworkProcess.cs
  class NetworkProcess (line 22) | [SupportedOSPlatform("windows")]
    type NetworkSnapshot (line 40) | private sealed record NetworkSnapshot(
    method Initialize (line 148) | public void Initialize()
    method GetCurrentNetworkSnapshot (line 171) | private NetworkSnapshot? GetCurrentNetworkSnapshot()
    method GetConnectedSsid (line 243) | private static string? GetConnectedSsid(string adapterGuid)
    method OnNetworkAddressChanged (line 281) | private void OnNetworkAddressChanged(object? sender, EventArgs? e)
    method HandleNetworkChange (line 312) | private void HandleNetworkChange()
    method HasSnapshotAddressChanged (line 371) | private bool HasSnapshotAddressChanged(NetworkSnapshot snapshot)
    method ApplySnapshot (line 385) | private void ApplySnapshot(NetworkSnapshot snapshot)
    method StartNetworkProcess (line 405) | public void StartNetworkProcess()
    method EndNetworkProcess (line 435) | public void EndNetworkProcess()
    method StopKernelSession (line 464) | private void StopKernelSession()
    method StopPeriodicWork (line 496) | private void StopPeriodicWork(ref PeriodicWork? work, string name)
    method FlushPendingDbWrites (line 512) | private void FlushPendingDbWrites()
    method StartSpeedMonitoring (line 539) | private void StartSpeedMonitoring()
    method StartDbPush (line 577) | private void StartDbPush()
    method CaptureNetworkPackets (line 615) | private void CaptureNetworkPackets()
    method ProcessPacket (line 663) | private void ProcessPacket(IPAddress src, IPAddress dest, int size, st...
    method ShouldProcessByNetworkType (line 703) | private bool ShouldProcessByNetworkType(bool isLocalSrc, IPAddress src...
    method RecordRecv (line 720) | private void RecordRecv(string name, int size)
    method RecordSend (line 730) | private void RecordSend(string name, int size)
    method RecordToBuffer (line 741) | private void RecordToBuffer(string name, int size, bool isRecv)
    method IsPrivateIP (line 777) | private bool IsPrivateIP(IPAddress ip)
    method OnPropertyChanged (line 812) | private void OnPropertyChanged(string propName) =>
    method Dispose (line 821) | public void Dispose()

FILE: OpenNetMeter/Compat/Properties/AppSettings.cs
  class AppSettings (line 3) | public class AppSettings

FILE: OpenNetMeter/Compat/Properties/Global.cs
  class Global (line 6) | internal class Global
    method GetFilePath (line 10) | public static string GetFilePath()

FILE: OpenNetMeter/Compat/Properties/SettingsManager.cs
  class SettingsManager (line 9) | internal static class SettingsManager
    method SettingsManager (line 15) | static SettingsManager()
    method Load (line 21) | public static void Load()
    method Save (line 60) | public static void Save()
    method GetBool (line 108) | private static bool GetBool(JsonObject root, string key, bool fallback)
    method GetInt (line 124) | private static int GetInt(JsonObject root, string key, int fallback)

FILE: OpenNetMeter/Compat/Utilities/ByteArray.cs
  class ByteArray (line 9) | internal class ByteArray
    method Compare (line 11) | public static bool Compare(ReadOnlySpan<byte> a1, ReadOnlySpan<byte> a2)

FILE: OpenNetMeter/Compat/Utilities/EventLogger.cs
  class EventLogger (line 9) | public static class EventLogger
    type LogLevel (line 14) | private enum LogLevel
    method Info (line 21) | public static void Info(string message, int eventId = 1000, short cate...
    method Warn (line 26) | public static void Warn(string message, int eventId = 2000, short cate...
    method Error (line 31) | public static void Error(
    method Error (line 43) | public static void Error(
    method Error (line 54) | public static void Error(
    method AddCallerContext (line 71) | private static string AddCallerContext(string message, string memberNa...
    method WriteEntrySafe (line 77) | private static void WriteEntrySafe(string message, LogLevel level, int...
    method WriteEntryWindows (line 95) | [SupportedOSPlatform("windows")]
    method Truncate (line 108) | private static string Truncate(string message)

FILE: OpenNetMeter/Compat/Utilities/PeriodicWork.cs
  class PeriodicWork (line 8) | internal sealed class PeriodicWork : IDisposable, IAsyncDisposable
    method PeriodicWork (line 18) | public PeriodicWork(string name, TimeSpan interval)
    method Start (line 24) | public void Start(Func<CancellationToken, Task> onTick)
    method RunAsync (line 34) | private async Task RunAsync(Func<CancellationToken, Task> onTick, Canc...
    method StopAsync (line 62) | public async Task StopAsync()
    method Stop (line 91) | public void Stop()
    method Dispose (line 96) | public void Dispose()
    method DisposeAsync (line 101) | public ValueTask DisposeAsync()

FILE: OpenNetMeter/Program.cs
  class Program (line 6) | internal sealed class Program
    method Main (line 8) | [STAThread]
    method BuildAvaloniaApp (line 14) | public static AppBuilder BuildAvaloniaApp()

FILE: OpenNetMeter/Services/AvaloniaThemeService.cs
  class AvaloniaThemeService (line 6) | public sealed class AvaloniaThemeService : IThemeService
    method AvaloniaThemeService (line 10) | public AvaloniaThemeService(Application application)
    method ApplyDarkMode (line 15) | public void ApplyDarkMode(bool enabled)

FILE: OpenNetMeter/Services/AvaloniaWindowService.cs
  class AvaloniaWindowService (line 8) | public sealed class AvaloniaWindowService : IWindowService
    method MinimizeMainWindow (line 10) | public void MinimizeMainWindow()
    method CloseMainWindow (line 16) | public void CloseMainWindow()
    method ShowAbout (line 22) | public void ShowAbout()
    method GetMainWindow (line 27) | private static Window? GetMainWindow()

FILE: OpenNetMeter/Services/ExternalLinkService.cs
  class ExternalLinkService (line 8) | public sealed class ExternalLinkService : IExternalLinkService
    method Open (line 10) | public void Open(string uri)

FILE: OpenNetMeter/Services/IMiniWidgetService.cs
  type IMiniWidgetService (line 5) | public interface IMiniWidgetService : System.IDisposable
    method Show (line 9) | void Show();
    method Hide (line 10) | void Hide();
    method RefreshAppearance (line 11) | void RefreshAppearance(bool darkMode, int transparency);
    method ResetPosition (line 12) | void ResetPosition(Window mainWindow);
    method EnsurePositionOnScreen (line 13) | void EnsurePositionOnScreen(Window mainWindow);

FILE: OpenNetMeter/Services/IThemeService.cs
  type IThemeService (line 3) | public interface IThemeService
    method ApplyDarkMode (line 5) | void ApplyDarkMode(bool enabled);

FILE: OpenNetMeter/Services/ITrayNotificationService.cs
  type ITrayNotificationService (line 6) | public interface ITrayNotificationService : IDisposable
    method ShowMinimizedToTrayOnce (line 8) | void ShowMinimizedToTrayOnce(Window mainWindow);

FILE: OpenNetMeter/Services/ITrayService.cs
  type ITrayService (line 3) | public interface ITrayService : System.IDisposable

FILE: OpenNetMeter/Services/NoOpThemeService.cs
  class NoOpThemeService (line 3) | public sealed class NoOpThemeService : IThemeService
    method ApplyDarkMode (line 5) | public void ApplyDarkMode(bool enabled)

FILE: OpenNetMeter/Services/PlaceholderMiniWidgetService.cs
  class PlaceholderMiniWidgetService (line 5) | public sealed class PlaceholderMiniWidgetService : IMiniWidgetService
    method Show (line 13) | public void Show()
    method Hide (line 17) | public void Hide()
    method RefreshAppearance (line 21) | public void RefreshAppearance(bool darkMode, int transparency)
    method ResetPosition (line 25) | public void ResetPosition(Window mainWindow)
    method EnsurePositionOnScreen (line 29) | public void EnsurePositionOnScreen(Window mainWindow)
    method Dispose (line 33) | public void Dispose()

FILE: OpenNetMeter/Services/PlaceholderNetworkCaptureService.cs
  class PlaceholderNetworkCaptureService (line 6) | public sealed class PlaceholderNetworkCaptureService : INetworkCaptureSe...
    method Start (line 17) | public void Start()
    method Stop (line 26) | public void Stop()
    method Dispose (line 31) | public void Dispose()

FILE: OpenNetMeter/Services/PlaceholderProcessIconService.cs
  class PlaceholderProcessIconService (line 5) | public sealed class PlaceholderProcessIconService : IProcessIconService
    method GetProcessIcon (line 7) | public object? GetProcessIcon(string processName) => null;

FILE: OpenNetMeter/Services/PlaceholderStartupRegistrationService.cs
  class PlaceholderStartupRegistrationService (line 5) | public sealed class PlaceholderStartupRegistrationService : IStartupRegi...
    method IsEnabled (line 7) | public bool IsEnabled() => false;
    method SetEnabled (line 9) | public void SetEnabled(bool enabled, bool startMinimized)

FILE: OpenNetMeter/Services/PlaceholderTrayNotificationService.cs
  class PlaceholderTrayNotificationService (line 5) | public sealed class PlaceholderTrayNotificationService : ITrayNotificati...
    method ShowMinimizedToTrayOnce (line 7) | public void ShowMinimizedToTrayOnce(Window mainWindow)
    method Dispose (line 11) | public void Dispose()

FILE: OpenNetMeter/Services/PlaceholderTrayService.cs
  class PlaceholderTrayService (line 3) | public sealed class PlaceholderTrayService : ITrayService
    method Dispose (line 5) | public void Dispose()

FILE: OpenNetMeter/Services/UpdateChecker.cs
  class UpdateChecker (line 8) | public static class UpdateChecker
    method CheckForUpdatesAsync (line 10) | public static async Task<(Version? latestVersion, string? downloadUrl)...

FILE: OpenNetMeter/Services/WindowsMiniWidgetService.cs
  class WindowsMiniWidgetService (line 11) | public sealed class WindowsMiniWidgetService : IMiniWidgetService
    method WindowsMiniWidgetService (line 22) | public WindowsMiniWidgetService(MiniWidgetViewModel viewModel, Window ...
    method Show (line 37) | public void Show()
    method Hide (line 55) | public void Hide()
    method RefreshAppearance (line 71) | public void RefreshAppearance(bool darkMode, int transparency)
    method ResetPosition (line 76) | public void ResetPosition(Window mainWindow)
    method EnsurePositionOnScreen (line 103) | public void EnsurePositionOnScreen(Window mainWindow)
    method Dispose (line 116) | public void Dispose()
    method OpenMainWindow (line 129) | private void OpenMainWindow()
    method Window_Opened (line 147) | private void Window_Opened(object? sender, EventArgs e)
    method Window_PositionChanged (line 171) | private void Window_PositionChanged(object? sender, PixelPointEventArg...
    method SaveWindowPosition (line 186) | private void SaveWindowPosition()
    method IsWindowInBounds (line 194) | private static bool IsWindowInBounds(Window target)

FILE: OpenNetMeter/Services/WindowsNetworkCaptureService.cs
  class WindowsNetworkCaptureService (line 9) | [SupportedOSPlatform("windows")]
    method Start (line 19) | public void Start()
    method Stop (line 33) | public void Stop()
    method Dispose (line 46) | public void Dispose()
    method NetworkProcess_PropertyChanged (line 58) | private void NetworkProcess_PropertyChanged(object? sender, PropertyCh...
    method EmitProcessTraffic (line 83) | private void EmitProcessTraffic()
    method StageForDatabase (line 129) | private void StageForDatabase(string processName, long receivedBytes, ...
    method ThrowIfDisposed (line 150) | private void ThrowIfDisposed()

FILE: OpenNetMeter/Services/WindowsProcessIconService.cs
  class WindowsProcessIconService (line 15) | [SupportedOSPlatform("windows")]
    method GetProcessIcon (line 21) | public object? GetProcessIcon(string processName)
    method LoadIcon (line 29) | private static AvaloniaBitmap? LoadIcon(string processName)
    method CreateDefaultIcon (line 78) | private static AvaloniaBitmap? CreateDefaultIcon()

FILE: OpenNetMeter/Services/WindowsStartupRegistrationService.cs
  class WindowsStartupRegistrationService (line 10) | [SupportedOSPlatform("windows")]
    method IsEnabled (line 16) | public bool IsEnabled()
    method SetEnabled (line 29) | public void SetEnabled(bool enabled, bool startMinimized)
    method CreateTask (line 64) | private static void CreateTask(bool startMinimized)
    method ResolveLaunchCommand (line 107) | private static (string path, string? arguments) ResolveLaunchCommand(b...

FILE: OpenNetMeter/Services/WindowsTrayNotificationService.cs
  class WindowsTrayNotificationService (line 11) | [SupportedOSPlatform("windows")]
    method ShowMinimizedToTrayOnce (line 28) | public void ShowMinimizedToTrayOnce(Window mainWindow)
    method Dispose (line 67) | public void Dispose()
    method RemoveTemporaryIconAsync (line 80) | private async Task RemoveTemporaryIconAsync(IntPtr handle)
    method LoadNotificationIcon (line 95) | private static Icon LoadNotificationIcon()
    method CreateNotifyIconData (line 108) | private static NOTIFYICONDATA CreateNotifyIconData(IntPtr handle, IntP...
    method Shell_NotifyIcon (line 119) | [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
    type NOTIFYICONDATA (line 122) | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]

FILE: OpenNetMeter/Services/WindowsTrayService.cs
  class WindowsTrayService (line 11) | public sealed class WindowsTrayService : ITrayService
    method WindowsTrayService (line 15) | public WindowsTrayService(
    method Dispose (line 58) | public void Dispose()

FILE: OpenNetMeter/Services/WindowsWidgetZOrderHelper.cs
  class WindowsWidgetZOrderHelper (line 9) | internal sealed class WindowsWidgetZOrderHelper : IDisposable
    method WindowsWidgetZOrderHelper (line 18) | public WindowsWidgetZOrderHelper(Window window)
    method Start (line 25) | public void Start()
    method Stop (line 30) | public void Stop()
    method Dispose (line 35) | public void Dispose()
    method Timer_Tick (line 41) | private void Timer_Tick(object? sender, EventArgs e)
    method FindWindowEx (line 79) | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    method GetWindow (line 82) | [DllImport("user32.dll", SetLastError = true)]
    method SetWindowPos (line 85) | [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
    method GetWindowLongPtr64 (line 88) | [DllImport("user32.dll", EntryPoint = "GetWindowLongPtrW", SetLastErro...
    method GetWindowLong32 (line 91) | [DllImport("user32.dll", EntryPoint = "GetWindowLongW", SetLastError =...
    method SetWindowLongPtr64 (line 94) | [DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW", SetLastErro...
    method SetWindowLong32 (line 97) | [DllImport("user32.dll", EntryPoint = "SetWindowLongW", SetLastError =...
    method GetWindowLongPtr (line 100) | private static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
    method SetWindowLongPtr (line 107) | private static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr...

FILE: OpenNetMeter/ViewModels/ByteSizeFormatter.cs
  class ByteSizeFormatter (line 3) | internal static class ByteSizeFormatter
    method FormatBytes (line 5) | public static string FormatBytes(long value)

FILE: OpenNetMeter/ViewModels/HistoryViewModel.cs
  class HistoryViewModel (line 15) | public sealed class HistoryViewModel : INotifyPropertyChanged
    method HistoryViewModel (line 27) | public HistoryViewModel()
    method HistoryViewModel (line 32) | public HistoryViewModel(IProcessIconService processIconService)
    method ReloadProfiles (line 134) | public void ReloadProfiles()
    method DeleteAllDbFiles (line 149) | public void DeleteAllDbFiles()
    method LoadProfiles (line 171) | private void LoadProfiles()
    method ApplyFilter (line 190) | private void ApplyFilter()
    method SortRows (line 236) | private void SortRows(string column)
    method OpenReadOnlyConnection (line 266) | private static SqliteConnection OpenReadOnlyConnection(string path)
    method ToDateInt (line 276) | private static int ToDateInt(DateTime date)
    method ResolveDatabasePath (line 281) | private static string ResolveDatabasePath()
    method DeleteIfExists (line 288) | private static void DeleteIfExists(string path)
    method OnPropertyChanged (line 294) | private void OnPropertyChanged(string propertyName)
    class NoOpProcessIconService (line 299) | private sealed class NoOpProcessIconService : IProcessIconService
      method GetProcessIcon (line 301) | public object? GetProcessIcon(string processName) => null;
  class HistoryRowViewModel (line 305) | public sealed class HistoryRowViewModel
    method HistoryRowViewModel (line 307) | public HistoryRowViewModel(string processName, long downloadBytes, lon...

FILE: OpenNetMeter/ViewModels/MainWindowViewModel.cs
  class MainWindowViewModel (line 10) | public sealed class MainWindowViewModel : MainShellTabsViewModel, IDispo...
    method MainWindowViewModel (line 21) | public MainWindowViewModel()
    method MainWindowViewModel (line 26) | public MainWindowViewModel(
    method Dispose (line 126) | public void Dispose()
    method OnNetworkChanged (line 136) | private void OnNetworkChanged(object? sender, NetworkSnapshotChangedEv...
    method Settings_PropertyChanged (line 150) | private void Settings_PropertyChanged(object? sender, System.Component...
    method Settings_DeleteAllDataConfirmed (line 159) | private void Settings_DeleteAllDataConfirmed()
    method Summary_PropertyChanged (line 180) | private void Summary_PropertyChanged(object? sender, System.ComponentM...
    method SyncMiniWidgetFromSummary (line 193) | private void SyncMiniWidgetFromSummary()
    class NoOpWindowService (line 201) | private sealed class NoOpWindowService : IWindowService
      method MinimizeMainWindow (line 203) | public void MinimizeMainWindow() { }
      method CloseMainWindow (line 204) | public void CloseMainWindow() { }
      method ShowAbout (line 205) | public void ShowAbout() { }
    class NoOpNetworkCaptureService (line 208) | private sealed class NoOpNetworkCaptureService : INetworkCaptureService
      method Start (line 221) | public void Start() { }
      method Stop (line 222) | public void Stop() { }
      method Dispose (line 223) | public void Dispose() { }
    class NoOpProcessIconService (line 226) | private sealed class NoOpProcessIconService : IProcessIconService
      method GetProcessIcon (line 228) | public object? GetProcessIcon(string processName) => null;
    class NoOpExternalLinkService (line 231) | private sealed class NoOpExternalLinkService : IExternalLinkService
      method Open (line 233) | public void Open(string uri) { }

FILE: OpenNetMeter/ViewModels/MiniWidgetViewModel.cs
  class MiniWidgetViewModel (line 12) | public sealed class MiniWidgetViewModel : INotifyPropertyChanged
    method MiniWidgetViewModel (line 114) | public MiniWidgetViewModel()
    method SetActions (line 121) | public void SetActions(Action openMainWindow, Action hideWidget)
    method RefreshBackground (line 129) | public void RefreshBackground(bool darkMode, int transparency)
    method LoadBitmap (line 142) | private static Bitmap LoadBitmap(string assetUri)
    method OnPropertyChanged (line 156) | private void OnPropertyChanged(string propertyName) =>

FILE: OpenNetMeter/ViewModels/RelayCommand.cs
  class RelayCommand (line 6) | public sealed class RelayCommand : ICommand
    method RelayCommand (line 11) | public RelayCommand(Action execute, Func<bool>? canExecute = null)
    method CanExecute (line 19) | public bool CanExecute(object? parameter)
    method Execute (line 24) | public void Execute(object? parameter)
    method RaiseCanExecuteChanged (line 29) | public void RaiseCanExecuteChanged()
  class ParameterRelayCommand (line 35) | public sealed class ParameterRelayCommand : ICommand
    method ParameterRelayCommand (line 40) | public ParameterRelayCommand(Action<object?> execute, Func<object?, bo...
    method CanExecute (line 48) | public bool CanExecute(object? parameter)
    method Execute (line 53) | public void Execute(object? parameter)
    method RaiseCanExecuteChanged (line 58) | public void RaiseCanExecuteChanged()

FILE: OpenNetMeter/ViewModels/SettingsViewModel.cs
  class SettingsViewModel (line 14) | public sealed class SettingsViewModel : INotifyPropertyChanged
    method SettingsViewModel (line 34) | public SettingsViewModel(MiniWidgetViewModel miniWidgetViewModel, IMin...
    method SettingsViewModel (line 61) | public SettingsViewModel()
    method SyncMiniWidgetVisibility (line 289) | public void SyncMiniWidgetVisibility(bool isVisible)
    method ResetData (line 300) | private void ResetData()
    method CheckForUpdatesAsync (line 305) | private async Task CheckForUpdatesAsync()
    method DownloadUpdate (line 358) | private void DownloadUpdate()
    method ConfirmDeleteAllData (line 377) | private void ConfirmDeleteAllData()
    method CancelDeleteAllData (line 383) | private void CancelDeleteAllData()
    method OnPropertyChanged (line 388) | private void OnPropertyChanged(string propertyName) =>
    class NoOpExternalLinkService (line 391) | private sealed class NoOpExternalLinkService : IExternalLinkService
      method Open (line 393) | public void Open(string uri)

FILE: OpenNetMeter/ViewModels/SummaryViewModel.cs
  class SummaryViewModel (line 23) | public sealed class SummaryViewModel : INotifyPropertyChanged, IDisposable
    method SummaryViewModel (line 58) | public SummaryViewModel(INetworkCaptureService networkCaptureService, ...
    method Dispose (line 163) | public void Dispose()
    method ClearOnDisconnect (line 169) | public void ClearOnDisconnect()
    method SetActiveAdapter (line 210) | public void SetActiveAdapter(string adapterName)
    method OnTrafficObserved (line 220) | private void OnTrafficObserved(object? sender, NetworkTrafficEventArgs e)
    method FlushPendingTraffic (line 243) | private void FlushPendingTraffic()
    method RefreshSinceDateBaseline (line 279) | private void RefreshSinceDateBaseline()
    method UpdateTotalFromDateFromBaselines (line 304) | private void UpdateTotalFromDateFromBaselines()
    method ReadDbTotals (line 318) | private static (long download, long upload) ReadDbTotals(string adapte...
    method OpenReadOnlyConnection (line 361) | private static SqliteConnection OpenReadOnlyConnection(string path)
    method ToDateInt (line 371) | private static int ToDateInt(DateTime date)
    method ResolveDatabasePath (line 376) | private static string ResolveDatabasePath()
    method NormalizeSinceDate (line 383) | private DateTimeOffset NormalizeSinceDate(DateTimeOffset? value)
    method AppendGraphPoint (line 393) | private void AppendGraphPoint()
    method CreateGraphYAxes (line 413) | private Axis[] CreateGraphYAxes()
    method MbpsToGraphValue (line 430) | private static double MbpsToGraphValue(double mbps)
    method GraphValueToMbps (line 439) | private static double GraphValueToMbps(double graphValue)
    method GraphValueToBytesPerSecond (line 447) | private static long GraphValueToBytesPerSecond(double graphValue)
    method UpdateGraphAxisLabelScale (line 455) | private void UpdateGraphAxisLabelScale()
    method FormatGraphAxisLabel (line 475) | private string FormatGraphAxisLabel(double graphValue)
    method ApplyProcessTick (line 486) | private void ApplyProcessTick(Dictionary<string, PendingTraffic> pendi...
    method SortProcesses (line 512) | private void SortProcesses(string column)
    method RefreshSpeedDisplayFormat (line 543) | public void RefreshSpeedDisplayFormat()
    method FormatSpeed (line 552) | private static string FormatSpeed(long bytesPerSecond)
    method ScaleToMagnitude (line 562) | private static decimal ScaleToMagnitude(long value, int magnitude)
    method FormatGraphAxisValue (line 568) | private static string FormatGraphAxisValue(decimal adjustedSize)
    method NormalizeMagnitude (line 582) | private static SpeedMagnitude NormalizeMagnitude(int magnitude)
    method GetAdjustedSize (line 589) | private static (decimal adjustedSize, int mag) GetAdjustedSize(long va...
    method BytesSuffix (line 615) | private static string BytesSuffix(int value)
    method BitsSuffix (line 630) | private static string BitsSuffix(int value)
    method OnPropertyChanged (line 645) | private void OnPropertyChanged(string propertyName)
    class PendingTraffic (line 650) | private sealed class PendingTraffic
  type SpeedMagnitude (line 657) | internal enum SpeedMagnitude
  class SummaryProcessRowViewModel (line 665) | public sealed class SummaryProcessRowViewModel : INotifyPropertyChanged
    method SummaryProcessRowViewModel (line 672) | public SummaryProcessRowViewModel(string processName, IImage? icon = n...
    method ApplyTick (line 692) | public void ApplyTick(long secondDownloadBytes, long secondUploadBytes)
    method ResetCurrent (line 709) | public void ResetCurrent()
    method ApplyTraffic (line 722) | public void ApplyTraffic(long bytes, bool isReceive)
    method OnPropertyChanged (line 744) | private void OnPropertyChanged(string propertyName) =>

FILE: OpenNetMeter/Views/MainWindow.axaml.cs
  class MainWindow (line 13) | public partial class MainWindow : Window
    method MainWindow (line 21) | public MainWindow()
    method InitializeWindowState (line 36) | public void InitializeWindowState(IMiniWidgetService miniWidgetService...
    method OpenFromTray (line 53) | public void OpenFromTray()
    method PrepareForExit (line 64) | public void PrepareForExit()
    method ResetWindowPositions (line 69) | public void ResetWindowPositions()
    method TitleBar_PointerPressed (line 76) | private void TitleBar_PointerPressed(object? sender, PointerPressedEve...
    method ResizeTop_PointerPressed (line 81) | private void ResizeTop_PointerPressed(object? sender, PointerPressedEv...
    method ResizeBottom_PointerPressed (line 82) | private void ResizeBottom_PointerPressed(object? sender, PointerPresse...
    method ResizeLeft_PointerPressed (line 83) | private void ResizeLeft_PointerPressed(object? sender, PointerPressedE...
    method ResizeRight_PointerPressed (line 84) | private void ResizeRight_PointerPressed(object? sender, PointerPressed...
    method ResizeTopLeft_PointerPressed (line 85) | private void ResizeTopLeft_PointerPressed(object? sender, PointerPress...
    method ResizeTopRight_PointerPressed (line 86) | private void ResizeTopRight_PointerPressed(object? sender, PointerPres...
    method ResizeBottomLeft_PointerPressed (line 87) | private void ResizeBottomLeft_PointerPressed(object? sender, PointerPr...
    method ResizeBottomRight_PointerPressed (line 88) | private void ResizeBottomRight_PointerPressed(object? sender, PointerP...
    method TryBeginResize (line 90) | private void TryBeginResize(WindowEdge edge, PointerPressedEventArgs e)
    method OnClosed (line 96) | protected override void OnClosed(System.EventArgs e)
    method MainWindow_Closing (line 107) | private void MainWindow_Closing(object? sender, WindowClosingEventArgs e)
    method MainWindow_Opened (line 117) | private void MainWindow_Opened(object? sender, EventArgs e)
    method MainWindow_PositionChanged (line 135) | private void MainWindow_PositionChanged(object? sender, PixelPointEven...
    method MainWindow_SizeChanged (line 143) | private void MainWindow_SizeChanged(object? sender, SizeChangedEventAr...
    method ResizeTimer_Tick (line 151) | private void ResizeTimer_Tick(object? sender, EventArgs e)
    method RelocationTimer_Tick (line 157) | private void RelocationTimer_Tick(object? sender, EventArgs e)
    method SaveWindowGeometry (line 163) | private void SaveWindowGeometry()
    method CenterOnPrimaryScreen (line 176) | private void CenterOnPrimaryScreen()
    method RestartTimer (line 191) | private static void RestartTimer(DispatcherTimer timer)
    method IsWindowInBounds (line 197) | private static bool IsWindowInBounds(Window target)

FILE: OpenNetMeter/Views/MainWindow/History.axaml.cs
  class HistoryView (line 5) | public partial class HistoryView : UserControl
    method HistoryView (line 7) | public HistoryView()

FILE: OpenNetMeter/Views/MainWindow/Settings.axaml.cs
  class SettingsView (line 5) | public partial class SettingsView : UserControl
    method SettingsView (line 7) | public SettingsView()

FILE: OpenNetMeter/Views/MainWindow/Summary.axaml.cs
  class SummaryView (line 5) | public partial class SummaryView : UserControl
    method SummaryView (line 7) | public SummaryView()

FILE: OpenNetMeter/Views/MiniWidgetWindow.axaml.cs
  class MiniWidgetWindow (line 9) | public partial class MiniWidgetWindow : Window
    method MiniWidgetWindow (line 11) | public MiniWidgetWindow()
    method InitializeComponent (line 16) | private void InitializeComponent()
    method WidgetChrome_PointerPressed (line 21) | private void WidgetChrome_PointerPressed(object? sender, PointerPresse...
Condensed preview — 154 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (758K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 882,
    "preview": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [u"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 718,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 604,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is"
  },
  {
    "path": ".github/workflows/dotnet.yml",
    "chars": 1584,
    "preview": "name: Build OpenNetMeter MSI and Push to Single Draft Release\n\non:\n  workflow_dispatch:\n\npermissions:\n  contents: write\n"
  },
  {
    "path": ".gitignore",
    "chars": 6263,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 380,
    "preview": "{\n    // Use IntelliSense to learn about possible attributes.\n    // Hover to view descriptions of existing attributes.\n"
  },
  {
    "path": "Directory.Build.props",
    "chars": 610,
    "preview": "<Project>\n  <PropertyGroup>\n\t  <ProductVersion>0.15.0</ProductVersion>\n\t  <ProductName>OpenNetMeter</ProductName>\n\t  <Ma"
  },
  {
    "path": "Installer/OpenNetMeter-Installer.wixproj",
    "chars": 800,
    "preview": "<Project Sdk=\"WixToolset.Sdk/5.0.1\">\r\n  <ItemGroup>\r\n    <PackageReference Include=\"WixToolset.Netfx.wixext\" Version=\"5"
  },
  {
    "path": "Installer/Product.wxs",
    "chars": 4078,
    "preview": "<!-- Import global variables -->\n\n<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\"\n\t xmlns:netfx=\"http://wixtoolset.org"
  },
  {
    "path": "Installer/Strings_en-us.wxl",
    "chars": 901,
    "preview": "<WixLocalization Culture=\"en-us\" xmlns=\"http://wixtoolset.org/schemas/v4/wxl\">\r\n  <String Id=\"DowngradeError\" Value=\"A "
  },
  {
    "path": "Installer/WixUIFeatureTree.wxs",
    "chars": 4542,
    "preview": "<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal Licen"
  },
  {
    "path": "LICENSE",
    "chars": 11350,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "NOTICE",
    "chars": 48,
    "preview": "OpenNetMeter\nCopyright 2021-2026 Ashfaaq Riphque"
  },
  {
    "path": "OpenNetMeter/App.axaml",
    "chars": 242,
    "preview": "<Application xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n"
  },
  {
    "path": "OpenNetMeter/App.axaml.cs",
    "chars": 4450,
    "preview": "using System;\nusing Avalonia;\nusing Avalonia.Controls.ApplicationLifetimes;\nusing Avalonia.Markup.Xaml;\nusing OpenNetMet"
  },
  {
    "path": "OpenNetMeter/Compat/Models/ApplicationDB.cs",
    "chars": 15695,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing Microsoft.Data.Sqlite;\nusing OpenNetMeter.Utiliti"
  },
  {
    "path": "OpenNetMeter/Compat/Models/MyProcess_Small.cs",
    "chars": 405,
    "preview": "namespace OpenNetMeter.Models;\n\npublic class MyProcess_Small\n{\n    public string? Name { get; set; }\n    public long Cur"
  },
  {
    "path": "OpenNetMeter/Compat/Models/NetworkProcess.TestHooks.cs",
    "chars": 706,
    "preview": "using System.Net;\n\nnamespace OpenNetMeter.Models\n{\n    // Test-only helpers kept internal to avoid exposing implementati"
  },
  {
    "path": "OpenNetMeter/Compat/Models/NetworkProcess.cs",
    "chars": 32577,
    "preview": "using System;\nusing System.Net;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Linq;\nusing System."
  },
  {
    "path": "OpenNetMeter/Compat/Properties/AppSettings.cs",
    "chars": 899,
    "preview": "namespace OpenNetMeter.Properties;\n\npublic class AppSettings\n{\n    public bool DarkMode { get; set; }\n    public bool St"
  },
  {
    "path": "OpenNetMeter/Compat/Properties/Global.cs",
    "chars": 396,
    "preview": "using System;\nusing System.IO;\n\nnamespace OpenNetMeter.Properties;\n\ninternal class Global\n{\n    public const string AppN"
  },
  {
    "path": "OpenNetMeter/Compat/Properties/SettingsManager.cs",
    "chars": 6054,
    "preview": "using System;\nusing System.IO;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing OpenNetMeter.Utilities;\n\nname"
  },
  {
    "path": "OpenNetMeter/Compat/Utilities/ByteArray.cs",
    "chars": 338,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nna"
  },
  {
    "path": "OpenNetMeter/Compat/Utilities/EventLogger.cs",
    "chars": 3822,
    "preview": "using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.Ver"
  },
  {
    "path": "OpenNetMeter/Compat/Utilities/PeriodicWork.cs",
    "chars": 2986,
    "preview": "using System;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace OpenNetMeter.Ut"
  },
  {
    "path": "OpenNetMeter/OpenNetMeter.csproj",
    "chars": 1506,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net8.0</Ta"
  },
  {
    "path": "OpenNetMeter/Program.cs",
    "chars": 392,
    "preview": "using System;\nusing Avalonia;\n\nnamespace OpenNetMeter;\n\ninternal sealed class Program\n{\n    [STAThread]\n    public stat"
  },
  {
    "path": "OpenNetMeter/Services/AvaloniaThemeService.cs",
    "chars": 467,
    "preview": "using Avalonia;\nusing Avalonia.Styling;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class AvaloniaThemeService : IT"
  },
  {
    "path": "OpenNetMeter/Services/AvaloniaWindowService.cs",
    "chars": 779,
    "preview": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Controls.ApplicationLifetimes;\nusing OpenNetMeter.PlatformAbstra"
  },
  {
    "path": "OpenNetMeter/Services/ExternalLinkService.cs",
    "chars": 629,
    "preview": "using System;\nusing System.Diagnostics;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMeter.Utilities;\n\nnamespac"
  },
  {
    "path": "OpenNetMeter/Services/IMiniWidgetService.cs",
    "chars": 363,
    "preview": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Services;\n\npublic interface IMiniWidgetService : System.IDisposable\n{\n "
  },
  {
    "path": "OpenNetMeter/Services/IThemeService.cs",
    "chars": 107,
    "preview": "namespace OpenNetMeter.Services;\n\npublic interface IThemeService\n{\n    void ApplyDarkMode(bool enabled);\n}\n"
  },
  {
    "path": "OpenNetMeter/Services/ITrayNotificationService.cs",
    "chars": 187,
    "preview": "using System;\nusing Avalonia.Controls;\n\nnamespace OpenNetMeter.Services;\n\npublic interface ITrayNotificationService : ID"
  },
  {
    "path": "OpenNetMeter/Services/ITrayService.cs",
    "chars": 90,
    "preview": "namespace OpenNetMeter.Services;\n\npublic interface ITrayService : System.IDisposable\n{\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/NoOpThemeService.cs",
    "chars": 147,
    "preview": "namespace OpenNetMeter.Services;\n\npublic sealed class NoOpThemeService : IThemeService\n{\n    public void ApplyDarkMode(b"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderMiniWidgetService.cs",
    "chars": 562,
    "preview": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderMiniWidgetService : IMiniWidg"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderNetworkCaptureService.cs",
    "chars": 721,
    "preview": "using System;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class Placeholde"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderProcessIconService.cs",
    "chars": 216,
    "preview": "using OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderProcessIconSe"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderStartupRegistrationService.cs",
    "chars": 282,
    "preview": "using OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderStartupRegist"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderTrayNotificationService.cs",
    "chars": 256,
    "preview": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderTrayNotificationService : ITr"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderTrayService.cs",
    "chars": 135,
    "preview": "namespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderTrayService : ITrayService\n{\n    public void Dispose()\n"
  },
  {
    "path": "OpenNetMeter/Services/UpdateChecker.cs",
    "chars": 1976,
    "preview": "using System;\nusing System.Net.Http;\nusing System.Text.Json;\nusing System.Threading.Tasks;\n\nnamespace OpenNetMeter.Servi"
  },
  {
    "path": "OpenNetMeter/Services/WindowsMiniWidgetService.cs",
    "chars": 6656,
    "preview": "using System;\nusing Avalonia;\nusing Avalonia.Controls;\nusing OpenNetMeter.ViewModels;\nusing OpenNetMeter.Views;\nusing Op"
  },
  {
    "path": "OpenNetMeter/Services/WindowsNetworkCaptureService.cs",
    "chars": 4817,
    "preview": "using System;\nusing System.ComponentModel;\nusing System.Runtime.Versioning;\nusing OpenNetMeter.Models;\nusing OpenNetMete"
  },
  {
    "path": "OpenNetMeter/Services/WindowsProcessIconService.cs",
    "chars": 3100,
    "preview": "using System;\nusing System.Collections.Concurrent;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.Drawing."
  },
  {
    "path": "OpenNetMeter/Services/WindowsStartupRegistrationService.cs",
    "chars": 4525,
    "preview": "using System;\nusing System.IO;\nusing System.Runtime.Versioning;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMe"
  },
  {
    "path": "OpenNetMeter/Services/WindowsTrayNotificationService.cs",
    "chars": 4403,
    "preview": "using System;\nusing System.Drawing;\nusing System.Runtime.InteropServices;\nusing System.Runtime.Versioning;\nusing System."
  },
  {
    "path": "OpenNetMeter/Services/WindowsTrayService.cs",
    "chars": 2041,
    "preview": "using System;\nusing Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Controls.ApplicationLifetimes;\nusing Avalonia.Plat"
  },
  {
    "path": "OpenNetMeter/Services/WindowsWidgetZOrderHelper.cs",
    "chars": 3778,
    "preview": "using System;\nusing System.Runtime.InteropServices;\nusing Avalonia.Controls;\nusing Avalonia.Threading;\nusing OpenNetMete"
  },
  {
    "path": "OpenNetMeter/ViewModels/ByteSizeFormatter.cs",
    "chars": 440,
    "preview": "namespace OpenNetMeter.ViewModels;\n\ninternal static class ByteSizeFormatter\n{\n    public static string FormatBytes(long "
  },
  {
    "path": "OpenNetMeter/ViewModels/HistoryViewModel.cs",
    "chars": 9583,
    "preview": "using System;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\nusi"
  },
  {
    "path": "OpenNetMeter/ViewModels/MainWindowViewModel.cs",
    "chars": 8157,
    "preview": "using System;\nusing System.Reflection;\nusing System.Windows.Input;\nusing OpenNetMeter.Services;\nusing OpenNetMeter.Core."
  },
  {
    "path": "OpenNetMeter/ViewModels/MiniWidgetViewModel.cs",
    "chars": 5090,
    "preview": "using System;\nusing System.ComponentModel;\nusing System.Windows.Input;\nusing Avalonia.Media;\nusing Avalonia.Media.Imagin"
  },
  {
    "path": "OpenNetMeter/ViewModels/RelayCommand.cs",
    "chars": 1405,
    "preview": "using System;\nusing System.Windows.Input;\n\nnamespace OpenNetMeter.ViewModels;\n\npublic sealed class RelayCommand : IComma"
  },
  {
    "path": "OpenNetMeter/ViewModels/SettingsViewModel.cs",
    "chars": 12598,
    "preview": "using System;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Reflection;\nusing System.Threading.Tas"
  },
  {
    "path": "OpenNetMeter/ViewModels/SummaryViewModel.cs",
    "chars": 25389,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/History.axaml",
    "chars": 4671,
    "preview": "<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n "
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/History.axaml.cs",
    "chars": 191,
    "preview": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Views.MainWindowTabs;\n\npublic partial class HistoryView : UserControl\n{"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/Settings.axaml",
    "chars": 18233,
    "preview": "<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n "
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/Settings.axaml.cs",
    "chars": 193,
    "preview": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Views.MainWindowTabs;\n\npublic partial class SettingsView : UserControl\n"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/Summary.axaml",
    "chars": 13413,
    "preview": "<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n "
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/Summary.axaml.cs",
    "chars": 191,
    "preview": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Views.MainWindowTabs;\n\npublic partial class SummaryView : UserControl\n{"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow.axaml",
    "chars": 18748,
    "preview": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xml"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow.axaml.cs",
    "chars": 7569,
    "preview": "using System;\nusing Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Input;\nusing Avalonia.Controls.Primitives;\nusing A"
  },
  {
    "path": "OpenNetMeter/Views/MiniWidgetWindow.axaml",
    "chars": 6979,
    "preview": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xml"
  },
  {
    "path": "OpenNetMeter/Views/MiniWidgetWindow.axaml.cs",
    "chars": 852,
    "preview": "using Avalonia.Controls;\nusing Avalonia.Input;\nusing Avalonia.Markup.Xaml;\nusing OpenNetMeter.ViewModels;\nusing OpenNetM"
  },
  {
    "path": "OpenNetMeter/Views/Themes.axaml",
    "chars": 5780,
    "preview": "<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winf"
  },
  {
    "path": "OpenNetMeter/app.manifest",
    "chars": 429,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <asse"
  },
  {
    "path": "OpenNetMeter.Core/OpenNetMeter.Core.csproj",
    "chars": 304,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable<"
  },
  {
    "path": "OpenNetMeter.Core/ViewModels/ConfirmationDialogVM.cs",
    "chars": 893,
    "preview": "using System.ComponentModel;\nusing System.Windows.Input;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMet"
  },
  {
    "path": "OpenNetMeter.Core/ViewModels/MainShellTabsViewModel.cs",
    "chars": 660,
    "preview": "using System.ComponentModel;\n\nnamespace OpenNetMeter.Core.ViewModels;\n\npublic class MainShellTabsViewModel : INotifyProp"
  },
  {
    "path": "OpenNetMeter.Old/DatabaseEngine/Connection.cs",
    "chars": 558,
    "preview": "using System.Data.SQLite;\nusing System.IO;\n\nnamespace DatabaseEngine\n{\n    internal class Connection\n    {\n        publ"
  },
  {
    "path": "OpenNetMeter.Old/DatabaseEngine/Database.cs",
    "chars": 7693,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Data.SQLite;\nusing System.Diagnostics;\n\nnamespace DatabaseE"
  },
  {
    "path": "OpenNetMeter.Old/DatabaseEngine/DatabaseEngine.csproj",
    "chars": 303,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0-windows</TargetFramework>\n    <Nullable"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/App.xaml",
    "chars": 809,
    "preview": "<Application x:Class=\"OpenNetMeter.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/App.xaml.cs",
    "chars": 1246,
    "preview": "using OpenNetMeter.Views;\nusing System.Windows;\nusing OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing Syst"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/AssemblyInfo.cs",
    "chars": 595,
    "preview": "using System.Windows;\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictio"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/ApplicationDB.cs",
    "chars": 18334,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing DatabaseEngine;\n\nnames"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/MyProcess.cs",
    "chars": 3283,
    "preview": "using OpenNetMeter.Utilities;\nusing System.ComponentModel;\nusing System.Windows.Media;\n\nnamespace OpenNetMeter.Models\n{"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/NativeMethods.cs",
    "chars": 1246,
    "preview": "using System;\nusing System.Drawing;\nusing System.Runtime.InteropServices;\n\nnamespace OpenNetMeter.Models\n{\n    public s"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/NetworkProcess.TestHooks.cs",
    "chars": 706,
    "preview": "using System.Net;\n\nnamespace OpenNetMeter.Models\n{\n    // Test-only helpers kept internal to avoid exposing implementati"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/NetworkProcess.cs",
    "chars": 32259,
    "preview": "using System;\nusing System.Net;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Linq;\nusing System."
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/SpeedGraph.cs",
    "chars": 14019,
    "preview": "using OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing System;\nusing System.Collections.Generic;\nusing Syst"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/OpenNetMeter.Old.csproj",
    "chars": 2539,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n\t<TargetFramework>net8.0-windo"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/AppSettings.cs",
    "chars": 3262,
    "preview": "using System.ComponentModel;\nusing System.Diagnostics;\nusing System.Drawing;\n\nnamespace OpenNetMeter.Properties\n{\n    p"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/Global.cs",
    "chars": 470,
    "preview": "using System;\nusing System.IO;\nusing System.Reflection;\n\nnamespace OpenNetMeter.Properties\n{\n    internal class Global\n "
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/InternalsVisibleTo.cs",
    "chars": 93,
    "preview": "using System.Runtime.CompilerServices;\n\n[assembly: InternalsVisibleTo(\"OpenNetMeter.Tests\")]\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/Resources.Designer.cs",
    "chars": 3870,
    "preview": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code w"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/Resources.resx",
    "chars": 6559,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The prim"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/SettingsManager.cs",
    "chars": 1618,
    "preview": "using System;\nusing System.Diagnostics;\nusing System.IO;\nusing Newtonsoft.Json;\nusing OpenNetMeter.Utilities;\n\nnamespac"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/BaseCommand.cs",
    "chars": 737,
    "preview": "using System;\nusing System.Windows.Input;\n\nnamespace OpenNetMeter.Utilities\n{\n    internal class BaseCommand : ICommand"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/ByteArray.cs",
    "chars": 338,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nna"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/DataSizeSuffix.cs",
    "chars": 2752,
    "preview": "using System;\n\nnamespace OpenNetMeter.Utilities\n{\n    public enum SpeedMagnitude\n    {\n        Auto = 0,\n        Kilo ="
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/EventLogger.cs",
    "chars": 3549,
    "preview": "using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Runtime.CompilerServices;\n\nnamespace OpenNetMeter."
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/IconToImgSource.cs",
    "chars": 538,
    "preview": "using System.Drawing;\nusing System.Windows;\nusing System.Windows.Interop;\nusing System.Windows.Media;\nusing System.Wind"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/JsonHelper.cs",
    "chars": 1532,
    "preview": "using System.Net.Http;\nusing System.Threading.Tasks;\nusing Newtonsoft.Json; \n\nnamespace OpenNetMeter.Utilities\n{\n    pu"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/PeriodicWork.cs",
    "chars": 2842,
    "preview": "using System;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace OpenNetMeter.Ut"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/ProcessIconCache.cs",
    "chars": 2744,
    "preview": "using System;\nusing System.Collections.Concurrent;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.D"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/UIMeasure.cs",
    "chars": 575,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/UpdateChecker.cs",
    "chars": 1555,
    "preview": "using Newtonsoft.Json.Linq;\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Net.Http;\nusing Syst"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/WindowsNetworkCaptureService.cs",
    "chars": 2948,
    "preview": "using System;\nusing System.ComponentModel;\nusing OpenNetMeter.Models;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespac"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/WindowsProcessIconService.cs",
    "chars": 299,
    "preview": "using OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Utilities\n{\n    public sealed class WindowsProcessIconS"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/WindowsStartupRegistrationService.cs",
    "chars": 3419,
    "preview": "using System;\nusing System.IO;\nusing OpenNetMeter.PlatformAbstractions;\nusing TaskScheduler = Microsoft.Win32.TaskSchedu"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/WpfUiDispatcher.cs",
    "chars": 562,
    "preview": "using System;\nusing System.Windows.Threading;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Utilities"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/ViewModels/DataUsageHistoryVM.cs",
    "chars": 6661,
    "preview": "using OpenNetMeter.Models;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMeter.Utilities;\nusing System;\nusing S"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/ViewModels/DataUsageSummaryVM.cs",
    "chars": 3978,
    "preview": "using System;\nusing OpenNetMeter.Models;\nusing System.ComponentModel;\nusing System.Collections.Concurrent;\n\nnamespace O"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/ViewModels/MainWindowVM.cs",
    "chars": 17972,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Win"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/ViewModels/MiniWidgetVM.cs",
    "chars": 3835,
    "preview": "using OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing System.ComponentModel;\nusing System.Windows;\nusing Sy"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/ViewModels/SettingsVM.cs",
    "chars": 13605,
    "preview": "using OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing System;\nusing System.ComponentModel;\nusing System.Dia"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/AboutWindow.xaml",
    "chars": 5227,
    "preview": "<Window x:Class=\"OpenNetMeter.Views.AboutWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentati"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/AboutWindow.xaml.cs",
    "chars": 2108,
    "preview": "using System.Diagnostics;\nusing System.Reflection;\nusing System.Windows;\nusing System.Windows.Input;\nusing System.Windo"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ConfirmationDialog.xaml",
    "chars": 5006,
    "preview": "<Window x:Class=\"OpenNetMeter.Views.ConfirmationDialog\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/pre"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ConfirmationDialog.xaml.cs",
    "chars": 1662,
    "preview": "using OpenNetMeter.ViewModels;\nusing System.Windows;\nusing System.Windows.Input;\n\nnamespace OpenNetMeter.Views\n{\n    //"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/Converters/BitmapToImageConverter.cs",
    "chars": 874,
    "preview": "using System;\nusing System.Globalization;\nusing System.IO;\nusing System.Windows.Data;\nusing System.Windows.Media.Imagin"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/Converters/NetSpeedFormatConverter.cs",
    "chars": 813,
    "preview": "using OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing System;\nusing System.Globalization;\nusing System.Win"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/Converters/RadioBoolToIntConverter.cs",
    "chars": 751,
    "preview": "using System;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace OpenNetMeter.Views\n{\n    public class R"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/Converters/UiVisibilityToWpfVisibilityConverter.cs",
    "chars": 1155,
    "preview": "using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\nusing OpenNetMeter.PlatformAb"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/Converters/UnitConverterBytes.cs",
    "chars": 563,
    "preview": "using OpenNetMeter.Utilities;\nusing System;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace OpenNetMe"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/CustomSystemTray.cs",
    "chars": 2162,
    "preview": "using System.Drawing;\nusing System.Windows.Forms;\nusing OpenNetMeter.Properties;\n\nnamespace OpenNetMeter.Views\n{\n    pu"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindow.xaml",
    "chars": 9645,
    "preview": "<Window x:Class=\"OpenNetMeter.Views.MainWindow\"\n        x:Name=\"MyWindow\"\n        xmlns=\"http://schemas.microsoft.com/w"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindow.xaml.cs",
    "chars": 12970,
    "preview": "using System.Windows;\nusing OpenNetMeter.ViewModels;\nusing System.Windows.Input;\nusing Forms = System.Windows.Forms;\nus"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/DataUsageHistoryV.xaml",
    "chars": 5817,
    "preview": "<UserControl x:Class=\"OpenNetMeter.Views.DataUsageHistoryV\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/DataUsageHistoryV.xaml.cs",
    "chars": 666,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusi"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/DataUsageSummaryV.xaml",
    "chars": 21332,
    "preview": "<UserControl x:Class=\"OpenNetMeter.Views.DataUsageSummaryV\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/DataUsageSummaryV.xaml.cs",
    "chars": 7220,
    "preview": "using System.Diagnostics;\nusing System.Windows.Media;\nusing System.Threading.Tasks;\nusing System.Windows.Controls;\nusin"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/SettingsV.xaml",
    "chars": 25542,
    "preview": "<UserControl x:Class=\"OpenNetMeter.Views.SettingsV\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/pr"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/SettingsV.xaml.cs",
    "chars": 291,
    "preview": "using System.Windows.Controls;\n\nnamespace OpenNetMeter.Views\n{\n    /// <summary>\n    /// Interaction logic for Settings"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MiniWidgetV.xaml",
    "chars": 11021,
    "preview": "<Window x:Class=\"OpenNetMeter.Views.MiniWidgetV\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentati"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MiniWidgetV.xaml.cs",
    "chars": 5160,
    "preview": "using System;\nusing System.Diagnostics;\nusing System.Windows;\nusing System.Windows.Interop;\nusing System.Windows.Thread"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/CustomDatePicker.xaml",
    "chars": 34244,
    "preview": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http:"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.Colors.xaml",
    "chars": 12712,
    "preview": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http:"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.ComboBox.xaml",
    "chars": 8209,
    "preview": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http:"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.ContextMenu.xaml",
    "chars": 2090,
    "preview": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http:"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.DataGrid.xaml",
    "chars": 21431,
    "preview": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http:"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.MainWindowTabs.xaml",
    "chars": 11249,
    "preview": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http:"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.Styles.xaml",
    "chars": 22735,
    "preview": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http:"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.SummaryPage.xaml",
    "chars": 4663,
    "preview": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http:"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/ThemeResources.xaml",
    "chars": 757,
    "preview": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http:"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/app.manifest",
    "chars": 3330,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <ass"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/IExternalLinkService.cs",
    "chars": 115,
    "preview": "namespace OpenNetMeter.PlatformAbstractions;\n\npublic interface IExternalLinkService\n{\n    void Open(string uri);\n}\n"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/INetworkCaptureService.cs",
    "chars": 951,
    "preview": "using System;\n\nnamespace OpenNetMeter.PlatformAbstractions;\n\npublic interface INetworkCaptureService : IDisposable\n{\n  "
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/IProcessIconService.cs",
    "chars": 136,
    "preview": "namespace OpenNetMeter.PlatformAbstractions;\n\npublic interface IProcessIconService\n{\n    object? GetProcessIcon(string "
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/IStartupRegistrationService.cs",
    "chars": 174,
    "preview": "namespace OpenNetMeter.PlatformAbstractions;\n\npublic interface IStartupRegistrationService\n{\n    bool IsEnabled();\n    "
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/IUiDispatcher.cs",
    "chars": 151,
    "preview": "using System;\n\nnamespace OpenNetMeter.PlatformAbstractions;\n\npublic interface IUiDispatcher\n{\n    bool CheckAccess();\n "
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/IWindowService.cs",
    "chars": 163,
    "preview": "namespace OpenNetMeter.PlatformAbstractions;\n\npublic interface IWindowService\n{\n    void MinimizeMainWindow();\n    void "
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/OpenNetMeter.PlatformAbstractions.csproj",
    "chars": 161,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable<"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/UiVisibility.cs",
    "chars": 116,
    "preview": "namespace OpenNetMeter.PlatformAbstractions;\n\npublic enum UiVisibility\n{\n    Hidden,\n    Visible,\n    Collapsed,\n}\n"
  },
  {
    "path": "OpenNetMeter.Tests/NetworkProcessTests.cs",
    "chars": 3415,
    "preview": "using System;\nusing System.Net;\nusing OpenNetMeter.Models;\nusing OpenNetMeter.Properties;\n\nnamespace OpenNetMeter.Tests;"
  },
  {
    "path": "OpenNetMeter.Tests/OpenNetMeter.Tests.csproj",
    "chars": 783,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>\n"
  },
  {
    "path": "OpenNetMeter.sln",
    "chars": 11604,
    "preview": "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 17\r\nVisualStudioVersion = 17.2.3"
  },
  {
    "path": "README.md",
    "chars": 1597,
    "preview": "\n# OpenNetMeter\n\nA simple program to monitor your network/data usage. Made for the average windows user.\n\n## Description"
  },
  {
    "path": "Resources/documentation/README.md",
    "chars": 62,
    "preview": "This directory is meant for any documentation for OpenNetMeter"
  },
  {
    "path": "scripts/build-avalonia-msi.ps1",
    "chars": 2080,
    "preview": "param(\n    [ValidateSet(\"Release\", \"Debug\")]\n    [string]$Configuration = \"Release\",\n    [ValidateSet(\"win-x64\")]\n    [s"
  },
  {
    "path": "scripts/publish-avalonia-rc.ps1",
    "chars": 813,
    "preview": "param(\n    [ValidateSet(\"win-x64\", \"win-arm64\", \"win-x86\", \"linux-x64\")]\n    [string]$Runtime = \"win-x64\",\n    [Validate"
  }
]

About this extraction

This page contains the full source code of the Ashfaaq18/OpenNetMeter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 154 files (698.1 KB), approximately 153.2k tokens, and a symbol index with 667 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!