[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: ashfaaq18\nthanks_dev: # Replace with a single thanks.dev username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**App and Windows Version (please complete the following information):**\n - Windows Specs: [Select Start > Settings > System > About. Open About settings.]\n - OpenNetMeter Version: \n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/dotnet.yml",
    "content": "name: Build OpenNetMeter MSI and Push to Single Draft Release\n\non:\n  workflow_dispatch:\n\npermissions:\n  contents: write\n\njobs:\n  draft-msi:\n    runs-on: windows-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup .NET 8\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: \"8.0.x\"\n\n      - name: Build Avalonia MSI\n        shell: pwsh\n        run: |\n          powershell -ExecutionPolicy Bypass -File .\\scripts\\build-avalonia-msi.ps1 -Runtime win-x64 -Configuration Release\n\n      - name: Find built MSI\n        id: find_msi\n        shell: pwsh\n        run: |\n          $msi = Get-ChildItem `\n            \"$Env:GITHUB_WORKSPACE\\Installer\\bin\" `\n            -Filter 'OpenNetMeter-*.msi' `\n            -Recurse `\n            | Sort-Object LastWriteTimeUtc -Descending `\n            | Select-Object -First 1\n          if (-not $msi) { throw 'No MSI found after build.' }\n\n          \"msi_path=$($msi.FullName)\" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf8 -Append\n\n      - name: Create or Update Draft Release and Upload MSI\n        uses: ncipollo/release-action@v1\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          tag: opennetmeter-draft\n          name: OpenNetMeter (Draft Release)\n          draft: true\n          allowUpdates: true\n          artifacts: ${{ steps.find_msi.outputs.msi_path }}\n\n      - name: Echo MSI path\n        shell: pwsh\n        env:\n          MSI_PATH: ${{ steps.find_msi.outputs.msi_path }}\n        run: |\n          Write-Host \"Built MSI path was: $Env:MSI_PATH\"\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Ww][Ii][Nn]32/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Oo]ut/\n[Ll]og/\n[Ll]ogs/\n_rc/\n_verify_build/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# ASP.NET Scaffolding\nScaffoldingReadMe.txt\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Coverlet is a free, cross platform Code Coverage Tool\ncoverage*.json\ncoverage*.xml\ncoverage*.info\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n\n# Fody - auto-generated XML schema\nFodyWeavers.xsd\n\n#My stuff\nMyStuff/"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n    // Use IntelliSense to learn about possible attributes.\n    // Hover to view descriptions of existing attributes.\n    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \".NET_CoreAttach\",\n            \"type\": \"coreclr\",\n            \"request\": \"attach\"\n        }\n    ]\n}"
  },
  {
    "path": "Directory.Build.props",
    "content": "<Project>\n  <PropertyGroup>\n\t  <ProductVersion>0.15.0</ProductVersion>\n\t  <ProductName>OpenNetMeter</ProductName>\n\t  <Manufacturer>Ashfaaq18</Manufacturer>\n  </PropertyGroup>\n\n  <Target Name=\"ValidateProductVersionForMsi\" BeforeTargets=\"Build\">\n    <Error\n      Condition=\"$([System.Text.RegularExpressions.Regex]::IsMatch('$(ProductVersion)', '^[0-9]+\\.[0-9]+\\.[0-9]+$')) != 'True'\"\n      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.\" />\n  </Target>\n</Project>\n"
  },
  {
    "path": "Installer/OpenNetMeter-Installer.wixproj",
    "content": "﻿<Project Sdk=\"WixToolset.Sdk/5.0.1\">\r\n  <ItemGroup>\r\n    <PackageReference Include=\"WixToolset.Netfx.wixext\" Version=\"5.0.1\" />\r\n    <PackageReference Include=\"WixToolset.UI.wixext\" Version=\"5.0.1\" />\r\n    <PackageReference Include=\"WixToolset.Util.wixext\" Version=\"5.0.1\" />\r\n  </ItemGroup>\r\n  <PropertyGroup>\n    <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>\n    <RepoRoot>$(MSBuildProjectDirectory)\\..\\</RepoRoot>\n    <!-- Optional: fallback -->\n\t  <DefineConstants>\n\t\t  $(DefineConstants);\n\t\t  RepoRoot=$(RepoRoot);\n\t\t  TargetFramework=$(TargetFramework);\n\t\t  Manufacturer=$(Manufacturer);\n\t\t  ProductName=$(ProductName);\n\t\t  ProductVersion=$(ProductVersion);\r\n\t  </DefineConstants>\r\n\t  <OutputName>$(ProductName)-$(ProductVersion)</OutputName>\r\n  </PropertyGroup>  \r\n</Project>\n"
  },
  {
    "path": "Installer/Product.wxs",
    "content": "<!-- Import global variables -->\n\n<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\"\n\t xmlns:netfx=\"http://wixtoolset.org/schemas/v4/wxs/netfx\"\n\t xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">\n\t<Package Language=\"1033\"\n\t\t Name=\"$(ProductName)\" Version=\"$(ProductVersion)\" Manufacturer=\"$(Manufacturer)\"\n\t\t UpgradeCode=\"c8795db6-2426-4f40-a615-671f26fcaf3a\" InstallerVersion=\"200\">\n\n\t\t<MajorUpgrade AllowSameVersionUpgrades=\"yes\" DowngradeErrorMessage=\"!(loc.DowngradeError)\" />\n\t\t<Icon Id=\"AppIcon.ico\" SourceFile=\"$(RepoRoot)Resources\\AppIcon.ico\"/>\n\t\t<Property Id=\"ARPPRODUCTICON\" Value=\"AppIcon.ico\" />\n \n\t\t<Property Id=\"DESKTOP_SHORTCUT\" Value=\"1\"/>\n\n\t\t<Property Id=\"WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT\" Value=\"Launch $(ProductName)\" />\n    \t<Property Id=\"WIXUI_EXITDIALOGOPTIONALCHECKBOX\" Value=\"1\" />\n\t\t<Property Id=\"WixShellExecTarget\" Value=\"[InstallFolder]$(ProductName).exe\" />\n\n    \t<CustomAction Id=\"LaunchApplication\"\n              BinaryRef=\"Wix4UtilCA_$(sys.BUILDARCHSHORT)\"\n              DllEntry=\"WixShellExec\"\n              Impersonate=\"yes\" />\n\n\t\t<CustomAction Id=\"CloseRunningApp\"\n              Directory=\"System64Folder\"\n              Execute=\"deferred\"\n              Impersonate=\"no\"\n              Return=\"ignore\"\n              ExeCommand='cmd.exe /c \"taskkill /F /IM $(ProductName).exe\"' />\n\n\t\t<CustomAction Id=\"CleanupAppData\"\n              Directory=\"System64Folder\"\n              Execute=\"deferred\"\n              Impersonate=\"yes\"\n              Return=\"ignore\"\n              ExeCommand='cmd.exe /c \"rmdir /S /Q \"%LOCALAPPDATA%\\$(ProductName)\"\"' />\n\n\t\t<CustomAction Id=\"DeleteOpenNetMeterTask\"\n\t\t\t\t\t  Directory=\"System64Folder\"\n\t\t\t\t\t  Execute=\"deferred\"\n\t\t\t\t\t  Impersonate=\"no\"\n\t\t\t\t\t  Return=\"ignore\"\n\t\t\t\t\t  ExeCommand='cmd.exe /c \"schtasks /Delete /TN \"\\OpenNetMeter\\OpenNetMeterLogon\" /F &amp; schtasks /Delete /TN \"\\OpenNetMeter\" /F\"' />\n\n\t\t<InstallExecuteSequence>\n\t\t\t<Custom Action=\"CloseRunningApp\"\n\t\t\t\t\tAfter=\"InstallInitialize\"\n\t\t\t\t\tCondition=\"WIX_UPGRADE_DETECTED OR REMOVE=&quot;ALL&quot;\" />\n\t\t\t<Custom Action=\"DeleteOpenNetMeterTask\"\n\t\t\t\t\tBefore=\"RemoveFiles\"\n\t\t\t\t\tCondition=\"REMOVE=&quot;ALL&quot; AND NOT UPGRADINGPRODUCTCODE\" />\n\t\t\t<Custom Action=\"CleanupAppData\"\n\t\t\t\t\tBefore=\"InstallFinalize\"\n\t\t\t\t\tCondition=\"REMOVE=&quot;ALL&quot; AND NOT UPGRADINGPRODUCTCODE\" />\n\t\t</InstallExecuteSequence>\n\n\t\t<MediaTemplate EmbedCab=\"yes\"/>\n\n\t\t<netfx:DotNetCompatibilityCheck Id=\"netCoreStatus64\"\n\t\tProperty=\"NETCORESTATUS\" RollForward=\"latestMajor\"\n\t\tRuntimeType=\"core\" Version=\"8.0.1\" Platform=\"x64\" />\n\t\t\t<Launch Condition=\"Installed OR NETCORESTATUS=&quot;0&quot;\"\n\t\t\t\tMessage=\"[ProductName] requires Microsoft .NET 8.0 Runtime.\" />\n\t\t\n\t\t<!-- UI stuff-->\n\t\t<UIRef Id=\"WixUIFeatureTree\" />\n\n\t\t<!-- components to install -->\n\t\t<Feature Id=\"ProductFeature\" Level=\"1\">\n\t\t\t<ComponentGroupRef Id=\"ProductComponents\" />\n\t\t\t<ComponentGroupRef Id=\"ShortcutComponents\" />\n\t\t</Feature>\n\t\t\n\t</Package>\n\t\n\t<Fragment>\n\t\t<StandardDirectory Id=\"ProgramFiles6432Folder\">\n\t\t\t<Directory Id=\"InstallFolder\" Name=\"$(ProductName)\"/>\n\t\t</StandardDirectory>\n\t\t\n\t\t<ComponentGroup Id=\"ProductComponents\" Directory=\"InstallFolder\">\n\t\t\t<!-- Files to install -->\n\t\t\t<Files Include=\"$(RepoRoot)_rc\\avalonia\\win-x64\\OpenNetMeter.exe\" />\n\t\t</ComponentGroup>\n\t</Fragment>\n\n\t<Fragment>\n\t\t<!-- Declare the standard desktop folder so MSI knows where to place the shortcut -->\n\t\t<StandardDirectory Id=\"DesktopFolder\" />\n\n\t\t<ComponentGroup Id=\"ShortcutComponents\" Directory=\"DesktopFolder\">\n\t\t\t<Component Id=\"CmpDesktopShortcut\" Guid=\"2dd319fe-df0c-4f5f-95d6-00a83eb09cb4\" Condition=\"DESKTOP_SHORTCUT=&quot;1&quot;\">\n\t\t\t\t<Shortcut Id=\"AppDesktopShortcut\"\n\t\t\t\t\t\t  Name=\"$(ProductName)\"\n\t\t\t\t\t\t  Target=\"[InstallFolder]$(ProductName).exe\"\n\t\t\t\t\t\t  WorkingDirectory=\"InstallFolder\" />\n\n\t\t\t\t<!-- Key path so this component is well-formed -->\n\t\t\t\t<RegistryValue Root=\"HKCU\"\n\t\t\t\t\t\t\t   Key=\"Software\\$(Manufacturer)\\$(ProductName)\"\n\t\t\t\t\t\t\t   Name=\"DesktopShortcutInstalled\"\n\t\t\t\t\t\t\t   Type=\"integer\" Value=\"1\" KeyPath=\"yes\" />\n\t\t\t</Component>\n\t\t</ComponentGroup>\n\t</Fragment>\n\t\n</Wix>\n"
  },
  {
    "path": "Installer/Strings_en-us.wxl",
    "content": "﻿<WixLocalization Culture=\"en-us\" xmlns=\"http://wixtoolset.org/schemas/v4/wxl\">\r\n  <String Id=\"DowngradeError\" Value=\"A newer version of [ProductName] is already installed.\" />\n  <String Id=\"UpgradeDetectedDlg_Title\" Value=\"[ProductName] Setup\" />\n  <String Id=\"UpgradeDetectedDlgTitle\" Value=\"Upgrade existing installation\" />\n  <String Id=\"UpgradeDetectedDlgDescription\" Value=\"A previous version of [ProductName] is already installed. Setup will upgrade it to this version and keep your existing settings.\" />\n\n  <String Id=\"PrerequisitesDlg_Title\" Value=\"[ProductName] Setup\" />\n  <String Id=\"PrerequisitesDlgBitmap\" Value=\"WixUI_Bmp_Banner\" />\r\n  <String Id=\"PrerequisitesDlgTitle\" Value=\"Prerequisites of [ProductName]\" />\r\n  <String Id=\"PrerequisitesDlgDescription\" Value=\"List and state of prerequisites. Install them manually or use the bootstrapper of [ProductName]\" />\r\n</WixLocalization>\r\n"
  },
  {
    "path": "Installer/WixUIFeatureTree.wxs",
    "content": "﻿<!-- 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. -->\n\n<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\"\n     xmlns:ui=\"http://wixtoolset.org/schemas/v4/wxs/ui\">\n\t<Fragment>\n\t\t<UI Id=\"WixUIFeatureTree\">\n\t\t\t<TextStyle Id=\"WixUI_Font_Normal\" FaceName=\"Tahoma\" Size=\"8\" />\n\t\t\t<TextStyle Id=\"WixUI_Font_Bigger\" FaceName=\"Tahoma\" Size=\"12\" />\n\t\t\t<TextStyle Id=\"WixUI_Font_Title\" FaceName=\"Tahoma\" Size=\"9\" Bold=\"yes\" />\n\n\t\t\t<Property Id=\"DefaultUIFont\" Value=\"WixUI_Font_Normal\" />\n\n\t\t\t<DialogRef Id=\"ErrorDlg\" />\n\t\t\t<DialogRef Id=\"FatalError\" />\n\t\t\t<DialogRef Id=\"FilesInUse\" />\n\t\t\t<DialogRef Id=\"MsiRMFilesInUse\" />\n\t\t\t<DialogRef Id=\"PrepareDlg\" />\n\t\t\t<DialogRef Id=\"ProgressDlg\" />\n\t\t\t<DialogRef Id=\"ResumeDlg\" />\n\t\t\t<DialogRef Id=\"UserExit\" />\n\n\t\t\t<Publish Dialog=\"ExitDialog\" Control=\"Finish\" Event=\"DoAction\" Value=\"LaunchApplication\" Condition=\"WIXUI_EXITDIALOGOPTIONALCHECKBOX=&quot;1&quot; and NOT Installed\" Order=\"998\"/>\n\t\t\t<Publish Dialog=\"ExitDialog\" Control=\"Finish\" Event=\"EndDialog\" Value=\"Return\" Order=\"999\" />\n\n\t\t\t<Publish Dialog=\"WelcomeDlg\" Control=\"Next\" Event=\"NewDialog\" Value=\"UpgradeDetectedDlg\" Condition=\"WIX_UPGRADE_DETECTED AND NOT Installed\" Order=\"1\" />\n\t\t\t<Publish Dialog=\"WelcomeDlg\" Control=\"Next\" Event=\"NewDialog\" Value=\"OptionsDlg\" Condition=\"NOT (WIX_UPGRADE_DETECTED AND NOT Installed)\" Order=\"2\" />\n\t\t\t\n\t\t\t<Publish Dialog=\"VerifyReadyDlg\" Control=\"Back\" Event=\"NewDialog\" Value=\"MaintenanceTypeDlg\" Condition=\"Installed\" Order=\"1\"/>\n\t\t\t<Publish Dialog=\"VerifyReadyDlg\" Control=\"Back\" Event=\"NewDialog\" Value=\"UpgradeDetectedDlg\" Condition=\"WIX_UPGRADE_DETECTED AND NOT Installed\" Order=\"2\"/>\n\t\t\t<Publish Dialog=\"VerifyReadyDlg\" Control=\"Back\" Event=\"NewDialog\" Value=\"OptionsDlg\" Condition=\"NOT Installed AND NOT WIX_UPGRADE_DETECTED\" Order=\"3\"/>\n\n\t\t\t<Publish Dialog=\"MaintenanceWelcomeDlg\" Control=\"Next\" Event=\"NewDialog\" Value=\"MaintenanceTypeDlg\" />\n\n\t\t\t<Publish Dialog=\"MaintenanceTypeDlg\" Control=\"RepairButton\" Event=\"NewDialog\" Value=\"VerifyReadyDlg\" />\n\t\t\t<Publish Dialog=\"MaintenanceTypeDlg\" Control=\"RemoveButton\" Event=\"NewDialog\" Value=\"VerifyReadyDlg\" />\n\t\t\t<Publish Dialog=\"MaintenanceTypeDlg\" Control=\"Back\" Event=\"NewDialog\" Value=\"MaintenanceWelcomeDlg\" />\n\n\t\t\t<Dialog Id=\"UpgradeDetectedDlg\" Width=\"370\" Height=\"270\" Title=\"!(loc.UpgradeDetectedDlg_Title)\">\n\t\t\t\t<Control Id=\"Title\" Type=\"Text\" X=\"15\" Y=\"6\" Width=\"340\" Height=\"15\"\n\t\t\t\t\t\t\tTransparent=\"yes\" NoPrefix=\"yes\" Text=\"{\\WixUI_Font_Title}!(loc.UpgradeDetectedDlgTitle)\" />\n\t\t\t\t<Control Id=\"Line\" Type=\"Line\" X=\"0\" Y=\"23\" Width=\"370\" Height=\"0\" />\n\t\t\t\t<Control Id=\"Description\" Type=\"Text\" X=\"20\" Y=\"55\" Width=\"330\" Height=\"60\"\n\t\t\t\t\t\t\tTransparent=\"yes\" NoPrefix=\"yes\" Text=\"!(loc.UpgradeDetectedDlgDescription)\" />\n\n\t\t\t\t<Control Id=\"Back\" Type=\"PushButton\" X=\"180\" Y=\"243\" Width=\"56\" Height=\"17\" Text=\"Back\">\n\t\t\t\t\t<Publish Event=\"NewDialog\" Value=\"WelcomeDlg\" />\n\t\t\t\t</Control>\n\t\t\t\t<Control Id=\"Next\" Type=\"PushButton\" X=\"236\" Y=\"243\" Width=\"56\" Height=\"17\" Default=\"yes\" Text=\"Next\">\n\t\t\t\t\t<Publish Event=\"NewDialog\" Value=\"VerifyReadyDlg\" />\n\t\t\t\t</Control>\n\t\t\t\t<Control Id=\"Cancel\" Type=\"PushButton\" X=\"304\" Y=\"243\" Width=\"56\" Height=\"17\" Cancel=\"yes\" Text=\"Cancel\">\n\t\t\t\t\t<Publish Event=\"SpawnDialog\" Value=\"CancelDlg\" />\n\t\t\t\t</Control>\n\t\t\t</Dialog>\n\n\t\t\t<Dialog Id=\"OptionsDlg\" Width=\"370\" Height=\"270\" Title=\"[ProductName] Setup\">\n\t\t\t\t<Control Id=\"Title\" Type=\"Text\" X=\"15\" Y=\"6\" Width=\"340\" Height=\"15\"\n\t\t\t\t\t\t\tTransparent=\"yes\" NoPrefix=\"yes\" Text=\"{\\WixUI_Font_Title}Choose options\" />\n\t\t\t\t<Control Id=\"Line\" Type=\"Line\" X=\"0\" Y=\"23\" Width=\"370\" Height=\"0\" />\n\n\t\t\t\t<Control Id=\"DesktopShortcutCheck\" Type=\"CheckBox\"\n\t\t\t\t\t\t\tX=\"20\" Y=\"60\" Width=\"330\" Height=\"17\"\n\t\t\t\t\t\t\tProperty=\"DESKTOP_SHORTCUT\" CheckBoxValue=\"1\"\n\t\t\t\t\t\t\tText=\"Create a Desktop shortcut\" />\n\n\t\t\t\t<!-- Nav buttons -->\n\t\t\t\t<Control Id=\"Back\" Type=\"PushButton\" X=\"180\" Y=\"243\" Width=\"56\" Height=\"17\" Text=\"Back\">\n\t\t\t\t\t<Publish Event=\"NewDialog\" Value=\"WelcomeDlg\"/>\n\t\t\t\t</Control>\n\t\t\t\t<Control Id=\"Next\" Type=\"PushButton\" X=\"236\" Y=\"243\" Width=\"56\" Height=\"17\" Default=\"yes\" Text=\"Next\">\n\t\t\t\t\t<Publish Event=\"NewDialog\" Value=\"VerifyReadyDlg\"/>\n\t\t\t\t</Control>\n\t\t\t\t<Control Id=\"Cancel\" Type=\"PushButton\" X=\"304\" Y=\"243\" Width=\"56\" Height=\"17\" Cancel=\"yes\" Text=\"Cancel\">\n\t\t\t\t\t<Publish Event=\"SpawnDialog\" Value=\"CancelDlg\"/>\n\t\t\t\t</Control>\n\t\t\t</Dialog>\n\t\t\t\n\t\t</UI>\n\t\t\n\n\t\t<UIRef Id=\"WixUI_Common\" />\n\t</Fragment>\n</Wix>\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2021-2026 Ashfaaq Riphque\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "NOTICE",
    "content": "OpenNetMeter\nCopyright 2021-2026 Ashfaaq Riphque"
  },
  {
    "path": "OpenNetMeter/App.axaml",
    "content": "﻿<Application xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             x:Class=\"OpenNetMeter.App\">\n  <Application.Styles>\n    <FluentTheme/>\n  </Application.Styles>\n</Application>\n"
  },
  {
    "path": "OpenNetMeter/App.axaml.cs",
    "content": "using System;\nusing Avalonia;\nusing Avalonia.Controls.ApplicationLifetimes;\nusing Avalonia.Markup.Xaml;\nusing OpenNetMeter.Services;\nusing OpenNetMeter.ViewModels;\nusing OpenNetMeter.Views;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter;\n\npublic partial class App : Application\n{\n    private static bool unhandledHandlersRegistered;\n\n    public override void Initialize()\n    {\n        AvaloniaXamlLoader.Load(this);\n    }\n\n    public override void OnFrameworkInitializationCompleted()\n    {\n        RegisterUnhandledExceptionLoggingOnce();\n        EventLogger.Info(\"Application starting\");\n\n        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)\n        {\n            desktop.ShutdownMode = global::Avalonia.Controls.ShutdownMode.OnExplicitShutdown;\n            bool startMinimized = HasStartMinimizedArgument();\n\n            IMiniWidgetService miniWidgetService = new PlaceholderMiniWidgetService();\n            ITrayService trayService = new PlaceholderTrayService();\n            ITrayNotificationService trayNotificationService = new PlaceholderTrayNotificationService();\n\n            desktop.Exit += (_, _) =>\n            {\n                EventLogger.Info(\"Application exiting\");\n                trayService.Dispose();\n                trayNotificationService.Dispose();\n                miniWidgetService.Dispose();\n                SettingsManager.Save();\n            };\n\n            var windowService = new AvaloniaWindowService();\n            IExternalLinkService externalLinkService = new ExternalLinkService();\n            IThemeService themeService = new AvaloniaThemeService(this);\n            var miniWidgetViewModel = new MiniWidgetViewModel();\n            IStartupRegistrationService startupRegistrationService = OperatingSystem.IsWindows()\n                ? new WindowsStartupRegistrationService()\n                : new PlaceholderStartupRegistrationService();\n            INetworkCaptureService networkCaptureService = OperatingSystem.IsWindows()\n                ? new WindowsNetworkCaptureService()\n                : new PlaceholderNetworkCaptureService();\n            IProcessIconService processIconService = OperatingSystem.IsWindows()\n                ? new WindowsProcessIconService()\n                : new PlaceholderProcessIconService();\n            var mainWindow = new MainWindow();\n\n            miniWidgetService = OperatingSystem.IsWindows()\n                ? new WindowsMiniWidgetService(miniWidgetViewModel, mainWindow)\n                : new PlaceholderMiniWidgetService();\n\n            trayNotificationService = OperatingSystem.IsWindows()\n                ? new WindowsTrayNotificationService()\n                : new PlaceholderTrayNotificationService();\n\n            mainWindow.InitializeWindowState(miniWidgetService, trayNotificationService);\n            mainWindow.DataContext = new MainWindowViewModel(windowService, networkCaptureService, processIconService, externalLinkService, miniWidgetViewModel, miniWidgetService, startupRegistrationService, themeService);\n            desktop.MainWindow = mainWindow;\n\n            trayService = OperatingSystem.IsWindows()\n                ? new WindowsTrayService(this, desktop, mainWindow, miniWidgetService)\n                : new PlaceholderTrayService();\n\n            if (startMinimized)\n                mainWindow.Hide();\n\n            if (OperatingSystem.IsWindows() && SettingsManager.Current.MiniWidgetVisibility)\n                miniWidgetService.Show();\n        }\n\n        base.OnFrameworkInitializationCompleted();\n    }\n\n    private static void RegisterUnhandledExceptionLoggingOnce()\n    {\n        if (unhandledHandlersRegistered)\n            return;\n\n        unhandledHandlersRegistered = true;\n\n        AppDomain.CurrentDomain.UnhandledException += (_, e) =>\n        {\n            if (e.ExceptionObject is Exception ex)\n            {\n                EventLogger.Error(\"Unhandled exception\", ex);\n            }\n            else\n            {\n                EventLogger.Error($\"Unhandled exception object: {e.ExceptionObject}\");\n            }\n        };\n    }\n\n    private static bool HasStartMinimizedArgument()\n    {\n        foreach (string arg in Environment.GetCommandLineArgs())\n        {\n            if (string.Equals(arg, \"/StartMinimized\", StringComparison.OrdinalIgnoreCase))\n                return true;\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Compat/Models/ApplicationDB.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing Microsoft.Data.Sqlite;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.Models;\n\ninternal sealed class ApplicationDB : IDisposable\n{\n    private static readonly object DbLock = new();\n    private static SqliteConnection? sharedConnection;\n    private static int refCount;\n\n    private readonly string adapterName;\n    private bool disposed;\n\n    private static string LogTime() => DateTime.Now.ToString(\"HH:mm:ss.fff\");\n\n    public const string UnifiedDBFileName = \"OpenNetMeter\";\n    public const int DataStoragePeriodInDays = 60;\n\n    public ApplicationDB(string dBFileName)\n    {\n        adapterName = dBFileName;\n\n        lock (DbLock)\n        {\n            if (sharedConnection == null)\n            {\n                Directory.CreateDirectory(Properties.Global.GetFilePath());\n\n                var builder = new SqliteConnectionStringBuilder\n                {\n                    DataSource = GetUnifiedDBFullPath(),\n                    Mode = SqliteOpenMode.ReadWriteCreate,\n                    Cache = SqliteCacheMode.Shared\n                };\n\n                sharedConnection = new SqliteConnection(builder.ToString());\n                sharedConnection.Open();\n\n                RunNonQuery(sharedConnection, \"PRAGMA journal_mode=WAL;\");\n                RunNonQuery(sharedConnection, \"PRAGMA busy_timeout=5000;\");\n                RunNonQuery(sharedConnection, \"PRAGMA synchronous=NORMAL;\");\n                RunNonQuery(sharedConnection, \"PRAGMA foreign_keys=ON;\");\n            }\n\n            refCount++;\n        }\n\n        Console.WriteLine($\"[{LogTime()}] [SQLite][Avalonia] ApplicationDB created adapter='{dBFileName}'\");\n    }\n\n    public static string GetUnifiedDBFullPath()\n    {\n        return Path.Combine(Properties.Global.GetFilePath(), UnifiedDBFileName + \".sqlite\");\n    }\n\n    public static void CloseSharedConnection()\n    {\n        lock (DbLock)\n        {\n            sharedConnection?.Dispose();\n            sharedConnection = null;\n            refCount = 0;\n        }\n    }\n\n    public int CreateTable()\n    {\n        lock (DbLock)\n        {\n            Console.WriteLine($\"[{LogTime()}] [SQLite][Avalonia] CreateTable adapter='{adapterName}' db='{GetUnifiedDBFullPath()}'\");\n            return CreateAdapterTable() >> 31 |\n                   CreateProcessTable() >> 31 |\n                   CreateDateTable() >> 31 |\n                   CreateProcessDateTable() >> 31;\n        }\n    }\n\n    public int InsertUniqueRow_AdapterTable(string adapter)\n    {\n        lock (DbLock)\n        {\n            Console.WriteLine($\"[{LogTime()}] [SQLite][Avalonia] InsertUniqueRow_AdapterTable adapter='{adapter}'\");\n            return RunNonQuery(\n                \"INSERT OR IGNORE INTO Adapter(Name) VALUES(@Name)\",\n                (\"@Name\", adapter));\n        }\n    }\n\n    public void UpdateDatesInDB()\n    {\n        lock (DbLock)\n        {\n            Console.WriteLine($\"[{LogTime()}] [SQLite][Avalonia] UpdateDatesInDB adapter='{adapterName}' today='{DateTime.Today:yyyy-MM-dd}' retentionDays={DataStoragePeriodInDays}\");\n            InsertUniqueRow_DateTable(DateTime.Today);\n            RemoveOldDate();\n            RemoveOldProcess();\n        }\n    }\n\n    public void PushToDB(string processName, long totalDataRecv, long totalDataSend)\n    {\n        lock (DbLock)\n        {\n            Console.WriteLine($\"[{LogTime()}] [SQLite][Avalonia] PushToDB adapter='{adapterName}' process='{processName}' recv={totalDataRecv} sent={totalDataSend}\");\n\n            InsertUniqueRow_ProcessTable(processName);\n            InsertUniqueRow_AdapterTable(adapterName);\n\n            long dateID = GetID_DateTable(DateTime.Today);\n            long processID = GetID_ProcessTable(processName);\n            long adapterID = GetID_AdapterTable_Internal(adapterName);\n\n            if (InsertUniqueRow_ProcessDateTable(processID, dateID, adapterID, totalDataRecv, totalDataSend) < 1)\n            {\n                UpdateRow_ProcessDateTable(processID, dateID, adapterID, totalDataRecv, totalDataSend);\n            }\n        }\n    }\n\n    public List<List<object>> GetDataSum_ProcessDateTable(DateTime date1, DateTime date2)\n    {\n        lock (DbLock)\n        {\n            return GetMultipleCellData(\n                \"SELECT p1.Name, SUM(pd1.DataReceived), SUM(pd1.DataSent) \" +\n                \"FROM ProcessDate pd1 \" +\n                \"JOIN Process p1 ON p1.ID = pd1.ProcessID \" +\n                \"WHERE DateID IN \" +\n                \"(SELECT ID FROM Date WHERE \" +\n                \"(Year * 10000 + Month * 100 + Day) \" +\n                \"BETWEEN \" +\n                $\"({date1.Year * 10000 + date1.Month * 100 + date1.Day}) \" +\n                \"AND \" +\n                $\"({date2.Year * 10000 + date2.Month * 100 + date2.Day})) \" +\n                \"AND AdapterID = @AdapterID \" +\n                \"GROUP BY ProcessID\",\n                (\"@AdapterID\", GetID_AdapterTable_Internal(adapterName).ToString()));\n        }\n    }\n\n    public (long, long) GetDataSumBetweenDates(DateTime startDate, DateTime endDate)\n    {\n        DateTime fromDate = startDate.Date;\n        DateTime toDate = endDate.Date;\n\n        if (toDate < fromDate)\n        {\n            (fromDate, toDate) = (toDate, fromDate);\n        }\n\n        lock (DbLock)\n        {\n            var sum = GetMultipleCellData(\n                \"SELECT SUM(DataReceived), SUM(DataSent) FROM ProcessDate \" +\n                \"WHERE DateID IN \" +\n                \"(SELECT ID FROM Date \" +\n                \"WHERE (Year * 10000 + Month * 100 + Day) \" +\n                \"BETWEEN \" +\n                $\"({fromDate.Year * 10000 + fromDate.Month * 100 + fromDate.Day}) \" +\n                \"AND \" +\n                $\"({toDate.Year * 10000 + toDate.Month * 100 + toDate.Day})) \" +\n                \"AND AdapterID = @AdapterID\",\n                (\"@AdapterID\", GetID_AdapterTable_Internal(adapterName).ToString()));\n\n            if (sum.Count == 1 && sum[0].Count == 2)\n            {\n                long download = Convert.IsDBNull(sum[0][0]) ? 0 : Convert.ToInt64(sum[0][0]);\n                long upload = Convert.IsDBNull(sum[0][1]) ? 0 : Convert.ToInt64(sum[0][1]);\n                return (download, upload);\n            }\n        }\n\n        return (0, 0);\n    }\n\n    public (long, long) GetTodayDataSum_ProcessDateTable()\n    {\n        lock (DbLock)\n        {\n            var sum = GetMultipleCellData(\n                \"SELECT SUM(DataReceived), SUM(DataSent) FROM ProcessDate \" +\n                \"WHERE DateID IN \" +\n                \"(SELECT ID FROM Date \" +\n                \"WHERE (Year * 10000 + Month * 100 + Day) = (@Year * 10000 + @Month * 100 + @Day)) \" +\n                \"AND AdapterID = @AdapterID\",\n                (\"@Year\", DateTime.Today.Year.ToString()),\n                (\"@Month\", DateTime.Today.Month.ToString()),\n                (\"@Day\", DateTime.Today.Day.ToString()),\n                (\"@AdapterID\", GetID_AdapterTable_Internal(adapterName).ToString()));\n\n            if (sum.Count == 1 && sum[0].Count == 2 &&\n                !Convert.IsDBNull(sum[0][0]) &&\n                !Convert.IsDBNull(sum[0][1]))\n            {\n                return (Convert.ToInt64(sum[0][0]), Convert.ToInt64(sum[0][1]));\n            }\n        }\n\n        return (0, 0);\n    }\n\n    public long GetID_AdapterTable(string adapter)\n    {\n        lock (DbLock)\n        {\n            return GetID_AdapterTable_Internal(adapter);\n        }\n    }\n\n    public List<string> GetAllAdapters()\n    {\n        var adapters = new List<string>();\n\n        lock (DbLock)\n        {\n            var rows = GetMultipleCellData(\"SELECT Name FROM Adapter ORDER BY Name\");\n            foreach (var row in rows)\n            {\n                if (row.Count > 0 && !Convert.IsDBNull(row[0]))\n                {\n                    adapters.Add(Convert.ToString(row[0])!);\n                }\n            }\n        }\n\n        return adapters;\n    }\n\n    public void Dispose()\n    {\n        if (disposed)\n        {\n            return;\n        }\n\n        disposed = true;\n\n        lock (DbLock)\n        {\n            refCount--;\n            if (refCount == 0 && sharedConnection != null)\n            {\n                sharedConnection.Dispose();\n                sharedConnection = null;\n            }\n        }\n    }\n\n    private static SqliteConnection Connection =>\n        sharedConnection ?? throw new InvalidOperationException(\"Database not initialized\");\n\n    private int CreateProcessTable()\n    {\n        return RunNonQuery(\n            \"CREATE TABLE IF NOT EXISTS Process(\" +\n            \"ID INTEGER PRIMARY KEY NOT NULL, \" +\n            \"Name TEXT NOT NULL UNIQUE)\");\n    }\n\n    private int CreateDateTable()\n    {\n        return RunNonQuery(\n            \"CREATE TABLE IF NOT EXISTS Date(\" +\n            \"ID INTEGER PRIMARY KEY NOT NULL, \" +\n            \"Year INTEGER NOT NULL, \" +\n            \"Month INTEGER NOT NULL, \" +\n            \"Day INTEGER NOT NULL, \" +\n            \"UNIQUE (Year, Month, Day) ON CONFLICT IGNORE)\");\n    }\n\n    private int CreateProcessDateTable()\n    {\n        return RunNonQuery(\n            \"CREATE TABLE IF NOT EXISTS ProcessDate(\" +\n            \"ID INTEGER PRIMARY KEY NOT NULL, \" +\n            \"ProcessID INTEGER NOT NULL, \" +\n            \"DateID INTEGER NOT NULL, \" +\n            \"AdapterID INTEGER NOT NULL, \" +\n            \"DataReceived INTEGER NOT NULL, \" +\n            \"DataSent INTEGER NOT NULL, \" +\n            \"FOREIGN KEY(ProcessID) REFERENCES Process(ID) ON DELETE CASCADE, \" +\n            \"FOREIGN KEY(DateID) REFERENCES Date(ID) ON DELETE CASCADE, \" +\n            \"FOREIGN KEY(AdapterID) REFERENCES Adapter(ID) ON DELETE CASCADE, \" +\n            \"UNIQUE (ProcessID, DateID, AdapterID) ON CONFLICT IGNORE)\");\n    }\n\n    private int CreateAdapterTable()\n    {\n        return RunNonQuery(\n            \"CREATE TABLE IF NOT EXISTS Adapter(\" +\n            \"ID INTEGER PRIMARY KEY NOT NULL, \" +\n            \"Name TEXT NOT NULL UNIQUE)\");\n    }\n\n    private int InsertUniqueRow_ProcessTable(string appName)\n    {\n        return RunNonQuery(\n            \"INSERT OR IGNORE INTO Process(Name) VALUES(@Name)\",\n            (\"@Name\", appName));\n    }\n\n    private int InsertUniqueRow_DateTable(DateTime date)\n    {\n        return RunNonQuery(\n            \"INSERT OR IGNORE INTO Date(Year, Month, Day) VALUES(@Year, @Month, @Day)\",\n            (\"@Year\", date.Year.ToString()),\n            (\"@Month\", date.Month.ToString()),\n            (\"@Day\", date.Day.ToString()));\n    }\n\n    private void RemoveOldDate()\n    {\n        var cutoff = DateTime.Now.AddDays(-DataStoragePeriodInDays);\n        Console.WriteLine($\"[{LogTime()}] [SQLite][Avalonia] RemoveOldDate cutoff='{cutoff:yyyy-MM-dd}'\");\n        RunNonQuery(\n            \"DELETE FROM Date WHERE (Year * 10000 + Month * 100 + Day) < \" +\n            $\"({cutoff.Year * 10000 + cutoff.Month * 100 + cutoff.Day})\");\n    }\n\n    private void RemoveOldProcess()\n    {\n        Console.WriteLine($\"[{LogTime()}] [SQLite][Avalonia] RemoveOldProcess\");\n        RunNonQuery(\n            \"DELETE FROM Process WHERE ID NOT IN \" +\n            \"(SELECT DISTINCT ProcessID FROM ProcessDate)\");\n    }\n\n    private int InsertUniqueRow_ProcessDateTable(long processID, long dateID, long adapterID, long dataReceived, long dataSent)\n    {\n        return RunNonQuery(\n            \"INSERT OR IGNORE INTO ProcessDate(ProcessID, DateID, AdapterID, DataReceived, DataSent) \" +\n            \"VALUES(@ProcessID, @DateID, @AdapterID, @DataReceived, @DataSent)\",\n            (\"@ProcessID\", processID.ToString()),\n            (\"@DateID\", dateID.ToString()),\n            (\"@AdapterID\", adapterID.ToString()),\n            (\"@DataReceived\", dataReceived.ToString()),\n            (\"@DataSent\", dataSent.ToString()));\n    }\n\n    private int UpdateRow_ProcessDateTable(long processID, long dateID, long adapterID, long dataReceived, long dataSent)\n    {\n        return RunNonQuery(\n            \"UPDATE ProcessDate SET \" +\n            \"DataReceived = DataReceived + @DataReceived, \" +\n            \"DataSent = DataSent + @DataSent \" +\n            \"WHERE ProcessID = @ProcessID AND DateID = @DateID AND AdapterID = @AdapterID\",\n            (\"@DataReceived\", dataReceived.ToString()),\n            (\"@DataSent\", dataSent.ToString()),\n            (\"@ProcessID\", processID.ToString()),\n            (\"@DateID\", dateID.ToString()),\n            (\"@AdapterID\", adapterID.ToString()));\n    }\n\n    private long GetID_DateTable(DateTime time)\n    {\n        var result = GetSingleCellData(\n            \"SELECT ID FROM Date WHERE Year = @Year AND Month = @Month AND Day = @Day\",\n            (\"@Year\", time.Year.ToString()),\n            (\"@Month\", time.Month.ToString()),\n            (\"@Day\", time.Day.ToString()));\n\n        return Convert.ToInt64(result ?? -1);\n    }\n\n    private long GetID_ProcessTable(string appName)\n    {\n        var result = GetSingleCellData(\n            \"SELECT ID FROM Process WHERE Name = @Name\",\n            (\"@Name\", appName));\n\n        return Convert.ToInt64(result ?? -1);\n    }\n\n    private long GetID_AdapterTable_Internal(string adapter)\n    {\n        var result = GetSingleCellData(\n            \"SELECT ID FROM Adapter WHERE Name = @Name\",\n            (\"@Name\", adapter));\n\n        return Convert.ToInt64(result ?? -1);\n    }\n\n    private static int RunNonQuery(string query, params (string Name, string Value)[] parameters)\n    {\n        return RunNonQuery(Connection, query, parameters);\n    }\n\n    private static int RunNonQuery(SqliteConnection connection, string query, params (string Name, string Value)[] parameters)\n    {\n        try\n        {\n            using var command = connection.CreateCommand();\n            command.CommandText = query;\n            foreach (var parameter in parameters)\n            {\n                command.Parameters.AddWithValue(parameter.Name, parameter.Value);\n            }\n\n            return command.ExecuteNonQuery();\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error($\"SQLite non-query failed. Query='{query}'\", ex);\n            return -1;\n        }\n    }\n\n    private static List<List<object>> GetMultipleCellData(string query, params (string Name, string Value)[] parameters)\n    {\n        var result = new List<List<object>>();\n\n        try\n        {\n            using var command = Connection.CreateCommand();\n            command.CommandText = query;\n            foreach (var parameter in parameters)\n            {\n                command.Parameters.AddWithValue(parameter.Name, parameter.Value);\n            }\n\n            using var reader = command.ExecuteReader();\n            while (reader.Read())\n            {\n                var row = new List<object>();\n                for (int i = 0; i < reader.FieldCount; i++)\n                {\n                    row.Add(reader.GetValue(i));\n                }\n\n                result.Add(row);\n            }\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error($\"SQLite multi-cell read failed. Query='{query}'\", ex);\n            return result;\n        }\n\n        return result;\n    }\n\n    private static object? GetSingleCellData(string query, params (string Name, string Value)[] parameters)\n    {\n        try\n        {\n            using var command = Connection.CreateCommand();\n            command.CommandText = query;\n            foreach (var parameter in parameters)\n            {\n                command.Parameters.AddWithValue(parameter.Name, parameter.Value);\n            }\n\n            return command.ExecuteScalar();\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error($\"SQLite single-cell read failed. Query='{query}'\", ex);\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "OpenNetMeter/Compat/Models/MyProcess_Small.cs",
    "content": "namespace OpenNetMeter.Models;\n\npublic class MyProcess_Small\n{\n    public string? Name { get; set; }\n    public long CurrentDataRecv { get; set; }\n    public long CurrentDataSend { get; set; }\n\n    public MyProcess_Small(string nameP, long currentDataRecvP, long currentDataSendP)\n    {\n        Name = nameP;\n        CurrentDataRecv = currentDataRecvP;\n        CurrentDataSend = currentDataSendP;\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Compat/Models/NetworkProcess.TestHooks.cs",
    "content": "using System.Net;\n\nnamespace OpenNetMeter.Models\n{\n    // Test-only helpers kept internal to avoid exposing implementation details publicly.\n    public partial class NetworkProcess\n    {\n        internal void TestSetLocalIPs(byte[] ipv4, byte[] ipv6)\n        {\n            localIPv4 = ipv4;\n            localIPv6 = ipv6;\n        }\n\n        internal void TestInvokeRecvProcess(IPAddress src, IPAddress dest, int size, string name)\n        {\n            ProcessPacket(src, dest, size, name, isRecv: true);\n        }\n\n        internal void TestInvokeSendProcess(IPAddress src, IPAddress dest, int size, string name)\n        {\n            ProcessPacket(src, dest, size, name, isRecv: false);\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Compat/Models/NetworkProcess.cs",
    "content": "﻿using System;\nusing System.Net;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Net.NetworkInformation;\nusing System.Threading.Tasks;\nusing Microsoft.Diagnostics.Tracing.Parsers;\nusing Microsoft.Diagnostics.Tracing.Parsers.Kernel;\nusing Microsoft.Diagnostics.Tracing.Session;\nusing System.Threading;\nusing System.Net.Sockets;\nusing System.Collections.Generic;\nusing OpenNetMeter.Utilities;\nusing OpenNetMeter.Properties;\nusing System.Diagnostics.Eventing.Reader;\nusing System.Runtime.Versioning;\nusing System.Text.RegularExpressions;\n\nnamespace OpenNetMeter.Models\n{\n    [SupportedOSPlatform(\"windows\")]\n    public partial class NetworkProcess : IDisposable\n    {\n        //---------- Constants ------------//\n\n        private const int OneSec = 1000;\n        private const int DebounceDelayMs = 300; // Delay before processing network change events\n\n        // Default IP values indicating \"not connected\"\n        private static readonly byte[] DefaultIPv4 = { 0, 0, 0, 0 };\n        private static readonly byte[] DefaultIPv6 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };\n\n        //---------- Network State ------------//\n\n        /// <summary>\n        /// Immutable snapshot of current network connection state.\n        /// Used to detect connection changes without race conditions.\n        /// </summary>\n        private sealed record NetworkSnapshot(\n            string AdapterName,  // Display name, includes SSID for Wi-Fi\n            string AdapterId,    // GUID for the adapter\n            byte[] IPv4,         // Assigned IPv4 address bytes\n            byte[] IPv6          // Assigned IPv6 address bytes\n        );\n\n        // Lock for thread-safe access to local IP addresses\n        private readonly object stateLock = new object();\n\n        // Currently assigned local IP addresses (used for packet filtering)\n        private byte[] localIPv4 = DefaultIPv4;\n        private byte[] localIPv6 = DefaultIPv6;\n\n        //---------- Debounce & Synchronization ------------//\n\n        // Timer-based debounce: each network change event resets the timer.\n        // When it finally fires (after DebounceDelayMs of silence), HandleNetworkChange runs.\n        private Timer? debounceTimer;\n        private volatile bool isDisposed;\n\n        // Lock to ensure only one HandleNetworkChange runs at a time\n        private readonly object networkChangeLock = new object();\n\n        //---------- Periodic Tasks ------------//\n\n        // Periodic task for updating network speed display\n        private PeriodicWork? networkSpeedWork;\n\n        // Periodic task for pushing process data to database\n        private PeriodicWork? dbPushWork;\n\n        //---------- ETW Session ------------//\n\n        // ETW kernel session for capturing network packets\n        private TraceEventSession? kernelSession;\n\n        private const string KernelSessionName = \"OpenNetMeter-Kernel\";\n\n        // Task running the ETW packet capture loop\n        public Task? PacketTask;\n\n        //---------- Public State ------------//\n\n        // Current network adapter name (includes SSID for Wi-Fi connections)\n        public string AdapterName { get; private set; } = \"\";\n\n        // Current adapter's unique ID (GUID) - used for comparison to detect changes\n        private string currentAdapterId = \"\";\n        public string CurrentAdapterId => currentAdapterId;\n\n        /// <summary>\n        /// Primary buffer for storing process network data.\n        /// Alternates with MyProcessesBuffer to allow lock-free reading.\n        /// </summary>\n        public Dictionary<string, MyProcess_Small?> MyProcesses { get; } = new();\n\n        /// <summary>\n        /// Secondary buffer for storing process network data.\n        /// While GUI reads from MyProcesses, new data goes here (and vice versa).\n        /// This double-buffering prevents lock contention with the UI thread.\n        /// </summary>\n        public Dictionary<string, MyProcess_Small?> MyProcessesBuffer { get; } = new();\n\n        /// <summary>\n        /// Buffer for data pending database write.\n        /// Cleared after each successful DB push.\n        /// </summary>\n        public Dictionary<string, MyProcess_Small?> PushToDBBuffer { get; } = new();\n\n        /// <summary>\n        /// Controls which buffer receives incoming packet data.\n        /// true = write to MyProcessesBuffer, read from MyProcesses\n        /// false = write to MyProcesses, read from MyProcessesBuffer\n        /// Toggled by the UI layer when extracting data for display.\n        /// </summary>\n        public bool IsBufferTime { get; set; }\n\n        // Running totals for current session (reset on adapter change)\n        public long CurrentSessionDownloadData;\n        public long CurrentSessionUploadData;\n        public long UploadSpeed;\n\n        //---------- Properties with Change Notification ------------//\n\n        private long downloadSpeed;\n        public long DownloadSpeed\n        {\n            get => downloadSpeed;\n            set { downloadSpeed = value; OnPropertyChanged(nameof(DownloadSpeed)); }\n        }\n\n        private string isNetworkOnline = \"Disconnected\";\n        /// <summary>\n        /// Current connection status. Either \"Disconnected\" or the adapter name.\n        /// </summary>\n        public string IsNetworkOnline\n        {\n            get => isNetworkOnline;\n            private set { isNetworkOnline = value; OnPropertyChanged(nameof(IsNetworkOnline)); }\n        }\n\n        //---------- Initialization ------------//\n\n        /// <summary>\n        /// Call after subscribing to property handlers in MainWindowVM.\n        /// Sets up network change monitoring and performs initial connection check.\n        /// </summary>\n        public void Initialize()\n        {\n            IsNetworkOnline = \"Disconnected\";\n\n            // Create the debounce timer (initially not running)\n            debounceTimer = new Timer(_ => HandleNetworkChange(), null, Timeout.Infinite, Timeout.Infinite);\n\n            // Subscribe to network address changes (fires on connect/disconnect/IP change)\n            NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;\n\n            // Check current connection state on startup\n            HandleNetworkChange();\n        }\n\n        //---------- Network Detection (Snapshot-Based) ------------//\n\n        /// <summary>\n        /// Queries the system for the current active network connection.\n        /// Returns an immutable snapshot of the connection, or null if disconnected.\n        /// \n        /// This replaces the old socket-based GetLocalIP() approach which was unreliable\n        /// during network transitions (socket connect to 8.8.8.8 would fail transiently).\n        /// </summary>\n        private NetworkSnapshot? GetCurrentNetworkSnapshot()\n        {\n            var adapters = NetworkInterface.GetAllNetworkInterfaces();\n\n            foreach (var adapter in adapters)\n            {\n                // Skip adapters that aren't connected\n                if (adapter.OperationalStatus != OperationalStatus.Up)\n                    continue;\n\n                // Skip loopback (127.0.0.1)\n                if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback)\n                    continue;\n\n                var props = adapter.GetIPProperties();\n\n                // No gateway = not a real internet connection (e.g., virtual adapters)\n                if (props.GatewayAddresses.Count == 0)\n                    continue;\n\n                byte[]? ipv4 = null;\n                byte[]? ipv6 = null;\n\n                // Extract assigned IP addresses\n                foreach (var unicast in props.UnicastAddresses)\n                {\n                    var addr = unicast.Address;\n\n                    if (addr.AddressFamily == AddressFamily.InterNetwork)\n                    {\n                        ipv4 = addr.GetAddressBytes();\n                    }\n                    else if (addr.AddressFamily == AddressFamily.InterNetworkV6\n                             && !addr.IsIPv6LinkLocal) // Skip link-local (fe80::) addresses\n                    {\n                        ipv6 = addr.GetAddressBytes();\n                    }\n                }\n\n                // Need at least one usable IP to consider this a valid connection\n                if (ipv4 == null && ipv6 == null)\n                    continue;\n\n                // Build display name (include SSID for Wi-Fi)\n                var name = adapter.Name;\n                if (adapter.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)\n                {\n                    var ssid = GetConnectedSsid(adapter.Id);\n                    if (!string.IsNullOrEmpty(ssid))\n                        name += $\"({ssid})\";\n                }\n\n                Debug.WriteLine($\"{adapter.Name} is up, IP: {(ipv4 != null ? new IPAddress(ipv4) : new IPAddress(ipv6!))}\");\n\n                return new NetworkSnapshot(\n                    name,\n                    adapter.Id,\n                    ipv4 ?? DefaultIPv4,\n                    ipv6 ?? DefaultIPv6\n                );\n            }\n\n            return null; // No valid connection found\n        }\n\n        /// <summary>\n        /// Gets the SSID of the currently connected Wi-Fi network by reading\n        /// from the Windows WLAN AutoConfig event log.\n        /// \n        /// This is more reliable than the WinRT API on Windows 11 which requires\n        /// location permissions to retrieve SSID.\n        /// </summary>\n        private static string? GetConnectedSsid(string adapterGuid)\n        {\n            try\n            {\n                // Query for EventID 8001 (successful connection) in WLAN-AutoConfig log\n                var query = new EventLogQuery(\n                    \"Microsoft-Windows-WLAN-AutoConfig/Operational\",\n                    PathType.LogName,\n                    \"*[System[EventID=8001]]\"\n                )\n                {\n                    ReverseDirection = true // Get most recent first\n                };\n\n                using var reader = new EventLogReader(query);\n                if (reader.ReadEvent() is EventRecord evt)\n                {\n                    var message = evt.FormatDescription();\n                    var match = Regex.Match(message, @\"^SSID:\\s*(.+)$\", RegexOptions.Multiline);\n                    if (match.Success)\n                        return match.Groups[1].Value.Trim();\n                }\n            }\n            catch (Exception ex)\n            {\n                EventLogger.Error(\"Failed to read connected SSID\", ex);\n            }\n\n            return null;\n        }\n\n        //---------- Network Change Handling ------------//\n\n        /// <summary>\n        /// Event handler for NetworkChange.NetworkAddressChanged.\n        /// Resets the debounce timer so that HandleNetworkChange only runs\n        /// after DebounceDelayMs of silence (no rapid-fire events).\n        /// </summary>\n        private void OnNetworkAddressChanged(object? sender, EventArgs? e)\n        {\n            if (isDisposed)\n                return;\n\n            // Reset the timer countdown. If another event arrives before it fires,\n            // the timer resets again. No allocations, no CTS, no race conditions.\n            try\n            {\n                debounceTimer?.Change(DebounceDelayMs, Timeout.Infinite);\n            }\n            catch (ObjectDisposedException)\n            {\n                EventLogger.Warn(\"Debounce timer was disposed while handling network address change\");\n            }\n        }\n\n        /// <summary>\n        /// Core network state machine. Compares current network state to tracked state\n        /// and triggers appropriate actions (connect/disconnect/switch/refresh).\n        /// \n        /// State transitions:\n        /// - Disconnected -> Connected: Start monitoring\n        /// - Connected -> Disconnected: Stop monitoring  \n        /// - Connected -> Different adapter: Stop then start monitoring\n        /// - Connected -> Same adapter:\n        ///   - IP/name changed: Refresh tracked snapshot (no restart)\n        ///   - No meaningful change: Ignore transient event\n        /// \n        /// Protected by networkChangeLock to prevent concurrent execution.\n        /// </summary>\n        private void HandleNetworkChange()\n        {\n            if (isDisposed)\n                return;\n\n            lock (networkChangeLock)\n            {\n                if (isDisposed)\n                    return;\n\n                var snapshot = GetCurrentNetworkSnapshot();\n\n                if (snapshot == null)\n                {\n                    // No valid connection found\n                    if (IsNetworkOnline != \"Disconnected\")\n                    {\n                        Debug.WriteLine(\"Ash: Connection lost\");\n                        EndNetworkProcess();\n                    }\n                    return;\n                }\n\n                // Valid connection found - determine if it's new or changed\n                if (IsNetworkOnline == \"Disconnected\")\n                {\n                    // Was disconnected, now connected\n                    Debug.WriteLine(\"Ash: Connection established\");\n                    ApplySnapshot(snapshot);\n                    StartNetworkProcess();\n                }\n                else if (currentAdapterId != snapshot.AdapterId)\n                {\n                    // Was connected to different adapter (e.g., switched Wi-Fi networks)\n                    // Compare by ID, not name, because SSID retrieval can race with event log\n                    Debug.WriteLine(\"Ash: Network adapter changed\");\n                    EndNetworkProcess();\n                    ApplySnapshot(snapshot);\n                    StartNetworkProcess();\n                }\n                else\n                {\n                    // Same adapter can still receive DHCP/IPv6 address changes or an SSID/name update.\n                    // Refresh tracked snapshot so packet filtering keeps matching local traffic.\n                    bool ipChanged = HasSnapshotAddressChanged(snapshot);\n                    bool nameChanged = !string.Equals(AdapterName, snapshot.AdapterName, StringComparison.Ordinal);\n                    if (ipChanged || nameChanged)\n                    {\n                        Debug.WriteLine(\"Ash: Adapter IP/name changed on same adapter - refreshing snapshot\");\n                        ApplySnapshot(snapshot);\n\n                        // Keep status text in sync when SSID/name changes without adapter GUID change.\n                        if (nameChanged)\n                            IsNetworkOnline = AdapterName;\n                    }\n                }\n            }\n        }\n\n        private bool HasSnapshotAddressChanged(NetworkSnapshot snapshot)\n        {\n            lock (stateLock)\n            {\n                return !ByteArray.Compare(localIPv4, snapshot.IPv4) ||\n                       !ByteArray.Compare(localIPv6, snapshot.IPv6);\n            }\n        }\n\n        /// <summary>\n        /// Updates tracked state from a network snapshot.\n        /// Called when establishing a new connection, switching adapters,\n        /// or refreshing state on same-adapter IP/name changes.\n        /// </summary>\n        private void ApplySnapshot(NetworkSnapshot snapshot)\n        {\n            lock (stateLock)\n            {\n                localIPv4 = snapshot.IPv4;\n                localIPv6 = snapshot.IPv6;\n            }\n            currentAdapterId = snapshot.AdapterId;\n            AdapterName = snapshot.AdapterName;\n        }\n\n        //---------- Start/Stop Network Monitoring ------------//\n\n        /// <summary>\n        /// Starts all network monitoring components:\n        /// - Database table setup\n        /// - ETW packet capture\n        /// - Speed monitoring periodic task\n        /// - Database push periodic task\n        /// </summary>\n        public void StartNetworkProcess()\n        {\n            // Ensure database table exists for this adapter\n            using (var db = new ApplicationDB(AdapterName))\n            {\n                if (db.CreateTable() < 0)\n                {\n                    EventLogger.Error(\"Error: Create table\");\n                }\n                else\n                {\n                    db.InsertUniqueRow_AdapterTable(AdapterName);\n                    db.UpdateDatesInDB();\n                }\n            }\n\n            TraceEventSession.GetActiveSession(KernelSessionName)?.Stop();\n            // Start packet capture and periodic tasks\n            CaptureNetworkPackets();\n            StartSpeedMonitoring();\n            StartDbPush();\n\n            // Update connection status (triggers UI update via PropertyChanged)\n            IsNetworkOnline = AdapterName;\n        }\n\n        /// <summary>\n        /// Stops all network monitoring components and resets state.\n        /// Called on disconnect or before switching to a different adapter.\n        /// </summary>\n        public void EndNetworkProcess()\n        {\n            // Stop ETW session first (generates most activity)\n            StopKernelSession();\n\n            // Stop periodic tasks\n            StopPeriodicWork(ref networkSpeedWork, \"Network speed\");\n            StopPeriodicWork(ref dbPushWork, \"DB push\");\n\n            FlushPendingDbWrites();\n\n            // Clear process data buffers\n            lock (MyProcesses) MyProcesses.Clear();\n            lock (MyProcessesBuffer) MyProcessesBuffer.Clear();\n\n            // Reset speed counters\n            CurrentSessionDownloadData = 0;\n            CurrentSessionUploadData = 0;\n            UploadSpeed = 0;\n            DownloadSpeed = 0;\n\n            // Reset adapter tracking\n            currentAdapterId = \"\";\n            IsNetworkOnline = \"Disconnected\";\n        }\n\n        /// <summary>\n        /// Stops the ETW kernel session and waits for the capture task to complete.\n        /// </summary>\n        private void StopKernelSession()\n        {\n            // Disposing the session causes Source.Process() to return\n            kernelSession?.Dispose();\n            kernelSession = null;\n\n            var task = PacketTask;\n            if (task != null)\n            {\n                try\n                {\n                    task.Wait(TimeSpan.FromMilliseconds(OneSec));\n                }\n                catch (AggregateException ex)\n                {\n                    EventLogger.Error(\"Packet capture stop error\", ex);\n                }\n                finally\n                {\n                    if (task.IsCompleted)\n                        task.Dispose();\n                    else\n                        task.ContinueWith(t => t.Dispose(), TaskScheduler.Default);\n\n                    PacketTask = null;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Safely stops a periodic work task with error handling.\n        /// </summary>\n        private void StopPeriodicWork(ref PeriodicWork? work, string name)\n        {\n            try\n            {\n                work?.Stop();\n            }\n            catch (Exception ex)\n            {\n                EventLogger.Error($\"{name} stop error\", ex);\n            }\n            finally\n            {\n                work = null;\n            }\n        }\n\n        private void FlushPendingDbWrites()\n        {\n            lock (PushToDBBuffer)\n            {\n                if (PushToDBBuffer.Count <= 0 || string.IsNullOrWhiteSpace(AdapterName))\n                {\n                    PushToDBBuffer.Clear();\n                    return;\n                }\n\n                using var db = new ApplicationDB(AdapterName);\n                foreach (var (key, proc) in PushToDBBuffer)\n                {\n                    if (proc != null)\n                        db.PushToDB(key, proc.CurrentDataRecv, proc.CurrentDataSend);\n                }\n\n                PushToDBBuffer.Clear();\n            }\n        }\n\n        //---------- Periodic Tasks ------------//\n\n        /// <summary>\n        /// Starts the periodic task that calculates current network speed.\n        /// Runs every second and computes speed as delta from previous totals.\n        /// </summary>\n        private void StartSpeedMonitoring()\n        {\n            long tempDownload = 0;\n            long tempUpload = 0;\n\n            networkSpeedWork = new PeriodicWork(\"Network speed\", TimeSpan.FromSeconds(1));\n            networkSpeedWork.Start(_ =>\n            {\n                // Read current totals (atomic reads)\n                long currentDown = Interlocked.Read(ref CurrentSessionDownloadData);\n                long currentUp = Interlocked.Read(ref CurrentSessionUploadData);\n\n                // Calculate speed based on user's preferred format\n                if (SettingsManager.Current.NetworkSpeedFormat == 0)\n                {\n                    // Bits per second\n                    DownloadSpeed = (currentDown - tempDownload) * 8;\n                    UploadSpeed = (currentUp - tempUpload) * 8;\n                }\n                else\n                {\n                    // Bytes per second\n                    DownloadSpeed = currentDown - tempDownload;\n                    UploadSpeed = currentUp - tempUpload;\n                }\n\n                // Store for next iteration's delta calculation\n                tempDownload = currentDown;\n                tempUpload = currentUp;\n\n                return Task.CompletedTask;\n            });\n        }\n\n        /// <summary>\n        /// Starts the periodic task that pushes accumulated process data to the database.\n        /// Runs every 5 seconds to batch writes and reduce I/O.\n        /// </summary>\n        private void StartDbPush()\n        {\n            dbPushWork = new PeriodicWork(\"DB push\", TimeSpan.FromSeconds(5));\n            dbPushWork.Start(_ =>\n            {\n#if DEBUG\n                var sw = Stopwatch.StartNew();\n#endif\n                lock (PushToDBBuffer)\n                {\n                    if (PushToDBBuffer.Count > 0)\n                    {\n                        using var db = new ApplicationDB(AdapterName);\n                        foreach (var (key, proc) in PushToDBBuffer)\n                        {\n                            if (proc != null)\n                                db.PushToDB(key, proc.CurrentDataRecv, proc.CurrentDataSend);\n                        }\n                        PushToDBBuffer.Clear();\n                    }\n                }\n#if DEBUG\n                sw.Stop();\n                Debug.WriteLine($\"elapsed time (DBpush): {sw.ElapsedMilliseconds} | time {DateTime.Now:O}\");\n#endif\n                return Task.CompletedTask;\n            });\n        }\n\n        //---------- ETW Packet Capture ------------//\n\n        /// <summary>\n        /// Starts the ETW (Event Tracing for Windows) kernel session to capture\n        /// all TCP/IP network packets on the system.\n        /// \n        /// Uses the NT Kernel Logger session which requires admin privileges.\n        /// Packets are filtered to only count those matching our local IP.\n        /// </summary>\n        private void CaptureNetworkPackets()\n        {\n            PacketTask = Task.Run(() =>\n            {\n                if (kernelSession != null) return;\n\n                try\n                {\n                    // Create kernel trace session (requires admin)\n                    kernelSession = new TraceEventSession(KernelSessionName);\n                    kernelSession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP);\n\n                    // Subscribe to all TCP/UDP send/receive events\n                    // All events funnel through ProcessPacket for unified handling\n                    var kernel = kernelSession.Source.Kernel;\n\n                    // Receive events (download)\n                    kernel.TcpIpRecv += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);\n                    kernel.TcpIpRecvIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);\n                    kernel.UdpIpRecv += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);\n                    kernel.UdpIpRecvIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);\n\n                    // Send events (upload)\n                    kernel.TcpIpSend += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);\n                    kernel.TcpIpSendIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);\n                    kernel.UdpIpSend += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);\n                    kernel.UdpIpSendIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);\n\n                    // Blocks until session is disposed\n                    kernelSession.Source.Process();\n                }\n                catch (Exception ex)\n                {\n                    EventLogger.Error(\"Packet capture loop error\", ex);\n                }\n            });\n        }\n\n        /// <summary>\n        /// Processes a single network packet captured by ETW.\n        /// Filters packets to only count those involving our local IP,\n        /// then records to the appropriate buffer based on direction.\n        /// </summary>\n        /// <param name=\"src\">Source IP address</param>\n        /// <param name=\"dest\">Destination IP address</param>\n        /// <param name=\"size\">Packet size in bytes</param>\n        /// <param name=\"processName\">Name of the process that sent/received the packet</param>\n        /// <param name=\"isRecv\">True for download, false for upload</param>\n        private void ProcessPacket(IPAddress src, IPAddress dest, int size, string processName, bool isRecv)\n        {\n            // Get current local IP for this address family\n            byte[] localIp;\n            lock (stateLock)\n            {\n                localIp = src.AddressFamily == AddressFamily.InterNetwork ? localIPv4 : localIPv6;\n            }\n\n            // Cache address bytes to avoid repeated allocations\n            var srcBytes = src.GetAddressBytes();\n            var destBytes = dest.GetAddressBytes();\n\n            // Check if packet involves our local IP\n            bool isSrc = ByteArray.Compare(srcBytes, localIp);\n            bool isDest = ByteArray.Compare(destBytes, localIp);\n\n            // XOR: exactly one should match (either we sent it or received it)\n            // If both match (loopback) or neither match (other adapter), skip\n            if (!(isSrc ^ isDest))\n                return;\n\n            // Apply network type filter (private/public/both)\n            if (!ShouldProcessByNetworkType(isSrc, src, dest))\n                return;\n\n            // Record the packet to appropriate counter and buffer\n            if (isRecv)\n                RecordRecv(processName, size);\n            else\n                RecordSend(processName, size);\n        }\n\n        /// <summary>\n        /// Checks if a packet should be counted based on the user's network type filter setting.\n        /// </summary>\n        /// <param name=\"isLocalSrc\">True if local IP is the source (upload), false if destination (download)</param>\n        /// <param name=\"src\">Source IP</param>\n        /// <param name=\"dest\">Destination IP</param>\n        /// <returns>True if packet should be counted</returns>\n        private bool ShouldProcessByNetworkType(bool isLocalSrc, IPAddress src, IPAddress dest)\n        {\n            // Remote IP is the one that isn't ours\n            var remoteIp = isLocalSrc ? dest : src;\n\n            return SettingsManager.Current.NetworkType switch\n            {\n                0 => IsPrivateIP(remoteIp),   // Private only (LAN traffic)\n                1 => !IsPrivateIP(remoteIp),  // Public only (internet traffic)\n                2 => true,                     // Both\n                _ => false\n            };\n        }\n\n        /// <summary>\n        /// Records a received (download) packet.\n        /// </summary>\n        private void RecordRecv(string name, int size)\n        {\n            // Thread-safe increment of running total\n            Interlocked.Add(ref CurrentSessionDownloadData, size);\n            RecordToBuffer(name, size, isRecv: true);\n        }\n\n        /// <summary>\n        /// Records a sent (upload) packet.\n        /// </summary>\n        private void RecordSend(string name, int size)\n        {\n            // Thread-safe increment of running total\n            Interlocked.Add(ref CurrentSessionUploadData, size);\n            RecordToBuffer(name, size, isRecv: false);\n        }\n\n        /// <summary>\n        /// Records packet data to the active process buffer.\n        /// Uses double-buffering to minimize lock contention with UI reads.\n        /// </summary>\n        private void RecordToBuffer(string name, int size, bool isRecv)\n        {\n            // Empty process name means kernel/system traffic\n            if (string.IsNullOrEmpty(name))\n                name = \"System\";\n\n            // Select buffer based on current phase\n            var dict = IsBufferTime ? MyProcessesBuffer : MyProcesses;\n\n            lock (dict)\n            {\n                // Get or create process entry (single lookup)\n                if (!dict.TryGetValue(name, out var proc))\n                {\n                    proc = new MyProcess_Small(name, 0, 0);\n                    dict[name] = proc;\n                }\n\n                // Accumulate data\n                if (isRecv)\n                    proc!.CurrentDataRecv += size;\n                else\n                    proc!.CurrentDataSend += size;\n            }\n        }\n\n        //---------- IP Classification ------------//\n\n        /// <summary>\n        /// Determines if an IP address is in a private (non-routable) range.\n        /// Used for filtering traffic by network type (LAN vs internet).\n        /// \n        /// Private ranges:\n        /// - IPv4: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16\n        /// - IPv6: Link-local (fe80::), site-local, unique-local (fc00::/7)\n        /// </summary>\n        private bool IsPrivateIP(IPAddress ip)\n        {\n            // Handle IPv4-mapped IPv6 addresses (::ffff:x.x.x.x)\n            if (ip.IsIPv4MappedToIPv6)\n                ip = ip.MapToIPv4();\n\n            // Loopback is always private\n            if (IPAddress.IsLoopback(ip))\n                return true;\n\n            if (ip.AddressFamily == AddressFamily.InterNetwork)\n            {\n                var bytes = ip.GetAddressBytes();\n                return bytes[0] == 10 ||                                        // 10.0.0.0/8 (Class A)\n                       (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) || // 172.16.0.0/12 (Class B)\n                       (bytes[0] == 192 && bytes[1] == 168) ||                  // 192.168.0.0/16 (Class C)\n                       (bytes[0] == 169 && bytes[1] == 254);                    // 169.254.0.0/16 (link-local/APIPA)\n            }\n\n            if (ip.AddressFamily == AddressFamily.InterNetworkV6)\n            {\n                return ip.IsIPv6LinkLocal ||     // fe80::/10\n#if NET6_0_OR_GREATER\n                       ip.IsIPv6UniqueLocal ||   // fc00::/7 (only available in .NET 6+)\n#endif\n                       ip.IsIPv6SiteLocal;       // fec0::/10 (deprecated but still checked)\n            }\n\n            return false;\n        }\n\n        //---------- Property Changed ------------//\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n\n        private void OnPropertyChanged(string propName) =>\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n\n        //---------- Dispose ------------//\n\n        /// <summary>\n        /// Cleans up all resources: unsubscribes from events, stops monitoring,\n        /// and flushes any pending data to the database.\n        /// </summary>\n        public void Dispose()\n        {\n            isDisposed = true;\n\n            // Unsubscribe from network change events\n            NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged;\n\n            // Dispose the debounce timer (prevents any pending callback from firing)\n            debounceTimer?.Dispose();\n            debounceTimer = null;\n\n            // Stop monitoring if active\n            if (IsNetworkOnline != \"Disconnected\")\n                EndNetworkProcess();\n\n            // Flush any remaining buffered data to database\n            FlushPendingDbWrites();\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Compat/Properties/AppSettings.cs",
    "content": "namespace OpenNetMeter.Properties;\n\npublic class AppSettings\n{\n    public bool DarkMode { get; set; }\n    public bool StartWithWin { get; set; }\n    public bool MinimizeOnStart { get; set; } = true;\n    public int WinPosX { get; set; }\n    public int WinPosY { get; set; }\n    public int WinWidth { get; set; } = 900;\n    public int WinHeight { get; set; } = 600;\n    public bool MainWindowPositionInitialized { get; set; }\n    public bool MiniWidgetVisibility { get; set; } = true;\n    public bool MiniWidgetPinned { get; set; }\n    public int MiniWidgetPosX { get; set; }\n    public int MiniWidgetPosY { get; set; }\n    public bool MiniWidgetPositionInitialized { get; set; }\n    public int MiniWidgetTransparentSlider { get; set; } = 20;\n\n    public int NetworkType { get; set; } = 2;\n    public int NetworkSpeedFormat { get; set; } = 0;\n    public int NetworkSpeedMagnitude { get; set; } = 0;\n}\n"
  },
  {
    "path": "OpenNetMeter/Compat/Properties/Global.cs",
    "content": "using System;\nusing System.IO;\n\nnamespace OpenNetMeter.Properties;\n\ninternal class Global\n{\n    public const string AppName = \"OpenNetMeter\";\n\n    public static string GetFilePath()\n    {\n        var path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\n        path = Path.Combine(path, AppName);\n        Directory.CreateDirectory(path);\n        return path;\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Compat/Properties/SettingsManager.cs",
    "content": "using System;\nusing System.IO;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.Properties;\n\ninternal static class SettingsManager\n{\n    private static readonly string filePath;\n    private static readonly object sync = new();\n    public static AppSettings Current { get; } = new();\n\n    static SettingsManager()\n    {\n        filePath = Path.Combine(Global.GetFilePath(), \"settings.json\");\n        Load();\n    }\n\n    public static void Load()\n    {\n        lock (sync)\n        {\n            if (!File.Exists(filePath))\n                return;\n\n            try\n            {\n                var json = File.ReadAllText(filePath);\n                var root = JsonNode.Parse(json) as JsonObject;\n                if (root == null)\n                    return;\n\n                Current.DarkMode = GetBool(root, nameof(AppSettings.DarkMode), Current.DarkMode);\n                Current.StartWithWin = GetBool(root, nameof(AppSettings.StartWithWin), Current.StartWithWin);\n                Current.MinimizeOnStart = GetBool(root, nameof(AppSettings.MinimizeOnStart), Current.MinimizeOnStart);\n                Current.WinPosX = GetInt(root, nameof(AppSettings.WinPosX), Current.WinPosX);\n                Current.WinPosY = GetInt(root, nameof(AppSettings.WinPosY), Current.WinPosY);\n                Current.WinWidth = GetInt(root, nameof(AppSettings.WinWidth), Current.WinWidth);\n                Current.WinHeight = GetInt(root, nameof(AppSettings.WinHeight), Current.WinHeight);\n                Current.MainWindowPositionInitialized = GetBool(root, nameof(AppSettings.MainWindowPositionInitialized), Current.MainWindowPositionInitialized);\n                Current.MiniWidgetVisibility = GetBool(root, nameof(AppSettings.MiniWidgetVisibility), Current.MiniWidgetVisibility);\n                Current.MiniWidgetPinned = GetBool(root, nameof(AppSettings.MiniWidgetPinned), Current.MiniWidgetPinned);\n                Current.MiniWidgetPosX = GetInt(root, nameof(AppSettings.MiniWidgetPosX), Current.MiniWidgetPosX);\n                Current.MiniWidgetPosY = GetInt(root, nameof(AppSettings.MiniWidgetPosY), Current.MiniWidgetPosY);\n                Current.MiniWidgetPositionInitialized = GetBool(root, nameof(AppSettings.MiniWidgetPositionInitialized), Current.MiniWidgetPositionInitialized);\n                Current.MiniWidgetTransparentSlider = GetInt(root, nameof(AppSettings.MiniWidgetTransparentSlider), Current.MiniWidgetTransparentSlider);\n                Current.NetworkType = GetInt(root, nameof(AppSettings.NetworkType), Current.NetworkType);\n                Current.NetworkSpeedFormat = GetInt(root, nameof(AppSettings.NetworkSpeedFormat), Current.NetworkSpeedFormat);\n                Current.NetworkSpeedMagnitude = GetInt(root, nameof(AppSettings.NetworkSpeedMagnitude), Current.NetworkSpeedMagnitude);\n            }\n            catch (Exception ex)\n            {\n                EventLogger.Error(\"Error loading settings\", ex);\n            }\n        }\n    }\n\n    public static void Save()\n    {\n        lock (sync)\n        {\n            JsonObject root;\n            if (File.Exists(filePath))\n            {\n                try\n                {\n                    root = JsonNode.Parse(File.ReadAllText(filePath)) as JsonObject ?? [];\n                }\n                catch (Exception ex)\n                {\n                    EventLogger.Error(\"Error parsing existing settings file before save; creating new settings root\", ex);\n                    root = [];\n                }\n            }\n            else\n            {\n                root = [];\n            }\n\n            root[nameof(AppSettings.DarkMode)] = Current.DarkMode;\n            root[nameof(AppSettings.StartWithWin)] = Current.StartWithWin;\n            root[nameof(AppSettings.MinimizeOnStart)] = Current.MinimizeOnStart;\n            root[nameof(AppSettings.WinPosX)] = Current.WinPosX;\n            root[nameof(AppSettings.WinPosY)] = Current.WinPosY;\n            root[nameof(AppSettings.WinWidth)] = Current.WinWidth;\n            root[nameof(AppSettings.WinHeight)] = Current.WinHeight;\n            root[nameof(AppSettings.MainWindowPositionInitialized)] = Current.MainWindowPositionInitialized;\n            root[nameof(AppSettings.MiniWidgetVisibility)] = Current.MiniWidgetVisibility;\n            root[nameof(AppSettings.MiniWidgetPinned)] = Current.MiniWidgetPinned;\n            root[nameof(AppSettings.MiniWidgetPosX)] = Current.MiniWidgetPosX;\n            root[nameof(AppSettings.MiniWidgetPosY)] = Current.MiniWidgetPosY;\n            root[nameof(AppSettings.MiniWidgetPositionInitialized)] = Current.MiniWidgetPositionInitialized;\n            root[nameof(AppSettings.MiniWidgetTransparentSlider)] = Current.MiniWidgetTransparentSlider;\n            root[nameof(AppSettings.NetworkType)] = Current.NetworkType;\n            root[nameof(AppSettings.NetworkSpeedFormat)] = Current.NetworkSpeedFormat;\n            root[nameof(AppSettings.NetworkSpeedMagnitude)] = Current.NetworkSpeedMagnitude;\n\n            var json = root.ToJsonString(new JsonSerializerOptions\n            {\n                WriteIndented = true\n            });\n            File.WriteAllText(filePath, json);\n        }\n    }\n\n    private static bool GetBool(JsonObject root, string key, bool fallback)\n    {\n        if (!root.TryGetPropertyValue(key, out var node) || node == null)\n            return fallback;\n\n        try\n        {\n            return node.GetValue<bool>();\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error($\"Error reading bool setting '{key}'\", ex);\n            return fallback;\n        }\n    }\n\n    private static int GetInt(JsonObject root, string key, int fallback)\n    {\n        if (!root.TryGetPropertyValue(key, out var node) || node == null)\n            return fallback;\n\n        try\n        {\n            return node.GetValue<int>();\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error($\"Error reading int setting '{key}'\", ex);\n            return fallback;\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Compat/Utilities/ByteArray.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace OpenNetMeter.Utilities\n{\n    internal class ByteArray\n    {\n        public static bool Compare(ReadOnlySpan<byte> a1, ReadOnlySpan<byte> a2)\n        {\n            return a1.SequenceEqual(a2);\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Compat/Utilities/EventLogger.cs",
    "content": "using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.Versioning;\n\nnamespace OpenNetMeter.Utilities;\n\npublic static class EventLogger\n{\n    private const string EventSourceName = \"OpenNetMeter\";\n    private const int MaxEventLogMessageLength = 31839;\n\n    private enum LogLevel\n    {\n        Information,\n        Warning,\n        Error\n    }\n\n    public static void Info(string message, int eventId = 1000, short category = 0)\n    {\n        WriteEntrySafe(message, LogLevel.Information, eventId, category);\n    }\n\n    public static void Warn(string message, int eventId = 2000, short category = 0)\n    {\n        WriteEntrySafe(message, LogLevel.Warning, eventId, category);\n    }\n\n    public static void Error(\n        string message,\n        int eventId = 3000,\n        short category = 0,\n        [CallerMemberName] string memberName = \"\",\n        [CallerFilePath] string filePath = \"\",\n        [CallerLineNumber] int lineNumber = 0)\n    {\n        string logMessage = AddCallerContext(message, memberName, filePath, lineNumber);\n        WriteEntrySafe(logMessage, LogLevel.Error, eventId, category);\n    }\n\n    public static void Error(\n        Exception ex,\n        int eventId = 3001,\n        short category = 0,\n        [CallerMemberName] string memberName = \"\",\n        [CallerFilePath] string filePath = \"\",\n        [CallerLineNumber] int lineNumber = 0)\n    {\n        Error(\"Unhandled exception\", ex, eventId, category, memberName, filePath, lineNumber);\n    }\n\n    public static void Error(\n        string message,\n        Exception ex,\n        int eventId = 3001,\n        short category = 0,\n        [CallerMemberName] string memberName = \"\",\n        [CallerFilePath] string filePath = \"\",\n        [CallerLineNumber] int lineNumber = 0)\n    {\n        string exceptionText = ex is AggregateException aggregateEx\n            ? aggregateEx.Flatten().ToString()\n            : ex.ToString();\n        string errorMessage = $\"{message}{Environment.NewLine}{exceptionText}\";\n        string logMessage = AddCallerContext(errorMessage, memberName, filePath, lineNumber);\n        WriteEntrySafe(logMessage, LogLevel.Error, eventId, category);\n    }\n\n    private static string AddCallerContext(string message, string memberName, string filePath, int lineNumber)\n    {\n        string fileName = string.IsNullOrWhiteSpace(filePath) ? \"unknown\" : Path.GetFileName(filePath);\n        return $\"[{fileName}:{lineNumber} in {memberName}] {message}\";\n    }\n\n    private static void WriteEntrySafe(string message, LogLevel level, int eventId, short category)\n    {\n        string safeMessage = Truncate(message);\n        Debug.WriteLine(safeMessage);\n\n        if (!OperatingSystem.IsWindows())\n            return;\n\n        try\n        {\n            WriteEntryWindows(safeMessage, level, eventId, category);\n        }\n        catch (Exception writeEx)\n        {\n            Debug.WriteLine($\"[EventLogger fallback] Failed to write to Windows Event Log: {writeEx}\");\n        }\n    }\n\n    [SupportedOSPlatform(\"windows\")]\n    private static void WriteEntryWindows(string message, LogLevel level, int eventId, short category)\n    {\n        var entryType = level switch\n        {\n            LogLevel.Information => EventLogEntryType.Information,\n            LogLevel.Warning => EventLogEntryType.Warning,\n            _ => EventLogEntryType.Error\n        };\n\n        EventLog.WriteEntry(EventSourceName, message, entryType, eventId, category);\n    }\n\n    private static string Truncate(string message)\n    {\n        if (message.Length <= MaxEventLogMessageLength)\n            return message;\n\n        const string suffix = \"... [truncated]\";\n        int maxLength = MaxEventLogMessageLength - suffix.Length;\n        return message[..Math.Max(0, maxLength)] + suffix;\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Compat/Utilities/PeriodicWork.cs",
    "content": "using System;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace OpenNetMeter.Utilities\n{\n    internal sealed class PeriodicWork : IDisposable, IAsyncDisposable\n    {\n        private readonly string name;\n        private readonly TimeSpan interval;\n        private readonly SemaphoreSlim stopLock = new SemaphoreSlim(1, 1);\n\n        private Task? runTask;\n        private PeriodicTimer? timer;\n        private CancellationTokenSource? cts;\n\n        public PeriodicWork(string name, TimeSpan interval)\n        {\n            this.name = name;\n            this.interval = interval;\n        }\n\n        public void Start(Func<CancellationToken, Task> onTick)\n        {\n            if (runTask != null)\n                return; // already running\n\n            cts = new CancellationTokenSource();\n            timer = new PeriodicTimer(interval);\n            runTask = RunAsync(onTick, cts.Token, timer);\n        }\n\n        private async Task RunAsync(Func<CancellationToken, Task> onTick, CancellationToken token, PeriodicTimer timer)\n        {\n            try\n            {\n                while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))\n                {\n                    try\n                    {\n                        await onTick(token).ConfigureAwait(false);\n                    }\n                    catch (OperationCanceledException)\n                    {\n                        EventLogger.Warn($\"{name} tick cancelled\");\n                        throw;\n                    }\n                    catch (Exception ex)\n                    {\n                        EventLogger.Error($\"{name} tick error\", ex);\n                    }\n                }\n            }\n            catch (OperationCanceledException)\n            {\n                Debug.WriteLine($\"{name} task cancelled\");\n                EventLogger.Warn($\"{name} task cancelled\");\n            }\n        }\n\n        public async Task StopAsync()\n        {\n            await stopLock.WaitAsync().ConfigureAwait(false);\n            try\n            {\n                if (runTask == null)\n                    return;\n\n                cts?.Cancel();\n                try\n                {\n                    await runTask.ConfigureAwait(false);\n                }\n                catch (OperationCanceledException)\n                {\n                    EventLogger.Warn($\"{name} stop cancelled\");\n                }\n            }\n            finally\n            {\n                timer?.Dispose();\n                cts?.Dispose();\n                runTask = null;\n                timer = null;\n                cts = null;\n                stopLock.Release();\n            }\n        }\n\n        public void Stop()\n        {\n            StopAsync().GetAwaiter().GetResult();\n        }\n\n        public void Dispose()\n        {\n            Stop();\n        }\n\n        public ValueTask DisposeAsync()\n        {\n            return new ValueTask(StopAsync());\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/OpenNetMeter.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable</Nullable>\n    <Version>$(ProductVersion)</Version>\n    <AssemblyName>$(ProductName)</AssemblyName>\n    <ApplicationManifest>app.manifest</ApplicationManifest>\n    <ApplicationIcon>..\\Resources\\AppIcon.ico</ApplicationIcon>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Avalonia\" Version=\"11.3.12\" />\n    <PackageReference Include=\"Avalonia.Controls.DataGrid\" Version=\"11.3.12\" />\n    <PackageReference Include=\"Avalonia.Desktop\" Version=\"11.3.12\" />\n    <PackageReference Include=\"Avalonia.Themes.Fluent\" Version=\"11.3.12\" />\n    <PackageReference Include=\"LiveChartsCore.SkiaSharpView.Avalonia\" Version=\"2.0.0-rc6.1\" />\n    <PackageReference Include=\"Microsoft.Data.Sqlite\" Version=\"8.0.8\" />\n    <PackageReference Include=\"Microsoft.Diagnostics.Tracing.TraceEvent\" Version=\"3.1.6\" />\n    <PackageReference Include=\"System.Diagnostics.EventLog\" Version=\"8.0.0\" />\n    <PackageReference Include=\"System.Drawing.Common\" Version=\"8.0.12\" />\n    <PackageReference Include=\"TaskScheduler\" Version=\"2.11.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\OpenNetMeter.Core\\OpenNetMeter.Core.csproj\" />\n    <ProjectReference Include=\"..\\OpenNetMeter.PlatformAbstractions\\OpenNetMeter.PlatformAbstractions.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <AvaloniaResource Include=\"Assets\\**\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "OpenNetMeter/Program.cs",
    "content": "﻿using System;\nusing Avalonia;\n\nnamespace OpenNetMeter;\n\ninternal sealed class Program\n{\n    [STAThread]\n    public static void Main(string[] args)\n    {\n        BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);\n    }\n\n    public static AppBuilder BuildAvaloniaApp()\n    {\n        return AppBuilder.Configure<App>()\n            .UsePlatformDetect()\n            .LogToTrace();\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Services/AvaloniaThemeService.cs",
    "content": "using Avalonia;\nusing Avalonia.Styling;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class AvaloniaThemeService : IThemeService\n{\n    private readonly Application application;\n\n    public AvaloniaThemeService(Application application)\n    {\n        this.application = application;\n    }\n\n    public void ApplyDarkMode(bool enabled)\n    {\n        application.RequestedThemeVariant = enabled\n            ? ThemeVariant.Dark\n            : ThemeVariant.Light;\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Services/AvaloniaWindowService.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Controls.ApplicationLifetimes;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class AvaloniaWindowService : IWindowService\n{\n    public void MinimizeMainWindow()\n    {\n        if (GetMainWindow() is { } window)\n            window.WindowState = WindowState.Minimized;\n    }\n\n    public void CloseMainWindow()\n    {\n        if (GetMainWindow() is { } window)\n            window.Close();\n    }\n\n    public void ShowAbout()\n    {\n        // Placeholder until About dialog content is migrated to Avalonia.\n    }\n\n    private static Window? GetMainWindow()\n    {\n        return (Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow;\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/ExternalLinkService.cs",
    "content": "using System;\nusing System.Diagnostics;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class ExternalLinkService : IExternalLinkService\n{\n    public void Open(string uri)\n    {\n        if (string.IsNullOrWhiteSpace(uri))\n            return;\n\n        try\n        {\n            Process.Start(new ProcessStartInfo\n            {\n                FileName = uri,\n                UseShellExecute = true\n            });\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error($\"Failed to open external link '{uri}'\", ex);\n        }\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/IMiniWidgetService.cs",
    "content": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Services;\n\npublic interface IMiniWidgetService : System.IDisposable\n{\n    event System.Action<bool>? VisibilityChanged;\n\n    void Show();\n    void Hide();\n    void RefreshAppearance(bool darkMode, int transparency);\n    void ResetPosition(Window mainWindow);\n    void EnsurePositionOnScreen(Window mainWindow);\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/IThemeService.cs",
    "content": "namespace OpenNetMeter.Services;\n\npublic interface IThemeService\n{\n    void ApplyDarkMode(bool enabled);\n}\n"
  },
  {
    "path": "OpenNetMeter/Services/ITrayNotificationService.cs",
    "content": "using System;\nusing Avalonia.Controls;\n\nnamespace OpenNetMeter.Services;\n\npublic interface ITrayNotificationService : IDisposable\n{\n    void ShowMinimizedToTrayOnce(Window mainWindow);\n}\n"
  },
  {
    "path": "OpenNetMeter/Services/ITrayService.cs",
    "content": "namespace OpenNetMeter.Services;\n\npublic interface ITrayService : System.IDisposable\n{\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/NoOpThemeService.cs",
    "content": "namespace OpenNetMeter.Services;\n\npublic sealed class NoOpThemeService : IThemeService\n{\n    public void ApplyDarkMode(bool enabled)\n    {\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderMiniWidgetService.cs",
    "content": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderMiniWidgetService : IMiniWidgetService\n{\n    public event System.Action<bool>? VisibilityChanged\n    {\n        add { }\n        remove { }\n    }\n\n    public void Show()\n    {\n    }\n\n    public void Hide()\n    {\n    }\n\n    public void RefreshAppearance(bool darkMode, int transparency)\n    {\n    }\n\n    public void ResetPosition(Window mainWindow)\n    {\n    }\n\n    public void EnsurePositionOnScreen(Window mainWindow)\n    {\n    }\n\n    public void Dispose()\n    {\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderNetworkCaptureService.cs",
    "content": "using System;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderNetworkCaptureService : INetworkCaptureService\n{\n    private bool started;\n\n    public event EventHandler<NetworkSnapshotChangedEventArgs>? NetworkChanged;\n    public event EventHandler<NetworkTrafficEventArgs>? TrafficObserved\n    {\n        add { }\n        remove { }\n    }\n\n    public void Start()\n    {\n        if (started)\n            return;\n\n        started = true;\n        NetworkChanged?.Invoke(this, new NetworkSnapshotChangedEventArgs(string.Empty, string.Empty));\n    }\n\n    public void Stop()\n    {\n        started = false;\n    }\n\n    public void Dispose()\n    {\n        Stop();\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderProcessIconService.cs",
    "content": "using OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderProcessIconService : IProcessIconService\n{\n    public object? GetProcessIcon(string processName) => null;\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderStartupRegistrationService.cs",
    "content": "using OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderStartupRegistrationService : IStartupRegistrationService\n{\n    public bool IsEnabled() => false;\n\n    public void SetEnabled(bool enabled, bool startMinimized)\n    {\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderTrayNotificationService.cs",
    "content": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderTrayNotificationService : ITrayNotificationService\n{\n    public void ShowMinimizedToTrayOnce(Window mainWindow)\n    {\n    }\n\n    public void Dispose()\n    {\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Services/PlaceholderTrayService.cs",
    "content": "namespace OpenNetMeter.Services;\n\npublic sealed class PlaceholderTrayService : ITrayService\n{\n    public void Dispose()\n    {\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/UpdateChecker.cs",
    "content": "using System;\nusing System.Net.Http;\nusing System.Text.Json;\nusing System.Threading.Tasks;\n\nnamespace OpenNetMeter.Services;\n\npublic static class UpdateChecker\n{\n    public static async Task<(Version? latestVersion, string? downloadUrl)> CheckForUpdatesAsync()\n    {\n        using var client = new HttpClient();\n        client.DefaultRequestHeaders.Add(\"User-Agent\", \"OpenNetMeter\");\n\n        using var response = await client.GetAsync($\"https://api.github.com/repos/Ashfaaq18/OpenNetMeter/releases/latest\");\n        if (!response.IsSuccessStatusCode)\n        {\n            return (null, null);\n        }\n\n        await using var stream = await response.Content.ReadAsStreamAsync();\n        using var document = await JsonDocument.ParseAsync(stream);\n        JsonElement root = document.RootElement;\n\n        if (!root.TryGetProperty(\"tag_name\", out JsonElement tagElement))\n        {\n            return (null, null);\n        }\n\n        string? latestTag = tagElement.GetString();\n        if (string.IsNullOrWhiteSpace(latestTag))\n        {\n            return (null, null);\n        }\n\n        string normalizedVersion = latestTag.Trim();\n        if (normalizedVersion.StartsWith('v') || normalizedVersion.StartsWith('V'))\n        {\n            normalizedVersion = normalizedVersion[1..];\n        }\n\n        if (!Version.TryParse(normalizedVersion, out Version? latestVersion))\n        {\n            return (null, null);\n        }\n\n        string? downloadUrl = null;\n        if (root.TryGetProperty(\"assets\", out JsonElement assetsElement) &&\n            assetsElement.ValueKind == JsonValueKind.Array &&\n            assetsElement.GetArrayLength() > 0)\n        {\n            JsonElement firstAsset = assetsElement[0];\n            if (firstAsset.TryGetProperty(\"browser_download_url\", out JsonElement downloadUrlElement))\n            {\n                downloadUrl = downloadUrlElement.GetString();\n            }\n        }\n\n        return (latestVersion, downloadUrl);\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/WindowsMiniWidgetService.cs",
    "content": "using System;\nusing Avalonia;\nusing Avalonia.Controls;\nusing OpenNetMeter.ViewModels;\nusing OpenNetMeter.Views;\nusing OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class WindowsMiniWidgetService : IMiniWidgetService\n{\n    private readonly Window mainWindow;\n    private readonly MiniWidgetViewModel viewModel;\n    private readonly MiniWidgetWindow window;\n    private readonly WindowsWidgetZOrderHelper zOrderHelper;\n    private bool positionTrackingEnabled;\n    private bool restoringPosition;\n\n    public event Action<bool>? VisibilityChanged;\n\n    public WindowsMiniWidgetService(MiniWidgetViewModel viewModel, Window mainWindow)\n    {\n        this.mainWindow = mainWindow;\n        this.viewModel = viewModel;\n        window = new MiniWidgetWindow\n        {\n            DataContext = viewModel\n        };\n        zOrderHelper = new WindowsWidgetZOrderHelper(window);\n\n        viewModel.SetActions(OpenMainWindow, Hide);\n        window.Opened += Window_Opened;\n        window.PositionChanged += Window_PositionChanged;\n    }\n\n    public void Show()\n    {\n        try\n        {\n            if (!window.IsVisible)\n                window.Show();\n            else\n                window.Activate();\n\n            zOrderHelper.Start();\n            VisibilityChanged?.Invoke(true);\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to show mini widget window\", ex);\n        }\n    }\n\n    public void Hide()\n    {\n        try\n        {\n            if (window.IsVisible)\n                window.Hide();\n\n            zOrderHelper.Stop();\n            VisibilityChanged?.Invoke(false);\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to hide mini widget window\", ex);\n        }\n    }\n\n    public void RefreshAppearance(bool darkMode, int transparency)\n    {\n        viewModel.RefreshBackground(darkMode, transparency);\n    }\n\n    public void ResetPosition(Window mainWindow)\n    {\n        try\n        {\n            restoringPosition = true;\n\n            var mainWidth = Math.Max(1, (int)Math.Round(mainWindow.Bounds.Width));\n            var mainHeight = Math.Max(1, (int)Math.Round(mainWindow.Bounds.Height));\n            var widgetWidth = Math.Max(1, (int)Math.Round(window.Bounds.Width > 0 ? window.Bounds.Width : window.Width));\n            var widgetHeight = Math.Max(1, (int)Math.Round(window.Bounds.Height > 0 ? window.Bounds.Height : window.Height));\n\n            var x = mainWindow.Position.X + (mainWidth / 2) - (widgetWidth / 2);\n            var y = mainWindow.Position.Y + (mainHeight / 2) - (widgetHeight / 2);\n\n            window.Position = new PixelPoint(x, y);\n            SaveWindowPosition();\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to reset mini widget window position\", ex);\n        }\n        finally\n        {\n            restoringPosition = false;\n        }\n    }\n\n    public void EnsurePositionOnScreen(Window mainWindow)\n    {\n        try\n        {\n            if (!SettingsManager.Current.MiniWidgetPositionInitialized || !IsWindowInBounds(window))\n                ResetPosition(mainWindow);\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to validate mini widget window position\", ex);\n        }\n    }\n\n    public void Dispose()\n    {\n        try\n        {\n            zOrderHelper.Dispose();\n            window.Close();\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to dispose mini widget window\", ex);\n        }\n    }\n\n    private void OpenMainWindow()\n    {\n        try\n        {\n            if (!mainWindow.IsVisible)\n                mainWindow.Show();\n\n            if (mainWindow.WindowState == WindowState.Minimized)\n                mainWindow.WindowState = WindowState.Normal;\n\n            mainWindow.Activate();\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to open main window from mini widget\", ex);\n        }\n    }\n\n    private void Window_Opened(object? sender, EventArgs e)\n    {\n        try\n        {\n            if (SettingsManager.Current.MiniWidgetPositionInitialized)\n            {\n                restoringPosition = true;\n                window.Position = new PixelPoint(SettingsManager.Current.MiniWidgetPosX, SettingsManager.Current.MiniWidgetPosY);\n                restoringPosition = false;\n            }\n            else\n            {\n                SaveWindowPosition();\n            }\n\n            positionTrackingEnabled = true;\n        }\n        catch (Exception ex)\n        {\n            restoringPosition = false;\n            EventLogger.Error(\"Failed to restore mini widget window position\", ex);\n        }\n    }\n\n    private void Window_PositionChanged(object? sender, PixelPointEventArgs e)\n    {\n        if (restoringPosition || !positionTrackingEnabled)\n            return;\n\n        try\n        {\n            SaveWindowPosition();\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to save mini widget window position\", ex);\n        }\n    }\n\n    private void SaveWindowPosition()\n    {\n        SettingsManager.Current.MiniWidgetPosX = window.Position.X;\n        SettingsManager.Current.MiniWidgetPosY = window.Position.Y;\n        SettingsManager.Current.MiniWidgetPositionInitialized = true;\n        SettingsManager.Save();\n    }\n\n    private static bool IsWindowInBounds(Window target)\n    {\n        var screens = target.Screens?.All;\n        if (screens is null || screens.Count == 0)\n            return true;\n\n        var width = Math.Max(1, (int)Math.Round(target.Bounds.Width > 0 ? target.Bounds.Width : target.Width));\n        var height = Math.Max(1, (int)Math.Round(target.Bounds.Height > 0 ? target.Bounds.Height : target.Height));\n        const int margin = 32;\n\n        foreach (var screen in screens)\n        {\n            // Use full screen bounds here, not WorkingArea, so a widget intentionally\n            // positioned over the taskbar is still considered a valid persisted position.\n            var area = screen.Bounds;\n            var areaRight = area.X + area.Width;\n            var areaBottom = area.Y + area.Height;\n            var targetRight = target.Position.X + width;\n            var targetBottom = target.Position.Y + height;\n\n            if (area.X < targetRight - margin &&\n                areaRight > target.Position.X + margin &&\n                area.Y < targetBottom - margin &&\n                areaBottom > target.Position.Y + margin)\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/WindowsNetworkCaptureService.cs",
    "content": "using System;\nusing System.ComponentModel;\nusing System.Runtime.Versioning;\nusing OpenNetMeter.Models;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Services;\n\n[SupportedOSPlatform(\"windows\")]\npublic sealed class WindowsNetworkCaptureService : INetworkCaptureService\n{\n    private NetworkProcess? networkProcess;\n    private readonly object syncLock = new();\n    private bool disposed;\n\n    public event EventHandler<NetworkSnapshotChangedEventArgs>? NetworkChanged;\n    public event EventHandler<NetworkTrafficEventArgs>? TrafficObserved;\n\n    public void Start()\n    {\n        lock (syncLock)\n        {\n            ThrowIfDisposed();\n            if (networkProcess != null)\n                return;\n\n            networkProcess = new NetworkProcess();\n            networkProcess.PropertyChanged += NetworkProcess_PropertyChanged;\n            networkProcess.Initialize();\n        }\n    }\n\n    public void Stop()\n    {\n        lock (syncLock)\n        {\n            if (networkProcess == null)\n                return;\n\n            networkProcess.PropertyChanged -= NetworkProcess_PropertyChanged;\n            networkProcess.Dispose();\n            networkProcess = null;\n        }\n    }\n\n    public void Dispose()\n    {\n        lock (syncLock)\n        {\n            if (disposed)\n                return;\n\n            Stop();\n            disposed = true;\n        }\n    }\n\n    private void NetworkProcess_PropertyChanged(object? sender, PropertyChangedEventArgs e)\n    {\n        if (networkProcess == null)\n            return;\n\n        switch (e.PropertyName)\n        {\n            case nameof(NetworkProcess.IsNetworkOnline):\n                var isDisconnected = string.Equals(\n                    networkProcess.IsNetworkOnline,\n                    \"Disconnected\",\n                    StringComparison.OrdinalIgnoreCase);\n\n                NetworkChanged?.Invoke(\n                    this,\n                    new NetworkSnapshotChangedEventArgs(\n                        isDisconnected ? string.Empty : networkProcess.IsNetworkOnline,\n                        isDisconnected ? string.Empty : networkProcess.CurrentAdapterId));\n                break;\n            case nameof(NetworkProcess.DownloadSpeed):\n                EmitProcessTraffic();\n                break;\n        }\n    }\n\n    private void EmitProcessTraffic()\n    {\n        if (networkProcess == null)\n            return;\n\n        networkProcess.IsBufferTime = true;\n        lock (networkProcess.MyProcesses)\n        {\n            foreach (var app in networkProcess.MyProcesses)\n            {\n                if (app.Value == null)\n                    continue;\n\n                if (app.Value.CurrentDataRecv > 0)\n                    TrafficObserved?.Invoke(this, new NetworkTrafficEventArgs(app.Key, app.Value.CurrentDataRecv, isReceive: true));\n\n                if (app.Value.CurrentDataSend > 0)\n                    TrafficObserved?.Invoke(this, new NetworkTrafficEventArgs(app.Key, app.Value.CurrentDataSend, isReceive: false));\n\n                StageForDatabase(app.Key, app.Value.CurrentDataRecv, app.Value.CurrentDataSend);\n            }\n\n            networkProcess.MyProcesses.Clear();\n        }\n\n        networkProcess.IsBufferTime = false;\n        lock (networkProcess.MyProcessesBuffer)\n        {\n            foreach (var app in networkProcess.MyProcessesBuffer)\n            {\n                if (app.Value == null)\n                    continue;\n\n                if (app.Value.CurrentDataRecv > 0)\n                    TrafficObserved?.Invoke(this, new NetworkTrafficEventArgs(app.Key, app.Value.CurrentDataRecv, isReceive: true));\n\n                if (app.Value.CurrentDataSend > 0)\n                    TrafficObserved?.Invoke(this, new NetworkTrafficEventArgs(app.Key, app.Value.CurrentDataSend, isReceive: false));\n\n                StageForDatabase(app.Key, app.Value.CurrentDataRecv, app.Value.CurrentDataSend);\n            }\n\n            networkProcess.MyProcessesBuffer.Clear();\n        }\n    }\n\n    private void StageForDatabase(string processName, long receivedBytes, long sentBytes)\n    {\n        if (networkProcess == null)\n            return;\n\n        if (receivedBytes <= 0 && sentBytes <= 0)\n            return;\n\n        lock (networkProcess.PushToDBBuffer)\n        {\n            if (!networkProcess.PushToDBBuffer.TryGetValue(processName, out var pending) || pending == null)\n            {\n                pending = new MyProcess_Small(processName, 0, 0);\n                networkProcess.PushToDBBuffer[processName] = pending;\n            }\n\n            pending.CurrentDataRecv += receivedBytes;\n            pending.CurrentDataSend += sentBytes;\n        }\n    }\n\n    private void ThrowIfDisposed()\n    {\n        if (disposed)\n            throw new ObjectDisposedException(nameof(WindowsNetworkCaptureService));\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/WindowsProcessIconService.cs",
    "content": "using System;\nusing System.Collections.Concurrent;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.IO;\nusing System.Runtime.Versioning;\nusing Avalonia.Media.Imaging;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMeter.Utilities;\nusing AvaloniaBitmap = Avalonia.Media.Imaging.Bitmap;\n\nnamespace OpenNetMeter.Services;\n\n[SupportedOSPlatform(\"windows\")]\npublic sealed class WindowsProcessIconService : IProcessIconService\n{\n    private readonly ConcurrentDictionary<string, AvaloniaBitmap?> cache = new(StringComparer.OrdinalIgnoreCase);\n    private static readonly AvaloniaBitmap? DefaultIcon = CreateDefaultIcon();\n\n    public object? GetProcessIcon(string processName)\n    {\n        if (string.IsNullOrWhiteSpace(processName))\n            return DefaultIcon;\n\n        return cache.GetOrAdd(processName, LoadIcon);\n    }\n\n    private static AvaloniaBitmap? LoadIcon(string processName)\n    {\n        var nameWithoutExtension = Path.GetFileNameWithoutExtension(processName);\n        if (string.IsNullOrWhiteSpace(nameWithoutExtension))\n            return DefaultIcon;\n\n        try\n        {\n            var processes = Process.GetProcessesByName(nameWithoutExtension);\n            try\n            {\n                foreach (var process in processes)\n                {\n                    try\n                    {\n                        var path = process.MainModule?.FileName;\n                        if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))\n                            continue;\n\n                        using Icon? icon = Icon.ExtractAssociatedIcon(path);\n                        if (icon == null)\n                            continue;\n\n                        using var bitmap = icon.ToBitmap();\n                        using var ms = new MemoryStream();\n                        bitmap.Save(ms, ImageFormat.Png);\n                        ms.Position = 0;\n                        return new AvaloniaBitmap(ms);\n                    }\n                    catch (Exception ex)\n                    {\n                        EventLogger.Error($\"Failed to fetch icon for process '{processName}' from candidate instance\", ex);\n                    }\n                }\n            }\n            finally\n            {\n                foreach (var process in processes)\n                    process.Dispose();\n            }\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error($\"Failed to resolve process icon for '{processName}'\", ex);\n        }\n\n        return DefaultIcon;\n    }\n\n    private static AvaloniaBitmap? CreateDefaultIcon()\n    {\n        try\n        {\n            using var icon = (Icon)SystemIcons.Application.Clone();\n            using var bitmap = icon.ToBitmap();\n            using var ms = new MemoryStream();\n            bitmap.Save(ms, ImageFormat.Png);\n            ms.Position = 0;\n            return new AvaloniaBitmap(ms);\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to create default process icon\", ex);\n            return null;\n        }\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/WindowsStartupRegistrationService.cs",
    "content": "using System;\nusing System.IO;\nusing System.Runtime.Versioning;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMeter.Utilities;\nusing TaskScheduler = Microsoft.Win32.TaskScheduler;\n\nnamespace OpenNetMeter.Services;\n\n[SupportedOSPlatform(\"windows\")]\npublic sealed class WindowsStartupRegistrationService : IStartupRegistrationService\n{\n    private const string TaskFolder = \"OpenNetMeter\";\n    private const string TaskName = \"OpenNetMeterLogon\";\n\n    public bool IsEnabled()\n    {\n        try\n        {\n            TaskScheduler.TaskFolder folder = TaskScheduler.TaskService.Instance.RootFolder.SubFolders[TaskFolder];\n            return folder.Tasks.Exists(TaskName);\n        }\n        catch\n        {\n            return false;\n        }\n    }\n\n    public void SetEnabled(bool enabled, bool startMinimized)\n    {\n        try\n        {\n            TaskScheduler.TaskFolder folder = TaskScheduler.TaskService.Instance.RootFolder.SubFolders[TaskFolder];\n            if (!enabled)\n            {\n                for (int i = 0; i < folder.Tasks.Count; i++)\n                {\n                    folder.DeleteTask(folder.Tasks[i].Name);\n                }\n\n                TaskScheduler.TaskService.Instance.RootFolder.DeleteFolder(TaskFolder);\n                return;\n            }\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Error while updating startup task registration\", ex);\n        }\n\n        if (enabled)\n        {\n            try\n            {\n                TaskScheduler.TaskService.Instance.RootFolder.CreateFolder(TaskFolder);\n                CreateTask(startMinimized);\n            }\n            catch (Exception ex)\n            {\n                EventLogger.Error(\"Error creating startup task folder/definition\", ex);\n            }\n        }\n    }\n\n    private static void CreateTask(bool startMinimized)\n    {\n        try\n        {\n            TaskScheduler.TaskDefinition td = TaskScheduler.TaskService.Instance.NewTask();\n            td.RegistrationInfo.Description = \"Run OpenNetMeter Avalonia on system log on\";\n            td.Principal.RunLevel = TaskScheduler.TaskRunLevel.Highest;\n            td.Principal.LogonType = TaskScheduler.TaskLogonType.InteractiveToken;\n            td.Settings.DisallowStartIfOnBatteries = false;\n            td.Settings.StopIfGoingOnBatteries = false;\n            td.Settings.Compatibility = TaskScheduler.TaskCompatibility.V2_3;\n\n            TaskScheduler.LogonTrigger logonTrigger = new TaskScheduler.LogonTrigger\n            {\n                Enabled = true,\n                UserId = null\n            };\n            td.Triggers.Add(logonTrigger);\n\n            (string path, string? arguments) = ResolveLaunchCommand(startMinimized);\n\n            TaskScheduler.ExecAction action = new TaskScheduler.ExecAction\n            {\n                Path = path,\n                Arguments = arguments ?? string.Empty\n            };\n\n            td.Actions.Add(action);\n\n            TaskScheduler.TaskService.Instance.RootFolder.SubFolders[TaskFolder].RegisterTaskDefinition(\n                TaskName,\n                td,\n                TaskScheduler.TaskCreation.CreateOrUpdate,\n                null,\n                null,\n                td.Principal.LogonType);\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Error creating startup task\", ex);\n        }\n    }\n\n    private static (string path, string? arguments) ResolveLaunchCommand(bool startMinimized)\n    {\n        string? processPath = Environment.ProcessPath;\n        string minimizedArgument = startMinimized ? \" /StartMinimized\" : string.Empty;\n\n        if (!string.IsNullOrWhiteSpace(processPath) &&\n            !string.Equals(Path.GetFileName(processPath), \"dotnet.exe\", StringComparison.OrdinalIgnoreCase) &&\n            !string.Equals(Path.GetFileName(processPath), \"dotnet\", StringComparison.OrdinalIgnoreCase))\n        {\n            return (processPath, startMinimized ? \"/StartMinimized\" : null);\n        }\n\n        string baseDirectory = AppContext.BaseDirectory;\n        string assemblyFileName = $\"{AppDomain.CurrentDomain.FriendlyName}.dll\";\n        string managedEntryPoint = Path.Combine(baseDirectory, assemblyFileName);\n\n        if (File.Exists(managedEntryPoint))\n        {\n            return (\"dotnet\", $\"\\\"{managedEntryPoint}\\\"{minimizedArgument}\");\n        }\n\n        string fallbackExe = Path.Combine(AppContext.BaseDirectory, \"OpenNetMeter.exe\");\n        return (fallbackExe, startMinimized ? \"/StartMinimized\" : null);\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/WindowsTrayNotificationService.cs",
    "content": "using System;\nusing System.Drawing;\nusing System.Runtime.InteropServices;\nusing System.Runtime.Versioning;\nusing System.Threading.Tasks;\nusing Avalonia.Controls;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.Services;\n\n[SupportedOSPlatform(\"windows\")]\npublic sealed class WindowsTrayNotificationService : ITrayNotificationService\n{\n    private const uint NIM_ADD = 0x00000000;\n    private const uint NIM_MODIFY = 0x00000001;\n    private const uint NIM_DELETE = 0x00000002;\n\n    private const uint NIF_ICON = 0x00000002;\n    private const uint NIF_TIP = 0x00000004;\n    private const uint NIF_INFO = 0x00000010;\n\n    private const uint NIIF_NONE = 0x00000000;\n    private const uint BalloonIconId = 0x4F4E4D;\n\n    private bool hasShownMinimizedNotification;\n    private Icon? balloonIcon;\n\n    public void ShowMinimizedToTrayOnce(Window mainWindow)\n    {\n        if (hasShownMinimizedNotification)\n            return;\n\n        try\n        {\n            var handle = mainWindow.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero;\n            if (handle == IntPtr.Zero)\n                return;\n\n            balloonIcon?.Dispose();\n            balloonIcon = LoadNotificationIcon();\n\n            var addData = CreateNotifyIconData(handle, balloonIcon.Handle);\n            addData.uFlags = NIF_ICON | NIF_TIP;\n            addData.szTip = \"OpenNetMeter\";\n\n            if (!Shell_NotifyIcon(NIM_ADD, ref addData))\n                return;\n\n            var infoData = CreateNotifyIconData(handle, balloonIcon.Handle);\n            infoData.uFlags = NIF_INFO;\n            infoData.szInfo = \"Minimized to system tray\";\n            infoData.szInfoTitle = string.Empty;\n            infoData.dwInfoFlags = NIIF_NONE;\n            infoData.uTimeoutOrVersion = 1000;\n\n            Shell_NotifyIcon(NIM_MODIFY, ref infoData);\n            hasShownMinimizedNotification = true;\n\n            _ = RemoveTemporaryIconAsync(handle);\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to show minimized-to-tray notification\", ex);\n        }\n    }\n\n    public void Dispose()\n    {\n        try\n        {\n            balloonIcon?.Dispose();\n            balloonIcon = null;\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to dispose tray notification icon\", ex);\n        }\n    }\n\n    private async Task RemoveTemporaryIconAsync(IntPtr handle)\n    {\n        try\n        {\n            await Task.Delay(TimeSpan.FromSeconds(5));\n\n            var deleteData = CreateNotifyIconData(handle, IntPtr.Zero);\n            Shell_NotifyIcon(NIM_DELETE, ref deleteData);\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to remove tray notification icon\", ex);\n        }\n    }\n\n    private static Icon LoadNotificationIcon()\n    {\n        string? processPath = Environment.ProcessPath;\n        if (!string.IsNullOrWhiteSpace(processPath) && System.IO.File.Exists(processPath))\n        {\n            var icon = Icon.ExtractAssociatedIcon(processPath);\n            if (icon != null)\n                return (Icon)icon.Clone();\n        }\n\n        return (Icon)SystemIcons.Application.Clone();\n    }\n\n    private static NOTIFYICONDATA CreateNotifyIconData(IntPtr handle, IntPtr iconHandle)\n    {\n        return new NOTIFYICONDATA\n        {\n            cbSize = (uint)Marshal.SizeOf<NOTIFYICONDATA>(),\n            hWnd = handle,\n            uID = BalloonIconId,\n            hIcon = iconHandle\n        };\n    }\n\n    [DllImport(\"shell32.dll\", CharSet = CharSet.Unicode)]\n    private static extern bool Shell_NotifyIcon(uint dwMessage, ref NOTIFYICONDATA lpData);\n\n    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]\n    private struct NOTIFYICONDATA\n    {\n        public uint cbSize;\n        public IntPtr hWnd;\n        public uint uID;\n        public uint uFlags;\n        public uint uCallbackMessage;\n        public IntPtr hIcon;\n\n        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]\n        public string szTip;\n\n        public uint dwState;\n        public uint dwStateMask;\n\n        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]\n        public string szInfo;\n\n        public uint uTimeoutOrVersion;\n\n        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]\n        public string szInfoTitle;\n\n        public uint dwInfoFlags;\n        public Guid guidItem;\n        public IntPtr hBalloonIcon;\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Services/WindowsTrayService.cs",
    "content": "using System;\nusing Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Controls.ApplicationLifetimes;\nusing Avalonia.Platform;\nusing OpenNetMeter.Views;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.Services;\n\npublic sealed class WindowsTrayService : ITrayService\n{\n    private readonly TrayIcon trayIcon;\n\n    public WindowsTrayService(\n        Application application,\n        IClassicDesktopStyleApplicationLifetime desktop,\n        MainWindow mainWindow,\n        IMiniWidgetService miniWidgetService)\n    {\n        var menu = new NativeMenu();\n\n        var resetPositionsItem = new NativeMenuItem(\"Reset all window positions\");\n        resetPositionsItem.Click += (_, _) => mainWindow.ResetWindowPositions();\n        menu.Add(resetPositionsItem);\n\n        var showMiniWidgetItem = new NativeMenuItem(\"Show Mini Widget\");\n        showMiniWidgetItem.Click += (_, _) => miniWidgetService.Show();\n        menu.Add(showMiniWidgetItem);\n\n        menu.Add(new NativeMenuItemSeparator());\n\n        var openItem = new NativeMenuItem(\"Open\");\n        openItem.Click += (_, _) => mainWindow.OpenFromTray();\n        menu.Add(openItem);\n\n        var exitItem = new NativeMenuItem(\"Exit\");\n        exitItem.Click += (_, _) =>\n        {\n            mainWindow.PrepareForExit();\n            desktop.Shutdown();\n        };\n        menu.Add(exitItem);\n\n        using var iconStream = AssetLoader.Open(new Uri(\"avares://OpenNetMeter/Assets/x48.png\"));\n        trayIcon = new TrayIcon\n        {\n            ToolTipText = \"OpenNetMeter\",\n            Menu = menu,\n            Icon = new WindowIcon(iconStream),\n            IsVisible = true\n        };\n\n        trayIcon.Clicked += (_, _) => mainWindow.OpenFromTray();\n        TrayIcon.SetIcons(application, new TrayIcons { trayIcon });\n    }\n\n    public void Dispose()\n    {\n        try\n        {\n            trayIcon.IsVisible = false;\n            trayIcon.Dispose();\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to dispose tray icon\", ex);\n        }\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Services/WindowsWidgetZOrderHelper.cs",
    "content": "using System;\nusing System.Runtime.InteropServices;\nusing Avalonia.Controls;\nusing Avalonia.Threading;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.Services;\n\ninternal sealed class WindowsWidgetZOrderHelper : IDisposable\n{\n    private static readonly IntPtr HwndTopMost = new(-1);\n    private const int GwlpHwndParent = -8;\n    private const string ShellTrayWindowClass = \"Shell_TrayWnd\";\n\n    private readonly Window window;\n    private readonly DispatcherTimer timer;\n\n    public WindowsWidgetZOrderHelper(Window window)\n    {\n        this.window = window;\n        timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };\n        timer.Tick += Timer_Tick;\n    }\n\n    public void Start()\n    {\n        timer.Start();\n    }\n\n    public void Stop()\n    {\n        timer.Stop();\n    }\n\n    public void Dispose()\n    {\n        timer.Stop();\n        timer.Tick -= Timer_Tick;\n    }\n\n    private void Timer_Tick(object? sender, EventArgs e)\n    {\n        try\n        {\n            var windowHandle = window.TryGetPlatformHandle()?.Handle ?? IntPtr.Zero;\n            if (windowHandle == IntPtr.Zero)\n                return;\n\n            var shellTray = FindWindowEx(IntPtr.Zero, IntPtr.Zero, ShellTrayWindowClass, string.Empty);\n            if (shellTray == IntPtr.Zero)\n                return;\n\n            var owner = GetWindowLongPtr(windowHandle, GwlpHwndParent);\n            if (owner != shellTray)\n                SetWindowLongPtr(windowHandle, GwlpHwndParent, shellTray);\n\n            for (var current = windowHandle; current != IntPtr.Zero; current = GetWindow(current, 3))\n            {\n                if (current != shellTray)\n                    continue;\n\n                SetWindowPos(\n                    windowHandle,\n                    HwndTopMost,\n                    0,\n                    0,\n                    0,\n                    0,\n                    0x4000 | 0x0010 | 0x0002 | 0x0001);\n                break;\n            }\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Failed to maintain mini widget z-order\", ex);\n        }\n    }\n\n    [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n    private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);\n\n    [DllImport(\"user32.dll\", SetLastError = true)]\n    private static extern IntPtr GetWindow(IntPtr hwnd, uint cmd);\n\n    [DllImport(\"user32.dll\", EntryPoint = \"SetWindowPos\")]\n    private static extern IntPtr SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);\n\n    [DllImport(\"user32.dll\", EntryPoint = \"GetWindowLongPtrW\", SetLastError = true)]\n    private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);\n\n    [DllImport(\"user32.dll\", EntryPoint = \"GetWindowLongW\", SetLastError = true)]\n    private static extern int GetWindowLong32(IntPtr hWnd, int nIndex);\n\n    [DllImport(\"user32.dll\", EntryPoint = \"SetWindowLongPtrW\", SetLastError = true)]\n    private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);\n\n    [DllImport(\"user32.dll\", EntryPoint = \"SetWindowLongW\", SetLastError = true)]\n    private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, int dwNewLong);\n\n    private static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)\n    {\n        return IntPtr.Size == 8\n            ? GetWindowLongPtr64(hWnd, nIndex)\n            : new IntPtr(GetWindowLong32(hWnd, nIndex));\n    }\n\n    private static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)\n    {\n        return IntPtr.Size == 8\n            ? SetWindowLongPtr64(hWnd, nIndex, dwNewLong)\n            : new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong.ToInt32()));\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/ViewModels/ByteSizeFormatter.cs",
    "content": "namespace OpenNetMeter.ViewModels;\n\ninternal static class ByteSizeFormatter\n{\n    public static string FormatBytes(long value)\n    {\n        string[] suffix = { \"B\", \"KB\", \"MB\", \"GB\", \"TB\" };\n        var current = (double)value;\n        var unit = 0;\n\n        while (current >= 1024 && unit < suffix.Length - 1)\n        {\n            current /= 1024;\n            unit++;\n        }\n\n        return $\"{current:0.##} {suffix[unit]}\";\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/ViewModels/HistoryViewModel.cs",
    "content": "using System;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\nusing System.Windows.Input;\nusing Avalonia.Media;\nusing Microsoft.Data.Sqlite;\nusing OpenNetMeter.Models;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.ViewModels;\n\npublic sealed class HistoryViewModel : INotifyPropertyChanged\n{\n    private readonly string dbPath;\n    private readonly IProcessIconService processIconService;\n    private string? selectedProfile;\n    private DateTimeOffset? dateStart;\n    private DateTimeOffset? dateEnd;\n    private long totalDownload;\n    private long totalUpload;\n    private string? currentSortColumn;\n    private bool sortDescending;\n\n    public HistoryViewModel()\n        : this(new NoOpProcessIconService())\n    {\n    }\n\n    public HistoryViewModel(IProcessIconService processIconService)\n    {\n        this.processIconService = processIconService;\n        dbPath = ResolveDatabasePath();\n\n        Profiles = [];\n        Rows = [];\n        FilterCommand = new RelayCommand(ApplyFilter);\n        SortRowsCommand = new ParameterRelayCommand(parameter =>\n        {\n            var column = parameter?.ToString();\n            if (string.IsNullOrWhiteSpace(column))\n                return;\n\n            SortRows(column);\n        });\n\n        DateStart = DateTimeOffset.Now.Date.AddDays(-7);\n        DateEnd = DateTimeOffset.Now.Date;\n\n        LoadProfiles();\n        if (Profiles.Count > 0)\n        {\n            SelectedProfile = Profiles[0];\n            ApplyFilter();\n        }\n    }\n\n    public ObservableCollection<string> Profiles { get; }\n\n    public string? SelectedProfile\n    {\n        get => selectedProfile;\n        set\n        {\n            if (selectedProfile == value)\n                return;\n            selectedProfile = value;\n            OnPropertyChanged(nameof(SelectedProfile));\n        }\n    }\n\n    public DateTimeOffset? DateStart\n    {\n        get => dateStart;\n        set\n        {\n            if (dateStart == value)\n                return;\n            dateStart = value;\n            OnPropertyChanged(nameof(DateStart));\n        }\n    }\n\n    public DateTimeOffset? DateEnd\n    {\n        get => dateEnd;\n        set\n        {\n            if (dateEnd == value)\n                return;\n            dateEnd = value;\n            OnPropertyChanged(nameof(DateEnd));\n        }\n    }\n\n    public ObservableCollection<HistoryRowViewModel> Rows { get; }\n\n    public long TotalDownload\n    {\n        get => totalDownload;\n        private set\n        {\n            if (totalDownload == value)\n                return;\n            totalDownload = value;\n            OnPropertyChanged(nameof(TotalDownload));\n            OnPropertyChanged(nameof(TotalDownloadText));\n        }\n    }\n\n    public long TotalUpload\n    {\n        get => totalUpload;\n        private set\n        {\n            if (totalUpload == value)\n                return;\n            totalUpload = value;\n            OnPropertyChanged(nameof(TotalUpload));\n            OnPropertyChanged(nameof(TotalUploadText));\n        }\n    }\n\n    public string TotalDownloadText => ByteSizeFormatter.FormatBytes(TotalDownload);\n    public string TotalUploadText => ByteSizeFormatter.FormatBytes(TotalUpload);\n\n    public ICommand FilterCommand { get; }\n    public ICommand SortRowsCommand { get; }\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n\n    public void ReloadProfiles()\n    {\n        string? previousSelection = SelectedProfile;\n        LoadProfiles();\n\n        if (!string.IsNullOrWhiteSpace(previousSelection) && Profiles.Contains(previousSelection))\n        {\n            SelectedProfile = previousSelection;\n        }\n        else\n        {\n            SelectedProfile = Profiles.Count > 0 ? Profiles[0] : null;\n        }\n    }\n\n    public void DeleteAllDbFiles()\n    {\n        try\n        {\n            ApplicationDB.CloseSharedConnection();\n            DeleteIfExists(dbPath);\n            DeleteIfExists($\"{dbPath}-wal\");\n            DeleteIfExists($\"{dbPath}-shm\");\n            DeleteIfExists($\"{dbPath}-journal\");\n\n            Profiles.Clear();\n            Rows.Clear();\n            SelectedProfile = null;\n            TotalDownload = 0;\n            TotalUpload = 0;\n        }\n        catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)\n        {\n            EventLogger.Error(\"Failed to delete usage database file\", ex);\n        }\n    }\n\n    private void LoadProfiles()\n    {\n        Profiles.Clear();\n\n        if (!File.Exists(dbPath))\n            return;\n\n        using var connection = OpenReadOnlyConnection(dbPath);\n        connection.Open();\n        using var command = connection.CreateCommand();\n        command.CommandText = \"SELECT Name FROM Adapter ORDER BY Name\";\n        using var reader = command.ExecuteReader();\n        while (reader.Read())\n        {\n            if (!reader.IsDBNull(0))\n                Profiles.Add(reader.GetString(0));\n        }\n    }\n\n    private void ApplyFilter()\n    {\n        Rows.Clear();\n        TotalDownload = 0;\n        TotalUpload = 0;\n\n        if (string.IsNullOrWhiteSpace(SelectedProfile) || !File.Exists(dbPath))\n            return;\n\n        var start = (DateStart ?? DateTimeOffset.Now.Date).Date;\n        var end = (DateEnd ?? DateTimeOffset.Now.Date).Date;\n        if (end < start)\n            (start, end) = (end, start);\n\n        using var connection = OpenReadOnlyConnection(dbPath);\n        connection.Open();\n\n        using var command = connection.CreateCommand();\n        command.CommandText =\n            \"SELECT p.Name, SUM(pd.DataReceived) AS TotalRecv, SUM(pd.DataSent) AS TotalSent \" +\n            \"FROM ProcessDate pd \" +\n            \"JOIN Process p ON p.ID = pd.ProcessID \" +\n            \"JOIN Adapter a ON a.ID = pd.AdapterID \" +\n            \"JOIN Date d ON d.ID = pd.DateID \" +\n            \"WHERE a.Name = @AdapterName \" +\n            \"AND (d.Year * 10000 + d.Month * 100 + d.Day) BETWEEN @StartDate AND @EndDate \" +\n            \"GROUP BY p.ID, p.Name \" +\n            \"ORDER BY p.Name\";\n        command.Parameters.AddWithValue(\"@AdapterName\", SelectedProfile);\n        command.Parameters.AddWithValue(\"@StartDate\", ToDateInt(start));\n        command.Parameters.AddWithValue(\"@EndDate\", ToDateInt(end));\n\n        using var reader = command.ExecuteReader();\n        while (reader.Read())\n        {\n            var processName = reader.IsDBNull(0) ? string.Empty : reader.GetString(0);\n            var download = reader.IsDBNull(1) ? 0 : reader.GetInt64(1);\n            var upload = reader.IsDBNull(2) ? 0 : reader.GetInt64(2);\n\n            var icon = processIconService.GetProcessIcon(processName) as IImage;\n            Rows.Add(new HistoryRowViewModel(processName, download, upload, icon));\n            TotalDownload += download;\n            TotalUpload += upload;\n        }\n    }\n\n    private void SortRows(string column)\n    {\n        if (string.Equals(currentSortColumn, column, StringComparison.Ordinal))\n        {\n            sortDescending = !sortDescending;\n        }\n        else\n        {\n            currentSortColumn = column;\n            sortDescending = false;\n        }\n\n        var sorted = column switch\n        {\n            \"Download\" => sortDescending\n                ? Rows.OrderByDescending(r => r.DownloadBytes).ToList()\n                : Rows.OrderBy(r => r.DownloadBytes).ToList(),\n            \"Upload\" => sortDescending\n                ? Rows.OrderByDescending(r => r.UploadBytes).ToList()\n                : Rows.OrderBy(r => r.UploadBytes).ToList(),\n            _ => sortDescending\n                ? Rows.OrderByDescending(r => r.ProcessName).ToList()\n                : Rows.OrderBy(r => r.ProcessName).ToList()\n        };\n\n        Rows.Clear();\n        foreach (var row in sorted)\n            Rows.Add(row);\n    }\n\n    private static SqliteConnection OpenReadOnlyConnection(string path)\n    {\n        var csb = new SqliteConnectionStringBuilder\n        {\n            DataSource = path,\n            Mode = SqliteOpenMode.ReadOnly\n        };\n        return new SqliteConnection(csb.ToString());\n    }\n\n    private static int ToDateInt(DateTime date)\n    {\n        return (date.Year * 10000) + (date.Month * 100) + date.Day;\n    }\n\n    private static string ResolveDatabasePath()\n    {\n        var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\n        var appFolder = Path.Combine(localAppData, \"OpenNetMeter\");\n        return Path.Combine(appFolder, \"OpenNetMeter.sqlite\");\n    }\n\n    private static void DeleteIfExists(string path)\n    {\n        if (File.Exists(path))\n            File.Delete(path);\n    }\n\n    private void OnPropertyChanged(string propertyName)\n    {\n        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n    }\n\n    private sealed class NoOpProcessIconService : IProcessIconService\n    {\n        public object? GetProcessIcon(string processName) => null;\n    }\n}\n\npublic sealed class HistoryRowViewModel\n{\n    public HistoryRowViewModel(string processName, long downloadBytes, long uploadBytes, IImage? icon = null)\n    {\n        ProcessName = processName;\n        DownloadBytes = downloadBytes;\n        UploadBytes = uploadBytes;\n        Icon = icon;\n    }\n\n    public IImage? Icon { get; }\n    public string ProcessName { get; }\n    public long DownloadBytes { get; }\n    public long UploadBytes { get; }\n    public string DownloadText => ByteSizeFormatter.FormatBytes(DownloadBytes);\n    public string UploadText => ByteSizeFormatter.FormatBytes(UploadBytes);\n}\n\n"
  },
  {
    "path": "OpenNetMeter/ViewModels/MainWindowViewModel.cs",
    "content": "using System;\nusing System.Reflection;\nusing System.Windows.Input;\nusing OpenNetMeter.Services;\nusing OpenNetMeter.Core.ViewModels;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.ViewModels;\n\npublic sealed class MainWindowViewModel : MainShellTabsViewModel, IDisposable\n{\n    private const string AboutRepositoryUri = \"https://github.com/Ashfaaq18/OpenNetMeter\";\n\n    private readonly IWindowService windowService;\n    private readonly INetworkCaptureService networkCaptureService;\n    private readonly IExternalLinkService externalLinkService;\n    private readonly MiniWidgetViewModel miniWidget;\n    private string networkStatus = \"Disconnected\";\n    private bool isAboutOpen;\n\n    public MainWindowViewModel()\n        : this(new NoOpWindowService(), new NoOpNetworkCaptureService(), new NoOpProcessIconService(), new NoOpExternalLinkService(), new MiniWidgetViewModel(), new PlaceholderMiniWidgetService(), new PlaceholderStartupRegistrationService(), new NoOpThemeService())\n    {\n    }\n\n    public MainWindowViewModel(\n        IWindowService windowService,\n        INetworkCaptureService networkCaptureService,\n        IProcessIconService processIconService,\n        IExternalLinkService externalLinkService,\n        MiniWidgetViewModel miniWidget,\n        IMiniWidgetService miniWidgetService,\n        IStartupRegistrationService startupRegistrationService,\n        IThemeService themeService)\n    {\n        this.windowService = windowService;\n        this.networkCaptureService = networkCaptureService;\n        this.externalLinkService = externalLinkService;\n        this.miniWidget = miniWidget;\n\n        Summary = new SummaryViewModel(this.networkCaptureService, processIconService);\n        History = new HistoryViewModel(processIconService);\n        Settings = new SettingsViewModel(miniWidget, miniWidgetService, startupRegistrationService, externalLinkService, themeService);\n        Settings.PropertyChanged += Settings_PropertyChanged;\n        Settings.DeleteAllDataConfirmed += Settings_DeleteAllDataConfirmed;\n        Summary.PropertyChanged += Summary_PropertyChanged;\n\n        SwitchTabCommand = new ParameterRelayCommand(parameter =>\n        {\n            if (parameter is null)\n                return;\n\n            if (int.TryParse(parameter.ToString(), out var nextIndex))\n                SelectedTabIndex = nextIndex;\n        });\n\n        AboutCommand = new RelayCommand(() => IsAboutOpen = true);\n        CloseAboutCommand = new RelayCommand(() => IsAboutOpen = false);\n        OpenAboutRepositoryCommand = new RelayCommand(() => this.externalLinkService.Open(AboutRepositoryUri));\n        MinimizeWindowCommand = new RelayCommand(() => this.windowService.MinimizeMainWindow());\n        CloseWindowCommand = new RelayCommand(() => this.windowService.CloseMainWindow());\n\n        this.networkCaptureService.NetworkChanged += OnNetworkChanged;\n        this.networkCaptureService.Start();\n        History.ReloadProfiles();\n        SyncMiniWidgetFromSummary();\n    }\n\n    public SummaryViewModel Summary { get; }\n    public HistoryViewModel History { get; }\n    public SettingsViewModel Settings { get; }\n    public string AboutVersionText { get; } = $\"Version: {Assembly.GetExecutingAssembly()?.GetName().Version}\";\n    public string AboutRepositoryUrl { get; } = AboutRepositoryUri;\n\n    public ICommand SwitchTabCommand { get; }\n    public ICommand AboutCommand { get; }\n    public ICommand CloseAboutCommand { get; }\n    public ICommand OpenAboutRepositoryCommand { get; }\n    public ICommand MinimizeWindowCommand { get; }\n    public ICommand CloseWindowCommand { get; }\n\n    public bool IsSummaryTab => SelectedTabIndex == 0;\n    public bool IsHistoryTab => SelectedTabIndex == 1;\n    public bool IsSettingsTab => SelectedTabIndex == 2;\n\n    public string NetworkStatus\n    {\n        get => networkStatus;\n        private set\n        {\n            if (networkStatus == value)\n                return;\n            networkStatus = value;\n            OnPropertyChanged(nameof(NetworkStatus));\n        }\n    }\n\n    public bool IsAboutOpen\n    {\n        get => isAboutOpen;\n        private set\n        {\n            if (isAboutOpen == value)\n                return;\n\n            isAboutOpen = value;\n            OnPropertyChanged(nameof(IsAboutOpen));\n        }\n    }\n\n    public override int SelectedTabIndex\n    {\n        get => base.SelectedTabIndex;\n        set\n        {\n            if (base.SelectedTabIndex == value)\n                return;\n\n            base.SelectedTabIndex = value;\n            OnPropertyChanged(nameof(IsSummaryTab));\n            OnPropertyChanged(nameof(IsHistoryTab));\n            OnPropertyChanged(nameof(IsSettingsTab));\n        }\n    }\n\n    public void Dispose()\n    {\n        Settings.PropertyChanged -= Settings_PropertyChanged;\n        Settings.DeleteAllDataConfirmed -= Settings_DeleteAllDataConfirmed;\n        Summary.PropertyChanged -= Summary_PropertyChanged;\n        Summary.Dispose();\n        networkCaptureService.NetworkChanged -= OnNetworkChanged;\n        networkCaptureService.Dispose();\n    }\n\n    private void OnNetworkChanged(object? sender, NetworkSnapshotChangedEventArgs e)\n    {\n        if (string.IsNullOrWhiteSpace(e.AdapterName))\n        {\n            NetworkStatus = \"Disconnected\";\n            Summary.ClearOnDisconnect();\n            return;\n        }\n\n        NetworkStatus = $\"Connected : {e.AdapterName}\";\n        Summary.SetActiveAdapter(e.AdapterName);\n        History.ReloadProfiles();\n    }\n\n    private void Settings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)\n    {\n        if (e.PropertyName == nameof(SettingsViewModel.SelectedSpeedMagnitudeIndex) ||\n            e.PropertyName == nameof(SettingsViewModel.SelectedSpeedUnitIndex))\n        {\n            Summary.RefreshSpeedDisplayFormat();\n        }\n    }\n\n    private void Settings_DeleteAllDataConfirmed()\n    {\n        bool wasConnected = !string.Equals(NetworkStatus, \"Disconnected\", StringComparison.OrdinalIgnoreCase);\n\n        if (wasConnected)\n        {\n            networkCaptureService.Stop();\n            Summary.ClearOnDisconnect();\n            NetworkStatus = \"Disconnected\";\n        }\n\n        History.DeleteAllDbFiles();\n\n        if (wasConnected)\n        {\n            networkCaptureService.Start();\n        }\n\n        History.ReloadProfiles();\n    }\n\n    private void Summary_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)\n    {\n        switch (e.PropertyName)\n        {\n            case nameof(SummaryViewModel.DownloadSpeedText):\n            case nameof(SummaryViewModel.UploadSpeedText):\n            case nameof(SummaryViewModel.CurrentSessionDownloadText):\n            case nameof(SummaryViewModel.CurrentSessionUploadText):\n                SyncMiniWidgetFromSummary();\n                break;\n        }\n    }\n\n    private void SyncMiniWidgetFromSummary()\n    {\n        miniWidget.DownloadSpeedText = Summary.DownloadSpeedText;\n        miniWidget.UploadSpeedText = Summary.UploadSpeedText;\n        miniWidget.CurrentSessionDownloadText = Summary.CurrentSessionDownloadText;\n        miniWidget.CurrentSessionUploadText = Summary.CurrentSessionUploadText;\n    }\n\n    private sealed class NoOpWindowService : IWindowService\n    {\n        public void MinimizeMainWindow() { }\n        public void CloseMainWindow() { }\n        public void ShowAbout() { }\n    }\n\n    private sealed class NoOpNetworkCaptureService : INetworkCaptureService\n    {\n        public event EventHandler<NetworkSnapshotChangedEventArgs>? NetworkChanged\n        {\n            add { }\n            remove { }\n        }\n        public event EventHandler<NetworkTrafficEventArgs>? TrafficObserved\n        {\n            add { }\n            remove { }\n        }\n\n        public void Start() { }\n        public void Stop() { }\n        public void Dispose() { }\n    }\n\n    private sealed class NoOpProcessIconService : IProcessIconService\n    {\n        public object? GetProcessIcon(string processName) => null;\n    }\n\n    private sealed class NoOpExternalLinkService : IExternalLinkService\n    {\n        public void Open(string uri) { }\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/ViewModels/MiniWidgetViewModel.cs",
    "content": "using System;\nusing System.ComponentModel;\nusing System.Windows.Input;\nusing Avalonia.Media;\nusing Avalonia.Media.Imaging;\nusing Avalonia.Platform;\nusing OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.ViewModels;\n\npublic sealed class MiniWidgetViewModel : INotifyPropertyChanged\n{\n    private static readonly Bitmap PinLightImage = LoadBitmap(\"avares://OpenNetMeter/Assets/pin/pin.png\");\n    private static readonly Bitmap PinDarkImage = LoadBitmap(\"avares://OpenNetMeter/Assets/pin/pin-dark.png\");\n    private static readonly Bitmap UnpinLightImage = LoadBitmap(\"avares://OpenNetMeter/Assets/pin/unpin.png\");\n    private static readonly Bitmap UnpinDarkImage = LoadBitmap(\"avares://OpenNetMeter/Assets/pin/unpin-dark.png\");\n\n    private string downloadSpeedText = \"35.2 Mbps\";\n    private string uploadSpeedText = \"4.8 Mbps\";\n    private string currentSessionDownloadText = \"1.24 GB\";\n    private string currentSessionUploadText = \"98.4 MB\";\n    private string backgroundColor = \"#cc252525\";\n    private bool isPinned;\n    private bool darkMode;\n    private ICommand togglePinnedCommand;\n\n    public string DownloadSpeedText\n    {\n        get => downloadSpeedText;\n        set\n        {\n            if (downloadSpeedText == value)\n                return;\n            downloadSpeedText = value;\n            OnPropertyChanged(nameof(DownloadSpeedText));\n        }\n    }\n\n    public string UploadSpeedText\n    {\n        get => uploadSpeedText;\n        set\n        {\n            if (uploadSpeedText == value)\n                return;\n            uploadSpeedText = value;\n            OnPropertyChanged(nameof(UploadSpeedText));\n        }\n    }\n\n    public string CurrentSessionDownloadText\n    {\n        get => currentSessionDownloadText;\n        set\n        {\n            if (currentSessionDownloadText == value)\n                return;\n            currentSessionDownloadText = value;\n            OnPropertyChanged(nameof(CurrentSessionDownloadText));\n        }\n    }\n\n    public string CurrentSessionUploadText\n    {\n        get => currentSessionUploadText;\n        set\n        {\n            if (currentSessionUploadText == value)\n                return;\n            currentSessionUploadText = value;\n            OnPropertyChanged(nameof(CurrentSessionUploadText));\n        }\n    }\n\n    public bool IsPinned\n    {\n        get => isPinned;\n        set\n        {\n            if (isPinned == value)\n                return;\n            isPinned = value;\n            SettingsManager.Current.MiniWidgetPinned = value;\n            SettingsManager.Save();\n            OnPropertyChanged(nameof(IsPinned));\n            OnPropertyChanged(nameof(PinIconSource));\n            OnPropertyChanged(nameof(PinToolTip));\n        }\n    }\n\n    public string BackgroundColor\n    {\n        get => backgroundColor;\n        private set\n        {\n            if (backgroundColor == value)\n                return;\n            backgroundColor = value;\n            OnPropertyChanged(nameof(BackgroundColor));\n        }\n    }\n\n    public IImage PinIconSource => IsPinned\n        ? (darkMode ? UnpinDarkImage : UnpinLightImage)\n        : (darkMode ? PinDarkImage : PinLightImage);\n\n    public string PinToolTip => IsPinned ? \"Unpin\" : \"Pin\";\n\n    public ICommand OpenMainWindowCommand { get; private set; } = new RelayCommand(() => { });\n    public ICommand HideWidgetCommand { get; private set; } = new RelayCommand(() => { });\n    public ICommand TogglePinnedCommand => togglePinnedCommand;\n\n    public MiniWidgetViewModel()\n    {\n        togglePinnedCommand = new RelayCommand(() => IsPinned = !IsPinned);\n        isPinned = SettingsManager.Current.MiniWidgetPinned;\n        RefreshBackground(SettingsManager.Current.DarkMode, SettingsManager.Current.MiniWidgetTransparentSlider);\n    }\n\n    public void SetActions(Action openMainWindow, Action hideWidget)\n    {\n        OpenMainWindowCommand = new RelayCommand(openMainWindow);\n        HideWidgetCommand = new RelayCommand(hideWidget);\n        OnPropertyChanged(nameof(OpenMainWindowCommand));\n        OnPropertyChanged(nameof(HideWidgetCommand));\n    }\n\n    public void RefreshBackground(bool darkMode, int transparency)\n    {\n        var clampedTransparency = Math.Clamp(transparency, 0, 100);\n        var alpha = ((100 - clampedTransparency) * 255) / 100;\n        this.darkMode = darkMode;\n        BackgroundColor = darkMode\n            ? $\"#{alpha:x2}252525\"\n            : $\"#{alpha:x2}f1f1f1\";\n        OnPropertyChanged(nameof(PinIconSource));\n    }\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n\n    private static Bitmap LoadBitmap(string assetUri)\n    {\n        try\n        {\n            using var assetStream = AssetLoader.Open(new Uri(assetUri));\n            return new Bitmap(assetStream);\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error($\"Failed to load mini widget pin asset '{assetUri}'\", ex);\n            throw;\n        }\n    }\n\n    private void OnPropertyChanged(string propertyName) =>\n        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n}\n\n"
  },
  {
    "path": "OpenNetMeter/ViewModels/RelayCommand.cs",
    "content": "using System;\nusing System.Windows.Input;\n\nnamespace OpenNetMeter.ViewModels;\n\npublic sealed class RelayCommand : ICommand\n{\n    private readonly Action execute;\n    private readonly Func<bool>? canExecute;\n\n    public RelayCommand(Action execute, Func<bool>? canExecute = null)\n    {\n        this.execute = execute;\n        this.canExecute = canExecute;\n    }\n\n    public event EventHandler? CanExecuteChanged;\n\n    public bool CanExecute(object? parameter)\n    {\n        return canExecute?.Invoke() ?? true;\n    }\n\n    public void Execute(object? parameter)\n    {\n        execute();\n    }\n\n    public void RaiseCanExecuteChanged()\n    {\n        CanExecuteChanged?.Invoke(this, EventArgs.Empty);\n    }\n}\n\npublic sealed class ParameterRelayCommand : ICommand\n{\n    private readonly Action<object?> execute;\n    private readonly Func<object?, bool>? canExecute;\n\n    public ParameterRelayCommand(Action<object?> execute, Func<object?, bool>? canExecute = null)\n    {\n        this.execute = execute;\n        this.canExecute = canExecute;\n    }\n\n    public event EventHandler? CanExecuteChanged;\n\n    public bool CanExecute(object? parameter)\n    {\n        return canExecute?.Invoke(parameter) ?? true;\n    }\n\n    public void Execute(object? parameter)\n    {\n        execute(parameter);\n    }\n\n    public void RaiseCanExecuteChanged()\n    {\n        CanExecuteChanged?.Invoke(this, EventArgs.Empty);\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/ViewModels/SettingsViewModel.cs",
    "content": "using System;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Reflection;\nusing System.Threading.Tasks;\nusing System.Windows.Input;\nusing OpenNetMeter.Services;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.ViewModels;\n\npublic sealed class SettingsViewModel : INotifyPropertyChanged\n{\n    private readonly IMiniWidgetService miniWidgetService;\n    private readonly IStartupRegistrationService startupRegistrationService;\n    private readonly IExternalLinkService externalLinkService;\n    private readonly IThemeService themeService;\n    private bool startWithWindows;\n    private bool minimizeOnStart;\n    private bool darkMode;\n    private bool miniWidgetVisible = true;\n    private double miniWidgetTransparency = 20;\n    private int selectedNetworkTargetIndex = 2;\n    private int selectedSpeedMagnitudeIndex;\n    private int selectedSpeedUnitIndex;\n    private bool isCheckingForUpdates;\n    private bool isUpdateAvailable;\n    private string updateStatusMessage = \"Click here to check for new updates\";\n    private string downloadUrl = string.Empty;\n    private bool isDeleteConfirmationOpen;\n\n    public SettingsViewModel(MiniWidgetViewModel miniWidgetViewModel, IMiniWidgetService miniWidgetService, IStartupRegistrationService startupRegistrationService, IExternalLinkService externalLinkService, IThemeService themeService)\n    {\n        this.miniWidgetService = miniWidgetService;\n        this.startupRegistrationService = startupRegistrationService;\n        this.externalLinkService = externalLinkService;\n        this.themeService = themeService;\n        var settings = SettingsManager.Current;\n        startWithWindows = settings.StartWithWin;\n        minimizeOnStart = settings.MinimizeOnStart;\n        darkMode = settings.DarkMode;\n        miniWidgetVisible = settings.MiniWidgetVisibility;\n        miniWidgetTransparency = settings.MiniWidgetTransparentSlider;\n        selectedNetworkTargetIndex = settings.NetworkType;\n        selectedSpeedMagnitudeIndex = settings.NetworkSpeedMagnitude;\n        selectedSpeedUnitIndex = settings.NetworkSpeedFormat;\n\n        ResetDataCommand = new RelayCommand(ResetData);\n        CheckForUpdatesCommand = new RelayCommand(async () => await CheckForUpdatesAsync());\n        DownloadUpdateCommand = new RelayCommand(DownloadUpdate);\n        ConfirmDeleteAllDataCommand = new RelayCommand(ConfirmDeleteAllData);\n        CancelDeleteAllDataCommand = new RelayCommand(CancelDeleteAllData);\n\n        this.miniWidgetService.VisibilityChanged += SyncMiniWidgetVisibility;\n        this.themeService.ApplyDarkMode(darkMode);\n        this.miniWidgetService.RefreshAppearance(darkMode, (int)Math.Round(miniWidgetTransparency));\n    }\n\n    public SettingsViewModel()\n        : this(new MiniWidgetViewModel(), new PlaceholderMiniWidgetService(), new PlaceholderStartupRegistrationService(), new NoOpExternalLinkService(), new NoOpThemeService())\n    {\n    }\n\n    public bool StartWithWindows\n    {\n        get => startWithWindows;\n        set\n        {\n            if (startWithWindows == value)\n                return;\n\n            startWithWindows = value;\n            SettingsManager.Current.StartWithWin = value;\n            SettingsManager.Save();\n            startupRegistrationService.SetEnabled(value, MinimizeOnStart);\n            OnPropertyChanged(nameof(StartWithWindows));\n            OnPropertyChanged(nameof(CanChangeMinimizeOnStart));\n        }\n    }\n\n    public bool MinimizeOnStart\n    {\n        get => minimizeOnStart;\n        set\n        {\n            if (minimizeOnStart == value)\n                return;\n            minimizeOnStart = value;\n            SettingsManager.Current.MinimizeOnStart = value;\n            SettingsManager.Save();\n            OnPropertyChanged(nameof(MinimizeOnStart));\n        }\n    }\n\n    public bool CanChangeMinimizeOnStart => !StartWithWindows;\n\n    public bool DarkMode\n    {\n        get => darkMode;\n        set\n        {\n            if (darkMode == value)\n                return;\n            darkMode = value;\n            SettingsManager.Current.DarkMode = value;\n            SettingsManager.Save();\n            themeService.ApplyDarkMode(value);\n            miniWidgetService.RefreshAppearance(value, (int)Math.Round(MiniWidgetTransparency));\n            OnPropertyChanged(nameof(DarkMode));\n        }\n    }\n\n    public bool MiniWidgetVisible\n    {\n        get => miniWidgetVisible;\n        set\n        {\n            if (miniWidgetVisible == value)\n                return;\n            miniWidgetVisible = value;\n            SettingsManager.Current.MiniWidgetVisibility = value;\n            SettingsManager.Save();\n            if (value)\n                miniWidgetService.Show();\n            else\n                miniWidgetService.Hide();\n            OnPropertyChanged(nameof(MiniWidgetVisible));\n        }\n    }\n\n    public double MiniWidgetTransparency\n    {\n        get => miniWidgetTransparency;\n        set\n        {\n            if (miniWidgetTransparency.Equals(value))\n                return;\n            miniWidgetTransparency = value;\n            SettingsManager.Current.MiniWidgetTransparentSlider = (int)Math.Round(value);\n            SettingsManager.Save();\n            miniWidgetService.RefreshAppearance(DarkMode, (int)Math.Round(value));\n            OnPropertyChanged(nameof(MiniWidgetTransparency));\n        }\n    }\n\n    public string[] NetworkTargets { get; } = [\"Private\", \"Public\", \"Both\"];\n\n    public int SelectedNetworkTargetIndex\n    {\n        get => selectedNetworkTargetIndex;\n        set\n        {\n            if (selectedNetworkTargetIndex == value)\n                return;\n            selectedNetworkTargetIndex = value;\n            SettingsManager.Current.NetworkType = value;\n            SettingsManager.Save();\n            OnPropertyChanged(nameof(SelectedNetworkTargetIndex));\n            OnPropertyChanged(nameof(IsNetworkTargetPrivate));\n            OnPropertyChanged(nameof(IsNetworkTargetPublic));\n            OnPropertyChanged(nameof(IsNetworkTargetBoth));\n        }\n    }\n\n    public bool IsNetworkTargetPrivate\n    {\n        get => SelectedNetworkTargetIndex == 0;\n        set\n        {\n            if (value)\n                SelectedNetworkTargetIndex = 0;\n        }\n    }\n\n    public bool IsNetworkTargetPublic\n    {\n        get => SelectedNetworkTargetIndex == 1;\n        set\n        {\n            if (value)\n                SelectedNetworkTargetIndex = 1;\n        }\n    }\n\n    public bool IsNetworkTargetBoth\n    {\n        get => SelectedNetworkTargetIndex == 2;\n        set\n        {\n            if (value)\n                SelectedNetworkTargetIndex = 2;\n        }\n    }\n\n    public string[] SpeedMagnitudes { get; } = [\"Auto\", \"Kilo\", \"Mega\", \"Giga\"];\n\n    public int SelectedSpeedMagnitudeIndex\n    {\n        get => selectedSpeedMagnitudeIndex;\n        set\n        {\n            if (selectedSpeedMagnitudeIndex == value)\n                return;\n            selectedSpeedMagnitudeIndex = value;\n            SettingsManager.Current.NetworkSpeedMagnitude = value;\n            SettingsManager.Save();\n            OnPropertyChanged(nameof(SelectedSpeedMagnitudeIndex));\n        }\n    }\n\n    public string[] SpeedUnits { get; } = [\"bps (bits/sec)\", \"Bps (bytes/sec)\"];\n\n    public int SelectedSpeedUnitIndex\n    {\n        get => selectedSpeedUnitIndex;\n        set\n        {\n            if (selectedSpeedUnitIndex == value)\n                return;\n            selectedSpeedUnitIndex = value;\n            SettingsManager.Current.NetworkSpeedFormat = value;\n            SettingsManager.Save();\n            OnPropertyChanged(nameof(SelectedSpeedUnitIndex));\n        }\n    }\n\n    public bool IsCheckingForUpdates\n    {\n        get => isCheckingForUpdates;\n        private set\n        {\n            if (isCheckingForUpdates == value)\n                return;\n            isCheckingForUpdates = value;\n            OnPropertyChanged(nameof(IsCheckingForUpdates));\n        }\n    }\n\n    public bool IsUpdateAvailable\n    {\n        get => isUpdateAvailable;\n        private set\n        {\n            if (isUpdateAvailable == value)\n                return;\n            isUpdateAvailable = value;\n            OnPropertyChanged(nameof(IsUpdateAvailable));\n        }\n    }\n\n    public string UpdateStatusMessage\n    {\n        get => updateStatusMessage;\n        private set\n        {\n            if (updateStatusMessage == value)\n                return;\n            updateStatusMessage = value;\n            OnPropertyChanged(nameof(UpdateStatusMessage));\n        }\n    }\n\n    public bool IsDeleteConfirmationOpen\n    {\n        get => isDeleteConfirmationOpen;\n        private set\n        {\n            if (isDeleteConfirmationOpen == value)\n                return;\n            isDeleteConfirmationOpen = value;\n            OnPropertyChanged(nameof(IsDeleteConfirmationOpen));\n        }\n    }\n\n    public string DeleteConfirmationMessage { get; } =\n        \"Warning!!! This will delete all saved profiles.\\nDo you still want to continue?\";\n\n    public ICommand ResetDataCommand { get; }\n    public ICommand CheckForUpdatesCommand { get; }\n    public ICommand DownloadUpdateCommand { get; }\n    public ICommand ConfirmDeleteAllDataCommand { get; }\n    public ICommand CancelDeleteAllDataCommand { get; }\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n    public event Action? DeleteAllDataConfirmed;\n\n    public void SyncMiniWidgetVisibility(bool isVisible)\n    {\n        if (miniWidgetVisible == isVisible)\n            return;\n\n        miniWidgetVisible = isVisible;\n        SettingsManager.Current.MiniWidgetVisibility = isVisible;\n        SettingsManager.Save();\n        OnPropertyChanged(nameof(MiniWidgetVisible));\n    }\n\n    private void ResetData()\n    {\n        IsDeleteConfirmationOpen = true;\n    }\n\n    private async Task CheckForUpdatesAsync()\n    {\n        IsCheckingForUpdates = true;\n        UpdateStatusMessage = \"Checking for updates...\";\n        IsUpdateAvailable = false;\n        downloadUrl = string.Empty;\n        string statusMessage = string.Empty;\n\n        const int minDisplayTimeMs = 2000;\n        var stopwatch = Stopwatch.StartNew();\n\n        try\n        {\n            (Version? latestVersion, string? nextDownloadUrl) = await UpdateChecker.CheckForUpdatesAsync();\n            if (latestVersion != null && nextDownloadUrl != null)\n            {\n                Version? currentVersion = Assembly.GetExecutingAssembly()?.GetName()?.Version;\n                if (currentVersion != null && latestVersion > currentVersion)\n                {\n                    downloadUrl = nextDownloadUrl;\n                    statusMessage = $\"A new version {latestVersion} is available!\";\n                    IsUpdateAvailable = true;\n                }\n                else\n                {\n                    statusMessage = \"You have the latest version.\";\n                }\n            }\n            else\n            {\n                statusMessage = \"Error checking for updates.\";\n            }\n        }\n        catch (Exception ex)\n        {\n            statusMessage = \"Error checking for updates.\";\n            EventLogger.Error(\"Error checking for updates\", ex);\n        }\n        finally\n        {\n            stopwatch.Stop();\n\n            int remainingTime = minDisplayTimeMs - (int)stopwatch.ElapsedMilliseconds;\n            if (remainingTime > 0)\n            {\n                await Task.Delay(remainingTime);\n            }\n\n            IsCheckingForUpdates = false;\n            UpdateStatusMessage = statusMessage;\n        }\n    }\n\n    private void DownloadUpdate()\n    {\n        if (string.IsNullOrWhiteSpace(downloadUrl))\n        {\n            UpdateStatusMessage = \"No update download is available.\";\n            return;\n        }\n\n        try\n        {\n            externalLinkService.Open(downloadUrl);\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error(\"Error launching update download URL\", ex);\n            UpdateStatusMessage = \"Error launching update download URL.\";\n        }\n    }\n\n    private void ConfirmDeleteAllData()\n    {\n        IsDeleteConfirmationOpen = false;\n        DeleteAllDataConfirmed?.Invoke();\n    }\n\n    private void CancelDeleteAllData()\n    {\n        IsDeleteConfirmationOpen = false;\n    }\n\n    private void OnPropertyChanged(string propertyName) =>\n        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n\n    private sealed class NoOpExternalLinkService : IExternalLinkService\n    {\n        public void Open(string uri)\n        {\n        }\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/ViewModels/SummaryViewModel.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\nusing System.Windows.Input;\nusing Avalonia.Threading;\nusing Avalonia.Media;\nusing LiveChartsCore;\nusing LiveChartsCore.Defaults;\nusing LiveChartsCore.SkiaSharpView;\nusing LiveChartsCore.SkiaSharpView.Painting;\nusing Microsoft.Data.Sqlite;\nusing OpenNetMeter.Models;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing SkiaSharp;\n\nnamespace OpenNetMeter.ViewModels;\n\npublic sealed class SummaryViewModel : INotifyPropertyChanged, IDisposable\n{\n    private const double GraphLogBase = 10d;\n    private const int MaxSpeedMagnitude = 6;\n    private readonly INetworkCaptureService networkCaptureService;\n    private readonly IProcessIconService processIconService;\n    private readonly ObservableCollection<ObservablePoint> dlValues = new();\n    private readonly ObservableCollection<ObservablePoint> ulValues = new();\n    private readonly Dictionary<string, SummaryProcessRowViewModel> processIndex = new(StringComparer.OrdinalIgnoreCase);\n    private readonly object pendingLock = new();\n    private readonly Dictionary<string, PendingTraffic> pendingByProcess = new(StringComparer.OrdinalIgnoreCase);\n    private readonly DispatcherTimer flushTimer;\n    private string? currentSortColumn;\n    private bool sortDescending;\n    private string activeAdapterName = string.Empty;\n    private long sinceDateDbDownloadBaseline;\n    private long sinceDateDbUploadBaseline;\n    private long sinceDateSessionDownloadBaseline;\n    private long sinceDateSessionUploadBaseline;\n\n    private const int WindowSize = 35;\n    private int tickCount;\n    private long currentSessionDownload;\n    private long currentSessionUpload;\n    private long totalFromDateDownload;\n    private long totalFromDateUpload;\n    private double latestDownloadMbps;\n    private double latestUploadMbps;\n    private DateTimeOffset? sinceDate;\n    private long pendingDownloadBytes;\n    private long pendingUploadBytes;\n    private long latestDownloadBytesPerSecond;\n    private long latestUploadBytesPerSecond;\n    private int graphAxisMagnitude;\n\n    public SummaryViewModel(INetworkCaptureService networkCaptureService, IProcessIconService processIconService)\n    {\n        this.networkCaptureService = networkCaptureService;\n        this.processIconService = processIconService;\n\n        // Match WPF dark theme accents:\n        // Download -> #367061, Upload -> #D98868\n        var dlColor = new SKColor(0x36, 0x70, 0x61);\n        var ulColor = new SKColor(0xD9, 0x88, 0x68);\n\n        GraphSeries =\n        [\n            new LineSeries<ObservablePoint>\n            {\n                Values = dlValues,\n                Stroke = new SolidColorPaint(dlColor, 2),\n                GeometrySize = 0,\n                GeometryStroke = null,\n                GeometryFill = null,\n                Fill = new SolidColorPaint(dlColor.WithAlpha(0x33)),\n                LineSmoothness = 0.3,\n                Name = \"Download\"\n            },\n            new LineSeries<ObservablePoint>\n            {\n                Values = ulValues,\n                Stroke = new SolidColorPaint(ulColor, 2),\n                GeometrySize = 0,\n                GeometryStroke = null,\n                GeometryFill = null,\n                Fill = new SolidColorPaint(ulColor.WithAlpha(0x33)),\n                LineSmoothness = 0.3,\n                Name = \"Upload\"\n            }\n        ];\n\n        GraphXAxes =\n        [\n            new Axis\n            {\n                ShowSeparatorLines = false,\n                IsVisible = false,\n                MinLimit = 0,\n                MaxLimit = WindowSize\n            }\n        ];\n\n        GraphYAxes = CreateGraphYAxes();\n\n        ActiveProcesses = [];\n        SortProcessesCommand = new ParameterRelayCommand(parameter =>\n        {\n            var column = parameter?.ToString();\n            if (string.IsNullOrWhiteSpace(column))\n                return;\n\n            SortProcesses(column);\n        });\n\n        DateMax = DateTime.Today;\n        DateMin = DateMax.AddDays(-ApplicationDB.DataStoragePeriodInDays);\n        sinceDate = DateMax;\n        this.networkCaptureService.TrafficObserved += OnTrafficObserved;\n\n        flushTimer = new DispatcherTimer\n        {\n            Interval = TimeSpan.FromSeconds(1)\n        };\n        flushTimer.Tick += (_, _) => FlushPendingTraffic();\n        flushTimer.Start();\n    }\n\n    public ISeries[] GraphSeries { get; }\n    public Axis[] GraphXAxes { get; }\n    public Axis[] GraphYAxes { get; private set; }\n    public ObservableCollection<SummaryProcessRowViewModel> ActiveProcesses { get; }\n    public ICommand SortProcessesCommand { get; }\n\n    public string CurrentSessionDownloadText => ByteSizeFormatter.FormatBytes(currentSessionDownload);\n    public string CurrentSessionUploadText => ByteSizeFormatter.FormatBytes(currentSessionUpload);\n    public string TotalFromDateDownloadText => ByteSizeFormatter.FormatBytes(totalFromDateDownload);\n    public string TotalFromDateUploadText => ByteSizeFormatter.FormatBytes(totalFromDateUpload);\n    public string DownloadSpeedText => $\"{FormatSpeed(latestDownloadBytesPerSecond)}ps\";\n    public string UploadSpeedText => $\"{FormatSpeed(latestUploadBytesPerSecond)}ps\";\n    public int ProcessCount => ActiveProcesses.Count;\n    public DateTime DateMin { get; }\n    public DateTime DateMax { get; }\n\n    public DateTimeOffset? SinceDate\n    {\n        get => sinceDate;\n        set\n        {\n            var normalized = NormalizeSinceDate(value);\n            if (sinceDate == normalized)\n                return;\n\n            sinceDate = normalized;\n            RefreshSinceDateBaseline();\n            OnPropertyChanged(nameof(SinceDate));\n        }\n    }\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n\n    public void Dispose()\n    {\n        networkCaptureService.TrafficObserved -= OnTrafficObserved;\n        flushTimer.Stop();\n    }\n\n    public void ClearOnDisconnect()\n    {\n        lock (pendingLock)\n        {\n            pendingDownloadBytes = 0;\n            pendingUploadBytes = 0;\n            pendingByProcess.Clear();\n        }\n\n        latestDownloadMbps = 0;\n        latestUploadMbps = 0;\n        latestDownloadBytesPerSecond = 0;\n        latestUploadBytesPerSecond = 0;\n        currentSessionDownload = 0;\n        currentSessionUpload = 0;\n        totalFromDateDownload = 0;\n        totalFromDateUpload = 0;\n        sinceDateDbDownloadBaseline = 0;\n        sinceDateDbUploadBaseline = 0;\n        sinceDateSessionDownloadBaseline = 0;\n        sinceDateSessionUploadBaseline = 0;\n        activeAdapterName = string.Empty;\n\n        dlValues.Clear();\n        ulValues.Clear();\n        tickCount = 0;\n        GraphXAxes[0].MinLimit = 0;\n        GraphXAxes[0].MaxLimit = WindowSize;\n\n        ActiveProcesses.Clear();\n        processIndex.Clear();\n        OnPropertyChanged(nameof(ProcessCount));\n        UpdateGraphAxisLabelScale();\n        OnPropertyChanged(nameof(CurrentSessionDownloadText));\n        OnPropertyChanged(nameof(CurrentSessionUploadText));\n        OnPropertyChanged(nameof(TotalFromDateDownloadText));\n        OnPropertyChanged(nameof(TotalFromDateUploadText));\n        OnPropertyChanged(nameof(DownloadSpeedText));\n        OnPropertyChanged(nameof(UploadSpeedText));\n    }\n\n    public void SetActiveAdapter(string adapterName)\n    {\n        var normalized = adapterName?.Trim() ?? string.Empty;\n        if (string.Equals(activeAdapterName, normalized, StringComparison.Ordinal))\n            return;\n\n        activeAdapterName = normalized;\n        RefreshSinceDateBaseline();\n    }\n\n    private void OnTrafficObserved(object? sender, NetworkTrafficEventArgs e)\n    {\n        lock (pendingLock)\n        {\n            if (!pendingByProcess.TryGetValue(e.ProcessName, out var pending))\n            {\n                pending = new PendingTraffic();\n                pendingByProcess[e.ProcessName] = pending;\n            }\n\n            if (e.IsReceive)\n            {\n                pending.DownloadBytes += e.Bytes;\n                pendingDownloadBytes += e.Bytes;\n            }\n            else\n            {\n                pending.UploadBytes += e.Bytes;\n                pendingUploadBytes += e.Bytes;\n            }\n        }\n    }\n\n    private void FlushPendingTraffic()\n    {\n        Dictionary<string, PendingTraffic> pendingSnapshot;\n        long secondDownloadBytes;\n        long secondUploadBytes;\n\n        lock (pendingLock)\n        {\n            secondDownloadBytes = pendingDownloadBytes;\n            secondUploadBytes = pendingUploadBytes;\n            pendingDownloadBytes = 0;\n            pendingUploadBytes = 0;\n\n            pendingSnapshot = new Dictionary<string, PendingTraffic>(pendingByProcess, StringComparer.OrdinalIgnoreCase);\n            pendingByProcess.Clear();\n        }\n\n        latestDownloadMbps = secondDownloadBytes * 8d / 1_000_000d;\n        latestUploadMbps = secondUploadBytes * 8d / 1_000_000d;\n        latestDownloadBytesPerSecond = secondDownloadBytes;\n        latestUploadBytesPerSecond = secondUploadBytes;\n        currentSessionDownload += secondDownloadBytes;\n        currentSessionUpload += secondUploadBytes;\n        UpdateTotalFromDateFromBaselines();\n\n        AppendGraphPoint();\n        ApplyProcessTick(pendingSnapshot);\n\n        OnPropertyChanged(nameof(CurrentSessionDownloadText));\n        OnPropertyChanged(nameof(CurrentSessionUploadText));\n        OnPropertyChanged(nameof(TotalFromDateDownloadText));\n        OnPropertyChanged(nameof(TotalFromDateUploadText));\n        OnPropertyChanged(nameof(DownloadSpeedText));\n        OnPropertyChanged(nameof(UploadSpeedText));\n    }\n\n    private void RefreshSinceDateBaseline()\n    {\n        sinceDateSessionDownloadBaseline = currentSessionDownload;\n        sinceDateSessionUploadBaseline = currentSessionUpload;\n\n        if (string.IsNullOrWhiteSpace(activeAdapterName))\n        {\n            sinceDateDbDownloadBaseline = 0;\n            sinceDateDbUploadBaseline = 0;\n            totalFromDateDownload = 0;\n            totalFromDateUpload = 0;\n        }\n        else\n        {\n            var fromDate = (sinceDate ?? DateTimeOffset.Now.Date).Date;\n            var totals = ReadDbTotals(activeAdapterName, fromDate, DateTime.Today);\n            sinceDateDbDownloadBaseline = totals.download;\n            sinceDateDbUploadBaseline = totals.upload;\n            UpdateTotalFromDateFromBaselines();\n        }\n\n        OnPropertyChanged(nameof(TotalFromDateDownloadText));\n        OnPropertyChanged(nameof(TotalFromDateUploadText));\n    }\n\n    private void UpdateTotalFromDateFromBaselines()\n    {\n        var sessionDownloadDelta = currentSessionDownload - sinceDateSessionDownloadBaseline;\n        var sessionUploadDelta = currentSessionUpload - sinceDateSessionUploadBaseline;\n\n        if (sessionDownloadDelta < 0)\n            sessionDownloadDelta = 0;\n        if (sessionUploadDelta < 0)\n            sessionUploadDelta = 0;\n\n        totalFromDateDownload = sinceDateDbDownloadBaseline + sessionDownloadDelta;\n        totalFromDateUpload = sinceDateDbUploadBaseline + sessionUploadDelta;\n    }\n\n    private static (long download, long upload) ReadDbTotals(string adapterName, DateTime startDate, DateTime endDate)\n    {\n        try\n        {\n            var dbPath = ResolveDatabasePath();\n            if (!File.Exists(dbPath))\n                return (0, 0);\n\n            var fromDate = startDate.Date;\n            var toDate = endDate.Date;\n            if (toDate < fromDate)\n                (fromDate, toDate) = (toDate, fromDate);\n\n            using var connection = OpenReadOnlyConnection(dbPath);\n            connection.Open();\n            using var command = connection.CreateCommand();\n            command.CommandText =\n                \"SELECT SUM(pd.DataReceived) AS TotalRecv, SUM(pd.DataSent) AS TotalSent \" +\n                \"FROM ProcessDate pd \" +\n                \"JOIN Adapter a ON a.ID = pd.AdapterID \" +\n                \"JOIN Date d ON d.ID = pd.DateID \" +\n                \"WHERE a.Name = @AdapterName \" +\n                \"AND (d.Year * 10000 + d.Month * 100 + d.Day) BETWEEN @StartDate AND @EndDate\";\n            command.Parameters.AddWithValue(\"@AdapterName\", adapterName);\n            command.Parameters.AddWithValue(\"@StartDate\", ToDateInt(fromDate));\n            command.Parameters.AddWithValue(\"@EndDate\", ToDateInt(toDate));\n\n            using var reader = command.ExecuteReader();\n            if (reader.Read())\n            {\n                var download = reader.IsDBNull(0) ? 0 : reader.GetInt64(0);\n                var upload = reader.IsDBNull(1) ? 0 : reader.GetInt64(1);\n                return (download, upload);\n            }\n        }\n        catch (Exception ex)\n        {\n            EventLogger.Error($\"Failed to read summary totals from database for adapter '{adapterName}'\", ex);\n        }\n\n        return (0, 0);\n    }\n\n    private static SqliteConnection OpenReadOnlyConnection(string path)\n    {\n        var csb = new SqliteConnectionStringBuilder\n        {\n            DataSource = path,\n            Mode = SqliteOpenMode.ReadOnly\n        };\n        return new SqliteConnection(csb.ToString());\n    }\n\n    private static int ToDateInt(DateTime date)\n    {\n        return (date.Year * 10000) + (date.Month * 100) + date.Day;\n    }\n\n    private static string ResolveDatabasePath()\n    {\n        var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\n        var appFolder = Path.Combine(localAppData, \"OpenNetMeter\");\n        return Path.Combine(appFolder, \"OpenNetMeter.sqlite\");\n    }\n\n    private DateTimeOffset NormalizeSinceDate(DateTimeOffset? value)\n    {\n        var candidate = (value ?? DateTimeOffset.Now.Date).Date;\n        if (candidate > DateMax)\n            candidate = DateMax;\n        if (candidate < DateMin)\n            candidate = DateMin;\n        return candidate;\n    }\n\n    private void AppendGraphPoint()\n    {\n        dlValues.Add(new ObservablePoint(tickCount, MbpsToGraphValue(latestDownloadMbps)));\n        ulValues.Add(new ObservablePoint(tickCount, MbpsToGraphValue(latestUploadMbps)));\n\n        while (dlValues.Count > WindowSize)\n            dlValues.RemoveAt(0);\n        while (ulValues.Count > WindowSize)\n            ulValues.RemoveAt(0);\n\n        if (tickCount >= WindowSize)\n        {\n            GraphXAxes[0].MinLimit = tickCount - WindowSize;\n            GraphXAxes[0].MaxLimit = tickCount;\n        }\n\n        UpdateGraphAxisLabelScale();\n        tickCount++;\n    }\n\n    private Axis[] CreateGraphYAxes()\n    {\n        return\n        [\n            new Axis\n            {\n                MinLimit = 0,\n                ShowSeparatorLines = true,\n                // Match dark divider/text tones from MainWindow resources\n                SeparatorsPaint = new SolidColorPaint(new SKColor(0x55, 0x55, 0x55)) { StrokeThickness = 1 },\n                LabelsPaint = new SolidColorPaint(new SKColor(0xA9, 0xAB, 0xAB)),\n                TextSize = 10,\n                Labeler = FormatGraphAxisLabel\n            }\n        ];\n    }\n\n    private static double MbpsToGraphValue(double mbps)\n    {\n        if (mbps <= 0)\n            return 0;\n\n        // Plot the graph on a logarithmic curve while preserving zero traffic.\n        return Math.Log(mbps + 1, GraphLogBase);\n    }\n\n    private static double GraphValueToMbps(double graphValue)\n    {\n        if (graphValue <= 0)\n            return 0;\n\n        return Math.Pow(GraphLogBase, graphValue) - 1;\n    }\n\n    private static long GraphValueToBytesPerSecond(double graphValue)\n    {\n        if (graphValue <= 0)\n            return 0;\n\n        return (long)Math.Round(GraphValueToMbps(graphValue) * 1_000_000d / 8d);\n    }\n\n    private void UpdateGraphAxisLabelScale()\n    {\n        var useBytes = SettingsManager.Current.NetworkSpeedFormat != 0;\n        long maxBytesPerSecond = 0;\n\n        if (dlValues.Count > 0)\n            maxBytesPerSecond = Math.Max(maxBytesPerSecond, GraphValueToBytesPerSecond(dlValues.Max(point => point.Y ?? 0d)));\n\n        if (ulValues.Count > 0)\n            maxBytesPerSecond = Math.Max(maxBytesPerSecond, GraphValueToBytesPerSecond(ulValues.Max(point => point.Y ?? 0d)));\n\n        var displayValue = useBytes ? maxBytesPerSecond : maxBytesPerSecond * 8;\n        var (_, magnitude) = GetAdjustedSize(displayValue, SpeedMagnitude.Auto);\n\n        if (graphAxisMagnitude == magnitude)\n            return;\n\n        graphAxisMagnitude = magnitude;\n    }\n\n    private string FormatGraphAxisLabel(double graphValue)\n    {\n        var bytesPerSecond = GraphValueToBytesPerSecond(graphValue);\n        var useBytes = SettingsManager.Current.NetworkSpeedFormat != 0;\n        var displayValue = useBytes ? bytesPerSecond : bytesPerSecond * 8;\n        var adjustedSize = ScaleToMagnitude(displayValue, graphAxisMagnitude);\n        var suffix = useBytes ? BytesSuffix(graphAxisMagnitude) : BitsSuffix(graphAxisMagnitude);\n\n        return $\"{FormatGraphAxisValue(adjustedSize)} {suffix}/s\";\n    }\n\n    private void ApplyProcessTick(Dictionary<string, PendingTraffic> pendingSnapshot)\n    {\n        var touched = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n\n        foreach (var kvp in pendingSnapshot)\n        {\n            touched.Add(kvp.Key);\n\n            if (!processIndex.TryGetValue(kvp.Key, out var row))\n            {\n                row = new SummaryProcessRowViewModel(kvp.Key, processIconService.GetProcessIcon(kvp.Key) as IImage);\n                processIndex[kvp.Key] = row;\n                ActiveProcesses.Add(row);\n                OnPropertyChanged(nameof(ProcessCount));\n            }\n\n            row.ApplyTick(kvp.Value.DownloadBytes, kvp.Value.UploadBytes);\n        }\n\n        foreach (var kvp in processIndex)\n        {\n            if (!touched.Contains(kvp.Key))\n                kvp.Value.ResetCurrent();\n        }\n    }\n\n    private void SortProcesses(string column)\n    {\n        if (string.Equals(currentSortColumn, column, StringComparison.Ordinal))\n        {\n            sortDescending = !sortDescending;\n        }\n        else\n        {\n            currentSortColumn = column;\n            sortDescending = false;\n        }\n\n        Func<SummaryProcessRowViewModel, IComparable> selector = column switch\n        {\n            \"Name\" => p => p.ProcessName,\n            \"CurrentDownload\" => p => p.CurrentDownloadBytes,\n            \"CurrentUpload\" => p => p.CurrentUploadBytes,\n            \"TotalDownload\" => p => p.TotalDownloadBytes,\n            \"TotalUpload\" => p => p.TotalUploadBytes,\n            _ => p => p.ProcessName\n        };\n\n        var sorted = sortDescending\n            ? ActiveProcesses.OrderByDescending(selector).ToList()\n            : ActiveProcesses.OrderBy(selector).ToList();\n\n        ActiveProcesses.Clear();\n        foreach (var item in sorted)\n            ActiveProcesses.Add(item);\n    }\n\n    public void RefreshSpeedDisplayFormat()\n    {\n        UpdateGraphAxisLabelScale();\n        GraphYAxes = CreateGraphYAxes();\n        OnPropertyChanged(nameof(GraphYAxes));\n        OnPropertyChanged(nameof(DownloadSpeedText));\n        OnPropertyChanged(nameof(UploadSpeedText));\n    }\n\n    private static string FormatSpeed(long bytesPerSecond)\n    {\n        var useBytes = SettingsManager.Current.NetworkSpeedFormat != 0;\n        var magnitude = NormalizeMagnitude(SettingsManager.Current.NetworkSpeedMagnitude);\n        var value = useBytes ? bytesPerSecond : bytesPerSecond * 8;\n        var (adjustedSize, mag) = GetAdjustedSize(value, magnitude);\n\n        return decimal.Round(adjustedSize, 2).ToString() + (useBytes ? BytesSuffix(mag) : BitsSuffix(mag));\n    }\n\n    private static decimal ScaleToMagnitude(long value, int magnitude)\n    {\n        var clampedMagnitude = Math.Clamp(magnitude, 0, MaxSpeedMagnitude);\n        return (decimal)value / (1L << (clampedMagnitude * 10));\n    }\n\n    private static string FormatGraphAxisValue(decimal adjustedSize)\n    {\n        if (adjustedSize <= 0)\n            return \"0\";\n\n        var rounded = adjustedSize < 10\n            ? decimal.Round(adjustedSize, 1)\n            : decimal.Round(adjustedSize, 0);\n\n        return rounded == decimal.Truncate(rounded)\n            ? rounded.ToString(\"0\")\n            : rounded.ToString(\"0.#\");\n    }\n\n    private static SpeedMagnitude NormalizeMagnitude(int magnitude)\n    {\n        return Enum.IsDefined(typeof(SpeedMagnitude), magnitude)\n            ? (SpeedMagnitude)magnitude\n            : SpeedMagnitude.Auto;\n    }\n\n    private static (decimal adjustedSize, int mag) GetAdjustedSize(long value, SpeedMagnitude magnitude)\n    {\n        int mag;\n        decimal adjustedSize;\n\n        if (magnitude == SpeedMagnitude.Auto)\n        {\n            mag = value > 0 ? (int)Math.Log(value, 1024) : 0;\n            mag = Math.Clamp(mag, 0, 6);\n\n            adjustedSize = (decimal)value / (1L << (mag * 10));\n            if (Math.Round(adjustedSize, 1) >= 1000 && mag < 6)\n            {\n                mag += 1;\n                adjustedSize /= 1024;\n            }\n        }\n        else\n        {\n            mag = Math.Clamp((int)magnitude, 0, 6);\n            adjustedSize = (decimal)value / (1L << (mag * 10));\n        }\n\n        return (adjustedSize, mag);\n    }\n\n    private static string BytesSuffix(int value)\n    {\n        return value switch\n        {\n            0 => \"B\",\n            1 => \"KB\",\n            2 => \"MB\",\n            3 => \"GB\",\n            4 => \"TB\",\n            5 => \"PB\",\n            6 => \"EB\",\n            _ => \"B\"\n        };\n    }\n\n    private static string BitsSuffix(int value)\n    {\n        return value switch\n        {\n            0 => \"b\",\n            1 => \"Kb\",\n            2 => \"Mb\",\n            3 => \"Gb\",\n            4 => \"Tb\",\n            5 => \"Pb\",\n            6 => \"Eb\",\n            _ => \"b\"\n        };\n    }\n\n    private void OnPropertyChanged(string propertyName)\n    {\n        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n    }\n\n    private sealed class PendingTraffic\n    {\n        public long DownloadBytes;\n        public long UploadBytes;\n    }\n}\n\ninternal enum SpeedMagnitude\n{\n    Auto = 0,\n    Kilo = 1,\n    Mega = 2,\n    Giga = 3\n}\n\npublic sealed class SummaryProcessRowViewModel : INotifyPropertyChanged\n{\n    private long currentDownloadBytes;\n    private long currentUploadBytes;\n    private long totalDownloadBytes;\n    private long totalUploadBytes;\n\n    public SummaryProcessRowViewModel(string processName, IImage? icon = null)\n    {\n        ProcessName = processName;\n        Icon = icon;\n    }\n\n    public IImage? Icon { get; }\n    public string ProcessName { get; }\n    public long CurrentDownloadBytes => currentDownloadBytes;\n    public long CurrentUploadBytes => currentUploadBytes;\n    public long TotalDownloadBytes => totalDownloadBytes;\n    public long TotalUploadBytes => totalUploadBytes;\n\n    public string CurrentDownload => ByteSizeFormatter.FormatBytes(currentDownloadBytes);\n    public string CurrentUpload => ByteSizeFormatter.FormatBytes(currentUploadBytes);\n    public string TotalDownload => ByteSizeFormatter.FormatBytes(totalDownloadBytes);\n    public string TotalUpload => ByteSizeFormatter.FormatBytes(totalUploadBytes);\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n\n    public void ApplyTick(long secondDownloadBytes, long secondUploadBytes)\n    {\n        currentDownloadBytes = secondDownloadBytes;\n        currentUploadBytes = secondUploadBytes;\n        totalDownloadBytes += secondDownloadBytes;\n        totalUploadBytes += secondUploadBytes;\n\n        OnPropertyChanged(nameof(CurrentDownloadBytes));\n        OnPropertyChanged(nameof(CurrentUploadBytes));\n        OnPropertyChanged(nameof(TotalDownloadBytes));\n        OnPropertyChanged(nameof(TotalUploadBytes));\n        OnPropertyChanged(nameof(CurrentDownload));\n        OnPropertyChanged(nameof(CurrentUpload));\n        OnPropertyChanged(nameof(TotalDownload));\n        OnPropertyChanged(nameof(TotalUpload));\n    }\n\n    public void ResetCurrent()\n    {\n        if (currentDownloadBytes == 0 && currentUploadBytes == 0)\n            return;\n\n        currentDownloadBytes = 0;\n        currentUploadBytes = 0;\n        OnPropertyChanged(nameof(CurrentDownloadBytes));\n        OnPropertyChanged(nameof(CurrentUploadBytes));\n        OnPropertyChanged(nameof(CurrentDownload));\n        OnPropertyChanged(nameof(CurrentUpload));\n    }\n\n    public void ApplyTraffic(long bytes, bool isReceive)\n    {\n        if (isReceive) // kept for compatibility with older call sites\n        {\n            currentDownloadBytes = bytes;\n            totalDownloadBytes += bytes;\n            OnPropertyChanged(nameof(CurrentDownloadBytes));\n            OnPropertyChanged(nameof(TotalDownloadBytes));\n            OnPropertyChanged(nameof(CurrentDownload));\n            OnPropertyChanged(nameof(TotalDownload));\n        }\n        else\n        {\n            currentUploadBytes = bytes;\n            totalUploadBytes += bytes;\n            OnPropertyChanged(nameof(CurrentUploadBytes));\n            OnPropertyChanged(nameof(TotalUploadBytes));\n            OnPropertyChanged(nameof(CurrentUpload));\n            OnPropertyChanged(nameof(TotalUpload));\n        }\n    }\n\n    private void OnPropertyChanged(string propertyName) =>\n        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/History.axaml",
    "content": "<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:vm=\"clr-namespace:OpenNetMeter.ViewModels\"\n             x:Class=\"OpenNetMeter.Views.MainWindowTabs.HistoryView\"\n             x:DataType=\"vm:HistoryViewModel\"\n             mc:Ignorable=\"d\">\n  <Grid Margin=\"12,8\" RowDefinitions=\"Auto,*,Auto\">\n    <Border Padding=\"12\" CornerRadius=\"8\" Background=\"{DynamicResource BrushSurfacePanel}\">\n      <StackPanel Orientation=\"Horizontal\" Spacing=\"8\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\">\n        <TextBlock VerticalAlignment=\"Center\" Text=\"Usage of\"/>\n        <ComboBox Width=\"180\"\n                  ItemsSource=\"{Binding Profiles}\"\n                  SelectedItem=\"{Binding SelectedProfile, Mode=TwoWay}\"/>\n        <TextBlock VerticalAlignment=\"Center\" Text=\"From\"/>\n        <DatePicker MinWidth=\"0\" Width=\"140\" Height=\"30\" HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Center\"\n                    SelectedDate=\"{Binding DateStart, Mode=TwoWay}\"/>\n        <TextBlock VerticalAlignment=\"Center\" Text=\"To\"/>\n        <DatePicker MinWidth=\"0\" Width=\"140\" Height=\"30\" HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Center\"\n                    SelectedDate=\"{Binding DateEnd, Mode=TwoWay}\"/>\n        <Button Width=\"64\" Height=\"24\" Padding=\"0\" Command=\"{Binding FilterCommand}\">\n          <TextBlock Text=\"Filter\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\"/>\n        </Button>\n      </StackPanel>\n    </Border>\n\n    <Border Grid.Row=\"1\" Margin=\"0,12,0,12\" Padding=\"0\" CornerRadius=\"8\" Background=\"{DynamicResource BrushSurfacePanel}\" ClipToBounds=\"True\">\n      <Grid RowDefinitions=\"Auto,*\">\n        <Border Grid.Row=\"0\" Padding=\"12,8\" Background=\"{DynamicResource BrushHeaderRow}\" BorderBrush=\"{DynamicResource BrushBorderEmphasis}\" BorderThickness=\"0,0,0,1\">\n          <Grid ColumnDefinitions=\"3*,1.5*,1.5*\">\n            <Button Content=\"Process\" Command=\"{Binding SortRowsCommand}\" CommandParameter=\"Process\"\n                    HorizontalAlignment=\"Left\" Background=\"{DynamicResource BrushTransparent}\" BorderThickness=\"0\"\n                    Foreground=\"{DynamicResource BrushTextHeader}\" FontSize=\"10\" FontWeight=\"SemiBold\" Padding=\"0\"/>\n            <Button Grid.Column=\"1\" Content=\"Data Received\" Command=\"{Binding SortRowsCommand}\" CommandParameter=\"Download\"\n                    HorizontalAlignment=\"Left\" Background=\"{DynamicResource BrushTransparent}\" BorderThickness=\"0\"\n                    Foreground=\"{DynamicResource BrushTextHeader}\" FontSize=\"10\" FontWeight=\"SemiBold\" Padding=\"0\"/>\n            <Button Grid.Column=\"2\" Content=\"Data Sent\" Command=\"{Binding SortRowsCommand}\" CommandParameter=\"Upload\"\n                    HorizontalAlignment=\"Left\" Background=\"{DynamicResource BrushTransparent}\" BorderThickness=\"0\"\n                    Foreground=\"{DynamicResource BrushTextHeader}\" FontSize=\"10\" FontWeight=\"SemiBold\" Padding=\"0\"/>\n          </Grid>\n        </Border>\n\n        <ScrollViewer Grid.Row=\"1\" Margin=\"0,0,0,6\">\n          <ItemsControl ItemsSource=\"{Binding Rows}\">\n            <ItemsControl.ItemTemplate>\n              <DataTemplate>\n                <Grid ColumnDefinitions=\"3*,1.5*,1.5*\" Margin=\"10,4\">\n                  <Grid ColumnDefinitions=\"Auto,*\" ColumnSpacing=\"8\" MinWidth=\"0\">\n                    <Image Source=\"{Binding Icon}\" Width=\"16\" Height=\"16\"/>\n                    <TextBlock Grid.Column=\"1\" Text=\"{Binding ProcessName}\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"11\"\n                               TextWrapping=\"Wrap\" MinWidth=\"0\"/>\n                  </Grid>\n                  <TextBlock Grid.Column=\"1\" Text=\"{Binding DownloadText}\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"11\" VerticalAlignment=\"Top\"/>\n                  <TextBlock Grid.Column=\"2\" Text=\"{Binding UploadText}\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"11\" VerticalAlignment=\"Top\"/>\n                </Grid>\n              </DataTemplate>\n            </ItemsControl.ItemTemplate>\n          </ItemsControl>\n        </ScrollViewer>\n      </Grid>\n    </Border>\n\n    <Border Grid.Row=\"2\" Padding=\"12\" CornerRadius=\"8\" Background=\"{DynamicResource BrushSurfacePanel}\">\n      <Grid ColumnDefinitions=\"2*,*,*\">\n        <TextBlock FontWeight=\"SemiBold\" Text=\"Total\"/>\n        <TextBlock Grid.Column=\"1\" Text=\"{Binding TotalDownloadText}\"/>\n        <TextBlock Grid.Column=\"2\" Text=\"{Binding TotalUploadText}\"/>\n      </Grid>\n    </Border>\n  </Grid>\n</UserControl>\n"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/History.axaml.cs",
    "content": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Views.MainWindowTabs;\n\npublic partial class HistoryView : UserControl\n{\n    public HistoryView()\n    {\n        InitializeComponent();\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/Settings.axaml",
    "content": "<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:vm=\"clr-namespace:OpenNetMeter.ViewModels\"\n             x:Class=\"OpenNetMeter.Views.MainWindowTabs.SettingsView\"\n             x:DataType=\"vm:SettingsViewModel\"\n             mc:Ignorable=\"d\">\n  <ScrollViewer Margin=\"12,8\">\n    <StackPanel Spacing=\"12\">\n      <Grid ColumnDefinitions=\"*,12,*\">\n        <Border Grid.Column=\"0\" CornerRadius=\"10\" Background=\"{DynamicResource BrushSurfacePanel}\" ClipToBounds=\"True\"\n                BoxShadow=\"{DynamicResource ShadowSoft}\" VerticalAlignment=\"Stretch\">\n          <StackPanel>\n            <Border Padding=\"16,12\" Background=\"{DynamicResource BrushPanelAlt}\">\n              <StackPanel Orientation=\"Horizontal\" Spacing=\"8\">\n                <Border CornerRadius=\"6\" Padding=\"4\" Width=\"24\" Height=\"24\" Background=\"{DynamicResource BrushSuccess}\">\n                  <PathIcon Data=\"M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.04 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.04 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z\"\n                            Foreground=\"{DynamicResource BrushWhite}\" Width=\"14\" Height=\"14\"/>\n                </Border>\n                <TextBlock Text=\"General\" Foreground=\"{DynamicResource BrushTextHeader}\" FontWeight=\"SemiBold\" FontSize=\"13\"\n                           VerticalAlignment=\"Center\"/>\n              </StackPanel>\n            </Border>\n            <StackPanel Margin=\"20,16\" Spacing=\"0\">\n              <Grid Margin=\"0,0,0,16\">\n                <Grid.ColumnDefinitions>\n                  <ColumnDefinition Width=\"*\"/>\n                  <ColumnDefinition Width=\"Auto\"/>\n                </Grid.ColumnDefinitions>\n                <StackPanel Grid.Column=\"0\">\n                  <TextBlock Text=\"Start on Windows startup\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"13\"/>\n                  <TextBlock Text=\"Launch automatically when you sign in\" Foreground=\"{DynamicResource BrushTextMuted}\" FontSize=\"11\" Margin=\"0,3,0,0\"/>\n                </StackPanel>\n                <ToggleSwitch Grid.Column=\"1\" IsChecked=\"{Binding StartWithWindows, Mode=TwoWay}\"\n                              VerticalAlignment=\"Center\" OnContent=\"\" OffContent=\"\" Margin=\"0,0,-8,0\"/>\n              </Grid>\n              <Border Height=\"1\" Background=\"{DynamicResource BrushDivider}\" Margin=\"0,0,0,16\"/>\n              <Grid Margin=\"0,0,0,16\">\n                <Grid.ColumnDefinitions>\n                  <ColumnDefinition Width=\"*\"/>\n                  <ColumnDefinition Width=\"Auto\"/>\n                </Grid.ColumnDefinitions>\n                <StackPanel Grid.Column=\"0\">\n                  <TextBlock Text=\"Minimize to tray on start\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"13\"/>\n                  <TextBlock Text=\"Start quietly in the system tray\" Foreground=\"{DynamicResource BrushTextMuted}\" FontSize=\"11\" Margin=\"0,3,0,0\"/>\n                </StackPanel>\n                <ToggleSwitch Grid.Column=\"1\" IsChecked=\"{Binding MinimizeOnStart, Mode=TwoWay}\"\n                              IsEnabled=\"{Binding CanChangeMinimizeOnStart}\"\n                              VerticalAlignment=\"Center\" OnContent=\"\" OffContent=\"\" Margin=\"0,0,-8,0\"/>\n              </Grid>\n              <Border Height=\"1\" Background=\"{DynamicResource BrushDivider}\" Margin=\"0,0,0,16\"/>\n              <Grid>\n                <Grid.ColumnDefinitions>\n                  <ColumnDefinition Width=\"*\"/>\n                  <ColumnDefinition Width=\"Auto\"/>\n                </Grid.ColumnDefinitions>\n                <StackPanel Grid.Column=\"0\">\n                  <TextBlock Text=\"Dark mode\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"13\"/>\n                  <TextBlock Text=\"Use dark theme for the interface\" Foreground=\"{DynamicResource BrushTextMuted}\" FontSize=\"11\" Margin=\"0,3,0,0\"/>\n                </StackPanel>\n                <ToggleSwitch Grid.Column=\"1\" IsChecked=\"{Binding DarkMode, Mode=TwoWay}\"\n                              VerticalAlignment=\"Center\" OnContent=\"\" OffContent=\"\" Margin=\"0,0,-8,0\"/>\n              </Grid>\n            </StackPanel>\n          </StackPanel>\n        </Border>\n\n        <Border Grid.Column=\"2\" CornerRadius=\"10\" Background=\"{DynamicResource BrushSurfacePanel}\" ClipToBounds=\"True\"\n                BoxShadow=\"{DynamicResource ShadowSoft}\" VerticalAlignment=\"Stretch\">\n          <StackPanel>\n            <Border Padding=\"16,12\" Background=\"{DynamicResource BrushPanelAlt}\">\n              <StackPanel Orientation=\"Horizontal\" Spacing=\"8\">\n                <Border CornerRadius=\"6\" Padding=\"4\" Width=\"24\" Height=\"24\" Background=\"{DynamicResource BrushInfo}\">\n                  <PathIcon Data=\"M21,17H7V3H21M21,1H7A2,2 0 0,0 5,3V17A2,2 0 0,0 7,19H21A2,2 0 0,0 23,17V3A2,2 0 0,0 21,1M3,5H1V21A2,2 0 0,0 3,23H19V21H3V5Z\"\n                            Foreground=\"{DynamicResource BrushWhite}\" Width=\"14\" Height=\"14\"/>\n                </Border>\n                <TextBlock Text=\"Mini Widget\" Foreground=\"{DynamicResource BrushTextHeader}\" FontWeight=\"SemiBold\" FontSize=\"13\"\n                           VerticalAlignment=\"Center\"/>\n              </StackPanel>\n            </Border>\n            <StackPanel Margin=\"20,16\" Spacing=\"0\">\n              <Grid Margin=\"0,0,0,16\">\n                <Grid.ColumnDefinitions>\n                  <ColumnDefinition Width=\"*\"/>\n                  <ColumnDefinition Width=\"Auto\"/>\n                </Grid.ColumnDefinitions>\n                <StackPanel Grid.Column=\"0\">\n                  <TextBlock Text=\"Show mini widget\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"13\"/>\n                  <TextBlock Text=\"Floating overlay showing live speeds\" Foreground=\"{DynamicResource BrushTextMuted}\" FontSize=\"11\" Margin=\"0,3,0,0\"/>\n                </StackPanel>\n                <ToggleSwitch Grid.Column=\"1\" IsChecked=\"{Binding MiniWidgetVisible, Mode=TwoWay}\"\n                              VerticalAlignment=\"Center\" OnContent=\"\" OffContent=\"\" Margin=\"0,0,-8,0\"/>\n              </Grid>\n              <Border Height=\"1\" Background=\"{DynamicResource BrushDivider}\" Margin=\"0,0,0,16\"/>\n              <Grid Margin=\"0,0,0,10\">\n                <TextBlock Text=\"Transparency\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"13\" VerticalAlignment=\"Center\"/>\n                <Border HorizontalAlignment=\"Right\" CornerRadius=\"4\" Padding=\"8,3\" Background=\"{DynamicResource BrushPanelAlt}\">\n                  <TextBlock Foreground=\"{DynamicResource BrushTextSecondaryAlt}\" FontSize=\"12\" FontFamily=\"Consolas,monospace\">\n                    <Run Text=\"{Binding MiniWidgetTransparency}\"/><Run Text=\"%\"/>\n                  </TextBlock>\n                </Border>\n              </Grid>\n              <Slider Minimum=\"0\" Maximum=\"100\" Value=\"{Binding MiniWidgetTransparency, Mode=TwoWay}\"\n                      IsEnabled=\"{Binding MiniWidgetVisible}\" Margin=\"0,0,0,4\"/>\n              <Grid>\n                <TextBlock HorizontalAlignment=\"Left\" Text=\"Opaque\" Foreground=\"{DynamicResource BrushTextFaint}\" FontSize=\"10\"/>\n                <TextBlock HorizontalAlignment=\"Right\" Text=\"Transparent\" Foreground=\"{DynamicResource BrushTextFaint}\" FontSize=\"10\"/>\n              </Grid>\n            </StackPanel>\n          </StackPanel>\n        </Border>\n      </Grid>\n\n      <Border CornerRadius=\"10\" Background=\"{DynamicResource BrushSurfacePanel}\" ClipToBounds=\"True\"\n              BoxShadow=\"{DynamicResource ShadowSoft}\">\n        <StackPanel>\n          <Border Padding=\"16,12\" Background=\"{DynamicResource BrushPanelAlt}\">\n            <StackPanel Orientation=\"Horizontal\" Spacing=\"8\">\n              <Border CornerRadius=\"6\" Padding=\"4\" Width=\"24\" Height=\"24\" Background=\"{DynamicResource BrushWarning}\">\n                <PathIcon Data=\"M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2M4,12C4,11.39 4.08,10.79 4.21,10.22L8,14V15C8,16.1 8.9,17 10,17V19.93C6.61,19.44 4,16.07 4,12M17.9,17.39C17.64,16.58 16.9,16 16,16H15V13C15,12.45 14.55,12 14,12H8V10H10C10.55,10 11,9.55 11,9V7H13C14.1,7 15,6.1 15,5V4.59C17.93,5.77 20,8.64 20,12C20,14.08 19.2,15.97 17.9,17.39Z\"\n                          Foreground=\"{DynamicResource BrushWhite}\" Width=\"14\" Height=\"14\"/>\n              </Border>\n              <TextBlock Text=\"Network Monitoring\" Foreground=\"{DynamicResource BrushTextHeader}\" FontWeight=\"SemiBold\" FontSize=\"13\"\n                         VerticalAlignment=\"Center\"/>\n            </StackPanel>\n          </Border>\n          <Grid ColumnDefinitions=\"*,Auto,*\" Margin=\"20,16\">\n            <StackPanel Grid.Column=\"0\" Spacing=\"0\">\n              <TextBlock Text=\"Monitor these networks\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"13\" Margin=\"0,0,0,4\"/>\n              <TextBlock Text=\"Choose Public for internet-only traffic\" Foreground=\"{DynamicResource BrushTextMuted}\" FontSize=\"11\" Margin=\"0,0,0,14\"/>\n              <StackPanel Orientation=\"Horizontal\" Spacing=\"8\">\n                <RadioButton GroupName=\"NetTarget\" Classes=\"pill\"\n                             IsChecked=\"{Binding IsNetworkTargetPrivate, Mode=TwoWay}\">\n                  <TextBlock Text=\"Private\"/>\n                </RadioButton>\n                <RadioButton GroupName=\"NetTarget\" Classes=\"pill\"\n                             IsChecked=\"{Binding IsNetworkTargetPublic, Mode=TwoWay}\">\n                  <TextBlock Text=\"Public\"/>\n                </RadioButton>\n                <RadioButton GroupName=\"NetTarget\" Classes=\"pill\"\n                             IsChecked=\"{Binding IsNetworkTargetBoth, Mode=TwoWay}\">\n                  <TextBlock Text=\"Both\"/>\n                </RadioButton>\n              </StackPanel>\n            </StackPanel>\n            <Border Grid.Column=\"1\" Width=\"1\" Background=\"{DynamicResource BrushDivider}\" Margin=\"24,0\"/>\n            <StackPanel Grid.Column=\"2\" Spacing=\"0\">\n              <TextBlock Text=\"Speed display format\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"13\" Margin=\"0,0,0,4\"/>\n              <TextBlock Text=\"Choose how speeds are shown\" Foreground=\"{DynamicResource BrushTextMuted}\" FontSize=\"11\" Margin=\"0,0,0,14\"/>\n              <Grid ColumnDefinitions=\"*,12,*\">\n                <StackPanel Grid.Column=\"0\" Spacing=\"6\">\n                  <TextBlock Text=\"Magnitude\" Foreground=\"{DynamicResource BrushTextMuted}\" FontSize=\"11\"/>\n                  <ComboBox HorizontalAlignment=\"Stretch\" ItemsSource=\"{Binding SpeedMagnitudes}\"\n                            SelectedIndex=\"{Binding SelectedSpeedMagnitudeIndex, Mode=TwoWay}\"/>\n                </StackPanel>\n                <StackPanel Grid.Column=\"2\" Spacing=\"6\">\n                  <TextBlock Text=\"Unit\" Foreground=\"{DynamicResource BrushTextMuted}\" FontSize=\"11\"/>\n                  <ComboBox HorizontalAlignment=\"Stretch\" ItemsSource=\"{Binding SpeedUnits}\"\n                            SelectedIndex=\"{Binding SelectedSpeedUnitIndex, Mode=TwoWay}\"/>\n                </StackPanel>\n              </Grid>\n            </StackPanel>\n          </Grid>\n        </StackPanel>\n      </Border>\n\n      <Grid ColumnDefinitions=\"*,12,*\">\n        <Border Grid.Column=\"0\" CornerRadius=\"10\" Background=\"{DynamicResource BrushSurfacePanel}\" ClipToBounds=\"True\"\n                BoxShadow=\"{DynamicResource ShadowSoft}\" VerticalAlignment=\"Stretch\">\n          <StackPanel>\n            <Border Padding=\"16,12\" Background=\"{DynamicResource BrushPanelAlt}\">\n              <StackPanel Orientation=\"Horizontal\" Spacing=\"8\">\n                <Border CornerRadius=\"6\" Padding=\"4\" Width=\"24\" Height=\"24\" Background=\"{DynamicResource BrushDangerBright}\">\n                  <PathIcon Data=\"M12,3C7.58,3 4,4.79 4,7C4,9.21 7.58,11 12,11C16.42,11 20,9.21 20,7C20,4.79 16.42,3 12,3M4,9V12C4,14.21 7.58,16 12,16C16.42,16 20,14.21 20,12V9C20,11.21 16.42,13 12,13C7.58,13 4,11.21 4,9M4,14V17C4,19.21 7.58,21 12,21C16.42,21 20,19.21 20,17V14C20,16.21 16.42,18 12,18C7.58,18 4,16.21 4,14Z\"\n                            Foreground=\"{DynamicResource BrushWhite}\" Width=\"14\" Height=\"14\"/>\n                </Border>\n                <TextBlock Text=\"Data Management\" Foreground=\"{DynamicResource BrushTextHeader}\" FontWeight=\"SemiBold\" FontSize=\"13\"\n                           VerticalAlignment=\"Center\"/>\n              </StackPanel>\n            </Border>\n            <StackPanel Margin=\"20,16\" Spacing=\"0\">\n              <Border CornerRadius=\"8\" BorderBrush=\"{DynamicResource BrushDangerBorderSoft}\" BorderThickness=\"1\" Background=\"{DynamicResource BrushDangerBgSoft}\" Padding=\"16\">\n                <StackPanel Spacing=\"12\">\n                  <StackPanel>\n                    <TextBlock Text=\"Delete all saved profiles\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"13\"/>\n                    <TextBlock Text=\"Remove all network usage history. This cannot be undone.\" Foreground=\"{DynamicResource BrushDangerBright}\" FontSize=\"11\" Margin=\"0,3,0,0\"/>\n                  </StackPanel>\n                  <Button Command=\"{Binding ResetDataCommand}\"\n                          HorizontalAlignment=\"Stretch\" HorizontalContentAlignment=\"Center\"\n                          Background=\"{DynamicResource BrushDangerBright}\" Foreground=\"{DynamicResource BrushWhite}\" Padding=\"16,8\" CornerRadius=\"6\">\n                    <TextBlock Text=\"Delete All\" FontWeight=\"SemiBold\" FontSize=\"12\"/>\n                  </Button>\n                </StackPanel>\n              </Border>\n            </StackPanel>\n          </StackPanel>\n        </Border>\n\n        <Border Grid.Column=\"2\" CornerRadius=\"10\" Background=\"{DynamicResource BrushSurfacePanel}\" ClipToBounds=\"True\"\n                BoxShadow=\"{DynamicResource ShadowSoft}\" VerticalAlignment=\"Stretch\">\n          <StackPanel>\n            <Border Padding=\"16,12\" Background=\"{DynamicResource BrushPanelAlt}\">\n              <StackPanel Orientation=\"Horizontal\" Spacing=\"8\">\n                <Border CornerRadius=\"6\" Padding=\"4\" Width=\"24\" Height=\"24\" Background=\"{DynamicResource BrushAccentPurple}\">\n                  <PathIcon Data=\"M21,10.12H14.22L16.96,7.3C14.23,4.6 9.81,4.5 7.08,7.2C4.35,9.91 4.35,14.28 7.08,17C9.81,19.7 14.23,19.7 16.96,17C18.32,15.65 19,14.08 19,12.1H21C21,14.08 20.12,16.65 18.36,18.39C14.85,21.87 9.15,21.87 5.64,18.39C2.14,14.92 2.11,9.28 5.62,5.81C9.13,2.34 14.76,2.34 18.27,5.81L21,3V10.12M12.5,8V12.25L16,14.33L15.28,15.54L11,13V8H12.5Z\"\n                            Foreground=\"{DynamicResource BrushWhite}\" Width=\"14\" Height=\"14\"/>\n                </Border>\n                <TextBlock Text=\"Updates\" Foreground=\"{DynamicResource BrushTextHeader}\" FontWeight=\"SemiBold\" FontSize=\"13\"\n                           VerticalAlignment=\"Center\"/>\n              </StackPanel>\n            </Border>\n            <StackPanel Margin=\"20,16\" Spacing=\"0\">\n              <TextBlock Text=\"Keep OpenNetMeter up to date for the latest features and fixes\"\n                         Foreground=\"{DynamicResource BrushTextMuted}\" FontSize=\"11\" Margin=\"0,0,0,16\"/>\n              <TextBlock Text=\"{Binding UpdateStatusMessage}\" Foreground=\"{DynamicResource BrushTextSecondaryAlt}\" FontSize=\"12\"\n                         HorizontalAlignment=\"Center\" Margin=\"0,0,0,12\"\n                         IsVisible=\"{Binding UpdateStatusMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}\"/>\n              <ProgressBar IsVisible=\"{Binding IsCheckingForUpdates}\" IsIndeterminate=\"True\" Height=\"3\"\n                           Margin=\"0,0,0,12\"/>\n              <Grid>\n                <Button Command=\"{Binding CheckForUpdatesCommand}\"\n                        IsVisible=\"{Binding !IsUpdateAvailable}\"\n                        HorizontalAlignment=\"Stretch\" HorizontalContentAlignment=\"Center\"\n                        Background=\"{DynamicResource BrushPanelAlt}\" Foreground=\"{DynamicResource BrushTextUi}\" Padding=\"16,10\" CornerRadius=\"6\"\n                        BorderBrush=\"{DynamicResource BrushBorderEmphasis}\" BorderThickness=\"1\">\n                  <StackPanel Orientation=\"Horizontal\" Spacing=\"8\">\n                    <PathIcon Data=\"M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z\"\n                              Foreground=\"{DynamicResource BrushTextSecondaryAlt}\" Width=\"14\" Height=\"14\"/>\n                    <TextBlock Text=\"Check for updates\"/>\n                  </StackPanel>\n                </Button>\n                <Button Command=\"{Binding DownloadUpdateCommand}\"\n                        IsVisible=\"{Binding IsUpdateAvailable}\"\n                        HorizontalAlignment=\"Stretch\" HorizontalContentAlignment=\"Center\"\n                        Background=\"{DynamicResource BrushSuccess}\" Foreground=\"{DynamicResource BrushWhite}\" Padding=\"16,10\" CornerRadius=\"6\">\n                  <StackPanel Orientation=\"Horizontal\" Spacing=\"8\">\n                    <PathIcon Data=\"M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z\"\n                              Foreground=\"{DynamicResource BrushWhite}\" Width=\"14\" Height=\"14\"/>\n                    <TextBlock Text=\"Download Update\" FontWeight=\"SemiBold\"/>\n                  </StackPanel>\n                </Button>\n              </Grid>\n            </StackPanel>\n          </StackPanel>\n        </Border>\n      </Grid>\n    </StackPanel>\n  </ScrollViewer>\n</UserControl>\n"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/Settings.axaml.cs",
    "content": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Views.MainWindowTabs;\n\npublic partial class SettingsView : UserControl\n{\n    public SettingsView()\n    {\n        InitializeComponent();\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/Summary.axaml",
    "content": "<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:vm=\"clr-namespace:OpenNetMeter.ViewModels\"\n             xmlns:lvc=\"using:LiveChartsCore.SkiaSharpView.Avalonia\"\n             x:Class=\"OpenNetMeter.Views.MainWindowTabs.SummaryView\"\n             x:DataType=\"vm:SummaryViewModel\"\n             mc:Ignorable=\"d\">\n  <Grid Margin=\"12,8\" RowDefinitions=\"*,110\" ColumnDefinitions=\"180,12,*\">\n    <Grid Grid.Row=\"0\" Grid.Column=\"0\" RowDefinitions=\"Auto,10,Auto,10,*\">\n      <Border Grid.Row=\"0\" Padding=\"10\" CornerRadius=\"12\" Background=\"{DynamicResource BrushSurfacePanel}\" BoxShadow=\"{DynamicResource ShadowStrong}\">\n        <StackPanel Spacing=\"4\">\n          <StackPanel Orientation=\"Horizontal\" Margin=\"0,0,0,6\">\n            <Border CornerRadius=\"6\" Padding=\"4\" Width=\"22\" Height=\"22\" Background=\"{DynamicResource BrushSuccess}\">\n              <PathIcon Data=\"M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12.5,7V12.25L17,14.92L16.25,16.15L11,13V7H12.5Z\"\n                        Foreground=\"{DynamicResource BrushWhite}\" Width=\"12\" Height=\"12\"/>\n            </Border>\n            <TextBlock Text=\"Current Session\" FontSize=\"11\" FontWeight=\"SemiBold\"\n                       Foreground=\"{DynamicResource BrushTextHeader}\" VerticalAlignment=\"Center\" Margin=\"6,0,0,0\"/>\n          </StackPanel>\n\n          <Border CornerRadius=\"6\" Padding=\"8,4\" Background=\"{DynamicResource BrushSuccess}\" Margin=\"0,0,0,4\">\n            <Grid ColumnDefinitions=\"Auto,*\">\n              <StackPanel Orientation=\"Horizontal\">\n                <PathIcon Data=\"M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z\"\n                          Foreground=\"{DynamicResource BrushWhite}\" Width=\"12\" Height=\"12\"/>\n                <TextBlock Text=\"DL\" Foreground=\"{DynamicResource BrushWhite}\" FontSize=\"10\" VerticalAlignment=\"Center\" Margin=\"4,0,0,0\"/>\n              </StackPanel>\n              <TextBlock Grid.Column=\"1\" Text=\"{Binding CurrentSessionDownloadText}\"\n                         Foreground=\"{DynamicResource BrushWhite}\" FontSize=\"11\" FontWeight=\"SemiBold\"\n                         HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\"/>\n            </Grid>\n          </Border>\n\n          <Border CornerRadius=\"6\" Padding=\"8,4\" Background=\"{DynamicResource BrushInfo}\">\n            <Grid ColumnDefinitions=\"Auto,*\">\n              <StackPanel Orientation=\"Horizontal\">\n                <PathIcon Data=\"M9,16V10H5L12,3L19,10H15V16H9M5,20V18H19V20H5Z\"\n                          Foreground=\"{DynamicResource BrushWhite}\" Width=\"12\" Height=\"12\"/>\n                <TextBlock Text=\"UL\" Foreground=\"{DynamicResource BrushWhite}\" FontSize=\"10\" VerticalAlignment=\"Center\" Margin=\"4,0,0,0\"/>\n              </StackPanel>\n              <TextBlock Grid.Column=\"1\" Text=\"{Binding CurrentSessionUploadText}\"\n                         Foreground=\"{DynamicResource BrushWhite}\" FontSize=\"11\" FontWeight=\"SemiBold\"\n                         HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\"/>\n            </Grid>\n          </Border>\n        </StackPanel>\n      </Border>\n\n      <Border Grid.Row=\"2\" Padding=\"10\" CornerRadius=\"12\" Background=\"{DynamicResource BrushSurfacePanel}\" BoxShadow=\"{DynamicResource ShadowStrong}\">\n        <StackPanel Spacing=\"4\">\n          <StackPanel Orientation=\"Horizontal\" Margin=\"0,0,0,6\">\n            <Border CornerRadius=\"6\" Padding=\"4\" Width=\"22\" Height=\"22\" Background=\"{DynamicResource BrushInfo}\">\n              <PathIcon Data=\"M12,3C7.58,3 4,4.79 4,7C4,9.21 7.58,11 12,11C16.42,11 20,9.21 20,7C20,4.79 16.42,3 12,3M4,9V12C4,14.21 7.58,16 12,16C16.42,16 20,14.21 20,12V9C20,11.21 16.42,13 12,13C7.58,13 4,11.21 4,9M4,14V17C4,19.21 7.58,21 12,21C16.42,21 20,19.21 20,17V14C20,16.21 16.42,18 12,18C7.58,18 4,16.21 4,14Z\"\n                        Foreground=\"{DynamicResource BrushWhite}\" Width=\"12\" Height=\"12\"/>\n            </Border>\n            <TextBlock Text=\"Usage From\" FontSize=\"11\" FontWeight=\"SemiBold\"\n                       Foreground=\"{DynamicResource BrushTextHeader}\" VerticalAlignment=\"Center\" Margin=\"6,0,0,0\"/>\n          </StackPanel>\n\n          <DatePicker MinWidth=\"0\" Width=\"140\" Height=\"30\" HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Center\" Margin=\"0,0,0,6\"\n                      SelectedDate=\"{Binding SinceDate, Mode=TwoWay}\"/>\n\n          <Border CornerRadius=\"6\" Padding=\"8,4\" Background=\"{DynamicResource BrushSuccess}\" Margin=\"0,0,0,4\">\n            <Grid ColumnDefinitions=\"Auto,*\">\n              <StackPanel Orientation=\"Horizontal\">\n                <PathIcon Data=\"M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z\"\n                          Foreground=\"{DynamicResource BrushWhite}\" Width=\"12\" Height=\"12\"/>\n                <TextBlock Text=\"DL\" Foreground=\"{DynamicResource BrushWhite}\" FontSize=\"10\" VerticalAlignment=\"Center\" Margin=\"4,0,0,0\"/>\n              </StackPanel>\n              <TextBlock Grid.Column=\"1\" Text=\"{Binding TotalFromDateDownloadText}\"\n                         Foreground=\"{DynamicResource BrushWhite}\" FontSize=\"11\" FontWeight=\"SemiBold\"\n                         HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\"/>\n            </Grid>\n          </Border>\n\n          <Border CornerRadius=\"6\" Padding=\"8,4\" Background=\"{DynamicResource BrushInfo}\">\n            <Grid ColumnDefinitions=\"Auto,*\">\n              <StackPanel Orientation=\"Horizontal\">\n                <PathIcon Data=\"M9,16V10H5L12,3L19,10H15V16H9M5,20V18H19V20H5Z\"\n                          Foreground=\"{DynamicResource BrushWhite}\" Width=\"12\" Height=\"12\"/>\n                <TextBlock Text=\"UL\" Foreground=\"{DynamicResource BrushWhite}\" FontSize=\"10\" VerticalAlignment=\"Center\" Margin=\"4,0,0,0\"/>\n              </StackPanel>\n              <TextBlock Grid.Column=\"1\" Text=\"{Binding TotalFromDateUploadText}\"\n                         Foreground=\"{DynamicResource BrushWhite}\" FontSize=\"11\" FontWeight=\"SemiBold\"\n                         HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\"/>\n            </Grid>\n          </Border>\n        </StackPanel>\n      </Border>\n\n      <Border Grid.Row=\"4\" Padding=\"10\" CornerRadius=\"12,12,0,0\" Background=\"{DynamicResource BrushSurfacePanel}\"\n              BoxShadow=\"{DynamicResource ShadowStrong}\" VerticalAlignment=\"Bottom\">\n        <StackPanel>\n          <StackPanel Orientation=\"Horizontal\">\n            <Border CornerRadius=\"6\" Padding=\"4\" Width=\"22\" Height=\"22\" Background=\"{DynamicResource BrushSuccess}\">\n              <PathIcon Data=\"M22,21H2V3H4V19H6V10H10V19H12V6H16V19H18V14H22V21Z\"\n                        Foreground=\"{DynamicResource BrushWhite}\" Width=\"12\" Height=\"12\"/>\n            </Border>\n            <TextBlock Text=\"Activity\" FontSize=\"11\" FontWeight=\"SemiBold\"\n                       Foreground=\"{DynamicResource BrushTextHeader}\" VerticalAlignment=\"Center\" Margin=\"6,0,0,0\"/>\n          </StackPanel>\n          <StackPanel Orientation=\"Horizontal\" Margin=\"0,8,0,0\">\n            <Ellipse Width=\"8\" Height=\"8\" Fill=\"{DynamicResource BrushSuccess}\" VerticalAlignment=\"Center\"/>\n            <TextBlock Text=\"Download\" Foreground=\"{DynamicResource BrushTextSecondaryAlt}\" FontSize=\"10\" Margin=\"4,0,12,0\" VerticalAlignment=\"Center\"/>\n            <Ellipse Width=\"8\" Height=\"8\" Fill=\"{DynamicResource BrushInfo}\" VerticalAlignment=\"Center\"/>\n            <TextBlock Text=\"Upload\" Foreground=\"{DynamicResource BrushTextSecondaryAlt}\" FontSize=\"10\" Margin=\"4,0,0,0\" VerticalAlignment=\"Center\"/>\n          </StackPanel>\n        </StackPanel>\n      </Border>\n    </Grid>\n\n    <Border Grid.Row=\"0\" Grid.Column=\"2\" CornerRadius=\"12\" Background=\"{DynamicResource BrushSurfacePanel}\"\n            BoxShadow=\"{DynamicResource ShadowStrong}\" Margin=\"0,0,0,10\" Padding=\"0\" ClipToBounds=\"True\">\n      <Grid RowDefinitions=\"Auto,*\">\n        <Border Grid.Row=\"0\" Padding=\"12,8\" Background=\"{DynamicResource BrushHeaderRow}\"\n                BorderBrush=\"{DynamicResource BrushBorderEmphasis}\" BorderThickness=\"0,0,0,1\">\n          <Grid>\n            <StackPanel Orientation=\"Horizontal\">\n              <PathIcon Data=\"M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M7.5,13A2.5,2.5 0 0,0 5,15.5A2.5,2.5 0 0,0 7.5,18A2.5,2.5 0 0,0 10,15.5A2.5,2.5 0 0,0 7.5,13M16.5,13A2.5,2.5 0 0,0 14,15.5A2.5,2.5 0 0,0 16.5,18A2.5,2.5 0 0,0 19,15.5A2.5,2.5 0 0,0 16.5,13Z\"\n                        Foreground=\"{DynamicResource BrushIconMuted}\" Width=\"16\" Height=\"16\"/>\n              <TextBlock Text=\"Active Processes\" Foreground=\"{DynamicResource BrushTextHeader}\" FontSize=\"12\"\n                         FontWeight=\"SemiBold\" VerticalAlignment=\"Center\" Margin=\"8,0,0,0\"/>\n            </StackPanel>\n            <TextBlock HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\"\n                       Foreground=\"{DynamicResource BrushTextSecondaryAlt}\" FontSize=\"10\">\n              <Run Text=\"{Binding ProcessCount}\"/>\n              <Run Text=\" processes\"/>\n            </TextBlock>\n          </Grid>\n        </Border>\n\n        <Grid Grid.Row=\"1\" RowDefinitions=\"Auto,*\">\n          <Grid ColumnDefinitions=\"3*,1.2*,1.2*,1.2*,1.2*\" Margin=\"10,8,10,4\">\n            <Button Content=\"Name\" Command=\"{Binding SortProcessesCommand}\" CommandParameter=\"Name\"\n                    HorizontalAlignment=\"Left\" Background=\"{DynamicResource BrushTransparent}\" BorderThickness=\"0\"\n                    Foreground=\"{DynamicResource BrushTextHeader}\" FontSize=\"10\" FontWeight=\"SemiBold\" Padding=\"0\"/>\n            <Button Grid.Column=\"1\" Content=\"? Current\" Command=\"{Binding SortProcessesCommand}\" CommandParameter=\"CurrentDownload\"\n                    HorizontalAlignment=\"Left\" Background=\"{DynamicResource BrushTransparent}\" BorderThickness=\"0\"\n                    Foreground=\"{DynamicResource BrushTextHeader}\" FontSize=\"10\" FontWeight=\"SemiBold\" Padding=\"0\"/>\n            <Button Grid.Column=\"2\" Content=\"? Current\" Command=\"{Binding SortProcessesCommand}\" CommandParameter=\"CurrentUpload\"\n                    HorizontalAlignment=\"Left\" Background=\"{DynamicResource BrushTransparent}\" BorderThickness=\"0\"\n                    Foreground=\"{DynamicResource BrushTextHeader}\" FontSize=\"10\" FontWeight=\"SemiBold\" Padding=\"0\"/>\n            <Button Grid.Column=\"3\" Content=\"? Total\" Command=\"{Binding SortProcessesCommand}\" CommandParameter=\"TotalDownload\"\n                    HorizontalAlignment=\"Left\" Background=\"{DynamicResource BrushTransparent}\" BorderThickness=\"0\"\n                    Foreground=\"{DynamicResource BrushTextHeader}\" FontSize=\"10\" FontWeight=\"SemiBold\" Padding=\"0\"/>\n            <Button Grid.Column=\"4\" Content=\"? Total\" Command=\"{Binding SortProcessesCommand}\" CommandParameter=\"TotalUpload\"\n                    HorizontalAlignment=\"Left\" Background=\"{DynamicResource BrushTransparent}\" BorderThickness=\"0\"\n                    Foreground=\"{DynamicResource BrushTextHeader}\" FontSize=\"10\" FontWeight=\"SemiBold\" Padding=\"0\"/>\n          </Grid>\n\n          <ScrollViewer Grid.Row=\"1\" Margin=\"0,0,0,6\">\n            <ItemsControl ItemsSource=\"{Binding ActiveProcesses}\">\n              <ItemsControl.ItemTemplate>\n                <DataTemplate>\n                  <Grid ColumnDefinitions=\"3*,1.2*,1.2*,1.2*,1.2*\" Margin=\"10,4\">\n                    <Grid ColumnDefinitions=\"Auto,*\" ColumnSpacing=\"8\" MinWidth=\"0\">\n                      <Image Source=\"{Binding Icon}\" Width=\"16\" Height=\"16\"/>\n                      <TextBlock Grid.Column=\"1\" Text=\"{Binding ProcessName}\" Foreground=\"{DynamicResource BrushTextBody}\"\n                                 FontSize=\"11\" TextWrapping=\"Wrap\" MinWidth=\"0\"/>\n                    </Grid>\n                    <TextBlock Grid.Column=\"1\" Text=\"{Binding CurrentDownload}\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"11\" VerticalAlignment=\"Top\"/>\n                    <TextBlock Grid.Column=\"2\" Text=\"{Binding CurrentUpload}\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"11\" VerticalAlignment=\"Top\"/>\n                    <TextBlock Grid.Column=\"3\" Text=\"{Binding TotalDownload}\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"11\" VerticalAlignment=\"Top\"/>\n                    <TextBlock Grid.Column=\"4\" Text=\"{Binding TotalUpload}\" Foreground=\"{DynamicResource BrushTextBody}\" FontSize=\"11\" VerticalAlignment=\"Top\"/>\n                  </Grid>\n                </DataTemplate>\n              </ItemsControl.ItemTemplate>\n            </ItemsControl>\n          </ScrollViewer>\n        </Grid>\n      </Grid>\n    </Border>\n\n    <Border Grid.Row=\"1\" Grid.Column=\"0\" Grid.ColumnSpan=\"3\"\n            Padding=\"10\" CornerRadius=\"0,12,12,12\" Background=\"{DynamicResource BrushSurfacePanel}\"\n            BoxShadow=\"{DynamicResource ShadowStrong}\">\n      <lvc:CartesianChart Series=\"{Binding GraphSeries}\"\n                          Background=\"{DynamicResource BrushGraphBackground}\"\n                          XAxes=\"{Binding GraphXAxes}\"\n                          YAxes=\"{Binding GraphYAxes}\"\n                          ZoomMode=\"None\"\n                          TooltipPosition=\"Hidden\"\n                          AnimationsSpeed=\"00:00:00.150\"/>\n    </Border>\n  </Grid>\n</UserControl>\n"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow/Summary.axaml.cs",
    "content": "using Avalonia.Controls;\n\nnamespace OpenNetMeter.Views.MainWindowTabs;\n\npublic partial class SummaryView : UserControl\n{\n    public SummaryView()\n    {\n        InitializeComponent();\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow.axaml",
    "content": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:vm=\"clr-namespace:OpenNetMeter.ViewModels\"\n        xmlns:tabs=\"clr-namespace:OpenNetMeter.Views.MainWindowTabs\"\n        x:Class=\"OpenNetMeter.Views.MainWindow\"\n        x:DataType=\"vm:MainWindowViewModel\"\n        Width=\"900\"\n        Height=\"600\"\n        MinWidth=\"800\"\n        MinHeight=\"500\"\n        Title=\"OpenNetMeter\"\n        Icon=\"avares://OpenNetMeter/Assets/x48.png\"\n        ExtendClientAreaToDecorationsHint=\"True\"\n        ExtendClientAreaChromeHints=\"NoChrome\"\n        ExtendClientAreaTitleBarHeightHint=\"0\"\n        SystemDecorations=\"None\"\n        Background=\"{DynamicResource BrushTransparent}\"\n        TransparencyLevelHint=\"None\">\n  <Design.DataContext>\n    <vm:MainWindowViewModel/>\n  </Design.DataContext>\n\n  <Window.KeyBindings>\n    <KeyBinding Gesture=\"Escape\" Command=\"{Binding CloseAboutCommand}\"/>\n  </Window.KeyBindings>\n\n  <Window.Resources>\n    <ResourceDictionary>\n      <ResourceDictionary.MergedDictionaries>\n        <ResourceInclude Source=\"avares://OpenNetMeter/Views/Themes.axaml\"/>\n      </ResourceDictionary.MergedDictionaries>\n    </ResourceDictionary>\n  </Window.Resources>\n\n  <Grid>\n    <Border Background=\"{DynamicResource BrushBgWindow}\" BorderBrush=\"{DynamicResource BrushBorderMain}\" BorderThickness=\"1\" CornerRadius=\"0\">\n      <Grid RowDefinitions=\"50,*,30\">\n\n        <Border Grid.Row=\"0\" Background=\"{DynamicResource BrushBgTitleBar}\" IsHitTestVisible=\"True\">\n          <Grid ColumnDefinitions=\"Auto,Auto,Auto,Auto,*,Auto,Auto,Auto\">\n\n            <Image Grid.Column=\"0\" Source=\"avares://OpenNetMeter/Assets/x48.png\"\n                   Width=\"28\" Height=\"28\" Margin=\"14,0,0,0\"\n                   VerticalAlignment=\"Center\"/>\n\n            <Button Grid.Column=\"1\" Content=\"Summary\" Classes=\"tab\"\n                    Classes.active=\"{Binding IsSummaryTab}\"\n                    Command=\"{Binding SwitchTabCommand}\" CommandParameter=\"0\"\n                    Margin=\"16,0,4,0\"/>\n            <Button Grid.Column=\"2\" Content=\"History\" Classes=\"tab\"\n                    Classes.active=\"{Binding IsHistoryTab}\"\n                    Command=\"{Binding SwitchTabCommand}\" CommandParameter=\"1\"\n                    Margin=\"0,0,4,0\"/>\n            <Button Grid.Column=\"3\" Content=\"Settings\" Classes=\"tab\"\n                    Classes.active=\"{Binding IsSettingsTab}\"\n                    Command=\"{Binding SwitchTabCommand}\" CommandParameter=\"2\"/>\n\n            <Border Grid.Column=\"4\" Background=\"{DynamicResource BrushTransparent}\" IsHitTestVisible=\"True\"\n                    PointerPressed=\"TitleBar_PointerPressed\"/>\n\n            <Button Grid.Column=\"5\" Content=\"About\" Classes=\"titleBtn\"\n                    Command=\"{Binding AboutCommand}\"/>\n\n            <Button Grid.Column=\"6\" Classes=\"titleBtn\" Command=\"{Binding MinimizeWindowCommand}\">\n              <Panel Width=\"10\" Height=\"1\">\n                <Rectangle Fill=\"{DynamicResource BrushTextUi}\" Height=\"1\"/>\n              </Panel>\n            </Button>\n\n            <Button Grid.Column=\"7\" Classes=\"titleBtn closeBtn\" Command=\"{Binding CloseWindowCommand}\">\n              <Panel Width=\"10\" Height=\"10\">\n                <Line StartPoint=\"0,0\" EndPoint=\"10,10\" Stroke=\"{DynamicResource BrushTextUi}\" StrokeThickness=\"1.2\"/>\n                <Line StartPoint=\"10,0\" EndPoint=\"0,10\" Stroke=\"{DynamicResource BrushTextUi}\" StrokeThickness=\"1.2\"/>\n              </Panel>\n            </Button>\n          </Grid>\n        </Border>\n\n        <TabControl Grid.Row=\"1\"\n                    SelectedIndex=\"{Binding SelectedTabIndex, Mode=TwoWay}\"\n                    Margin=\"0\"\n                    Padding=\"0\">\n          <TabControl.Styles>\n            <Style Selector=\"TabControl /template/ ItemsPresenter#PART_ItemsPresenter\">\n              <Setter Property=\"IsVisible\" Value=\"False\"/>\n            </Style>\n          </TabControl.Styles>\n\n          <TabItem>\n            <tabs:SummaryView DataContext=\"{Binding Summary}\"/>\n          </TabItem>\n\n          <TabItem>\n            <tabs:HistoryView DataContext=\"{Binding History}\"/>\n          </TabItem>\n\n          <TabItem>\n            <tabs:SettingsView DataContext=\"{Binding Settings}\"/>\n          </TabItem>\n        </TabControl>\n\n        <Border Grid.Row=\"2\" Background=\"{DynamicResource BrushBgTitleBar}\" BorderBrush=\"{DynamicResource BrushBorderMain}\" BorderThickness=\"0,1,0,0\">\n          <Grid ColumnDefinitions=\"*,Auto,Auto,Auto,Auto\" Margin=\"10,0\">\n            <TextBlock Grid.Column=\"0\" Text=\"{Binding NetworkStatus}\" FontSize=\"12\"\n                       Foreground=\"{DynamicResource BrushTextUi}\" VerticalAlignment=\"Center\"/>\n\n            <TextBlock Grid.Column=\"1\" Text=\"Download Speed :\" FontSize=\"12\"\n                       Foreground=\"{DynamicResource BrushTextSecondaryAlt}\" VerticalAlignment=\"Center\" Margin=\"0,0,4,0\"/>\n            <TextBlock Grid.Column=\"2\" FontSize=\"12\" Foreground=\"{DynamicResource BrushTextUi}\"\n                       VerticalAlignment=\"Center\" Margin=\"0,0,16,0\"\n                       Text=\"{Binding Summary.DownloadSpeedText}\"/>\n\n            <TextBlock Grid.Column=\"3\" Text=\"Upload Speed :\" FontSize=\"12\"\n                       Foreground=\"{DynamicResource BrushTextSecondaryAlt}\" VerticalAlignment=\"Center\" Margin=\"0,0,4,0\"/>\n            <TextBlock Grid.Column=\"4\" FontSize=\"12\" Foreground=\"{DynamicResource BrushTextUi}\"\n                       VerticalAlignment=\"Center\"\n                       Text=\"{Binding Summary.UploadSpeedText}\"/>\n          </Grid>\n        </Border>\n      </Grid>\n    </Border>\n\n    <Border HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Top\" Height=\"6\" Background=\"{DynamicResource BrushTransparent}\"\n            Cursor=\"SizeNorthSouth\" PointerPressed=\"ResizeTop_PointerPressed\"/>\n    <Border HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Bottom\" Height=\"6\" Background=\"{DynamicResource BrushTransparent}\"\n            Cursor=\"SizeNorthSouth\" PointerPressed=\"ResizeBottom_PointerPressed\"/>\n    <Border HorizontalAlignment=\"Left\" VerticalAlignment=\"Stretch\" Width=\"6\" Background=\"{DynamicResource BrushTransparent}\"\n            Cursor=\"SizeWestEast\" PointerPressed=\"ResizeLeft_PointerPressed\"/>\n    <Border HorizontalAlignment=\"Right\" VerticalAlignment=\"Stretch\" Width=\"6\" Background=\"{DynamicResource BrushTransparent}\"\n            Cursor=\"SizeWestEast\" PointerPressed=\"ResizeRight_PointerPressed\"/>\n\n    <Border HorizontalAlignment=\"Left\" VerticalAlignment=\"Top\" Width=\"10\" Height=\"10\" Background=\"{DynamicResource BrushTransparent}\"\n            Cursor=\"TopLeftCorner\" PointerPressed=\"ResizeTopLeft_PointerPressed\"/>\n    <Border HorizontalAlignment=\"Right\" VerticalAlignment=\"Top\" Width=\"10\" Height=\"10\" Background=\"{DynamicResource BrushTransparent}\"\n            Cursor=\"TopRightCorner\" PointerPressed=\"ResizeTopRight_PointerPressed\"/>\n    <Border HorizontalAlignment=\"Left\" VerticalAlignment=\"Bottom\" Width=\"10\" Height=\"10\" Background=\"{DynamicResource BrushTransparent}\"\n            Cursor=\"BottomLeftCorner\" PointerPressed=\"ResizeBottomLeft_PointerPressed\"/>\n    <Border HorizontalAlignment=\"Right\" VerticalAlignment=\"Bottom\" Width=\"10\" Height=\"10\" Background=\"{DynamicResource BrushTransparent}\"\n            Cursor=\"BottomRightCorner\" PointerPressed=\"ResizeBottomRight_PointerPressed\"/>\n\n    <Border IsVisible=\"{Binding IsAboutOpen}\"\n            Background=\"{DynamicResource BrushOverlayScrim}\">\n      <Grid>\n        <Border Width=\"360\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"\n                BorderBrush=\"{DynamicResource BrushBorderMain}\" BorderThickness=\"1\"\n                Background=\"{DynamicResource BrushBgWindow}\">\n          <Grid RowDefinitions=\"Auto,*\">\n\n            <Border Grid.Row=\"0\" Background=\"{DynamicResource BrushBgTitleBar}\">\n              <Grid ColumnDefinitions=\"*,Auto\" Margin=\"12,0,0,10\">\n                <StackPanel Grid.Column=\"0\" Margin=\"0,10,0,0\" Orientation=\"Horizontal\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Spacing=\"10\">\n                  <Image Source=\"avares://OpenNetMeter/Assets/x48.png\"\n                         Width=\"48\" Height=\"48\"/>\n                  <TextBlock Text=\"OpenNetMeter\" FontSize=\"30\" FontWeight=\"Bold\"\n                             Foreground=\"{DynamicResource BrushTextPrimary}\"/>\n                </StackPanel>\n\n                <Button Grid.Column=\"1\" Classes=\"titleBtn closeBtn\" HorizontalAlignment=\"Right\" Command=\"{Binding CloseAboutCommand}\">\n                  <Panel Width=\"10\" Height=\"10\">\n                    <Line StartPoint=\"0,0\" EndPoint=\"10,10\" Stroke=\"{DynamicResource BrushTextUi}\" StrokeThickness=\"1.2\"/>\n                    <Line StartPoint=\"10,0\" EndPoint=\"0,10\" Stroke=\"{DynamicResource BrushTextUi}\" StrokeThickness=\"1.2\"/>\n                  </Panel>\n                </Button>\n              </Grid>\n            </Border>\n\n            <Border Grid.Row=\"1\" Background=\"{DynamicResource BrushBgSubtle}\" Padding=\"10\">\n              <Border BorderBrush=\"{DynamicResource BrushBorderSubtle}\" BorderThickness=\"1\"\n                      Background=\"{DynamicResource BrushBgSurface}\" Padding=\"12\">\n                <StackPanel Spacing=\"10\">\n                  <TextBlock Text=\"{Binding AboutVersionText}\"\n                             FontSize=\"{DynamicResource FontSizeMd}\"\n                             Foreground=\"{DynamicResource BrushTextPrimary}\"/>\n                  <TextBlock TextWrapping=\"Wrap\"\n                             Text=\"This product is created and maintained by Ashfaaq Riphque.\"\n                             Foreground=\"{DynamicResource BrushTextPrimary}\"/>\n                  <TextBlock TextWrapping=\"Wrap\"\n                             Text=\"If you find any problems with the product or have ideas for improvements, feel free to create an issue at:\"\n                             Foreground=\"{DynamicResource BrushTextPrimary}\"/>\n                  <Button Classes=\"linkBtn\"\n                          Command=\"{Binding OpenAboutRepositoryCommand}\"\n                          HorizontalAlignment=\"Left\">\n                    <TextBlock Text=\"{Binding AboutRepositoryUrl}\"\n                               Foreground=\"{DynamicResource BrushLink}\"\n                               TextDecorations=\"Underline\"/>\n                  </Button>\n                </StackPanel>\n              </Border>\n            </Border>\n\n          </Grid>\n        </Border>\n      </Grid>\n    </Border>\n\n    <Border IsVisible=\"{Binding Settings.IsDeleteConfirmationOpen}\"\n            Background=\"{DynamicResource BrushOverlayScrim}\">\n      <Grid>\n        <Border Width=\"360\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"\n                BorderBrush=\"{DynamicResource BrushBorderMain}\" BorderThickness=\"1\"\n                Background=\"{DynamicResource BrushBgWindow}\">\n          <Grid RowDefinitions=\"Auto,*\">\n\n            <Border Grid.Row=\"0\" Background=\"{DynamicResource BrushBgTitleBar}\">\n              <Grid ColumnDefinitions=\"*,Auto\">\n                <TextBlock Grid.Column=\"0\" Text=\"Confirm\" VerticalAlignment=\"Center\"\n                           Margin=\"15,0\" Foreground=\"{DynamicResource BrushTextPrimary}\"/>\n\n                <Button Grid.Column=\"1\" Classes=\"titleBtn closeBtn\" HorizontalAlignment=\"Right\"\n                        Command=\"{Binding Settings.CancelDeleteAllDataCommand}\">\n                  <Panel Width=\"10\" Height=\"10\">\n                    <Line StartPoint=\"0,0\" EndPoint=\"10,10\" Stroke=\"{DynamicResource BrushTextUi}\" StrokeThickness=\"1.2\"/>\n                    <Line StartPoint=\"10,0\" EndPoint=\"0,10\" Stroke=\"{DynamicResource BrushTextUi}\" StrokeThickness=\"1.2\"/>\n                  </Panel>\n                </Button>\n              </Grid>\n            </Border>\n\n            <Border Grid.Row=\"1\" Background=\"{DynamicResource BrushBgSubtle}\" Padding=\"20,10,20,20\">\n              <StackPanel Spacing=\"20\">\n                <TextBlock Text=\"{Binding Settings.DeleteConfirmationMessage}\"\n                           Foreground=\"{DynamicResource BrushTextPrimary}\"\n                           TextWrapping=\"Wrap\"/>\n\n                <StackPanel Orientation=\"Horizontal\" HorizontalAlignment=\"Center\" Spacing=\"20\">\n                  <Button MinWidth=\"60\" Padding=\"16,8\" Command=\"{Binding Settings.ConfirmDeleteAllDataCommand}\">\n                    <TextBlock Text=\"Yes\" HorizontalAlignment=\"Center\"/>\n                  </Button>\n                  <Button MinWidth=\"60\" Padding=\"16,8\" Command=\"{Binding Settings.CancelDeleteAllDataCommand}\">\n                    <TextBlock Text=\"No\" HorizontalAlignment=\"Center\"/>\n                  </Button>\n                </StackPanel>\n              </StackPanel>\n            </Border>\n\n          </Grid>\n        </Border>\n      </Grid>\n    </Border>\n  </Grid>\n\n  <Window.Styles>\n    <Style Selector=\"TextBlock\">\n      <Setter Property=\"FontSize\" Value=\"{DynamicResource FontSizeSm}\"/>\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushTextPrimary}\"/>\n    </Style>\n    <Style Selector=\"Button\">\n      <Setter Property=\"FontSize\" Value=\"{DynamicResource FontSizeSm}\"/>\n    </Style>\n    <Style Selector=\"ComboBox\">\n      <Setter Property=\"FontSize\" Value=\"{DynamicResource FontSizeSm}\"/>\n    </Style>\n    <Style Selector=\"DatePicker\">\n      <Setter Property=\"FontSize\" Value=\"{DynamicResource FontSizeSm}\" />\n      <Setter Property=\"Padding\" Value=\"2\" />\n    </Style>\n\n    <Style Selector=\"Button.tab\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushTransparent}\"/>\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushTextSecondary}\"/>\n      <Setter Property=\"FontSize\" Value=\"{DynamicResource FontSizeLg}\"/>\n      <Setter Property=\"Padding\" Value=\"12,6\"/>\n      <Setter Property=\"BorderThickness\" Value=\"0,0,0,3\"/>\n      <Setter Property=\"BorderBrush\" Value=\"{DynamicResource BrushTransparent}\"/>\n      <Setter Property=\"VerticalAlignment\" Value=\"Bottom\"/>\n      <Setter Property=\"Height\" Value=\"40\"/>\n      <Setter Property=\"CornerRadius\" Value=\"0\"/>\n    </Style>\n    <Style Selector=\"Button.tab /template/ ContentPresenter\">\n      <Setter Property=\"BorderThickness\" Value=\"0,0,0,3\"/>\n      <Setter Property=\"BorderBrush\" Value=\"{DynamicResource BrushTransparent}\"/>\n    </Style>\n    <Style Selector=\"Button.tab:pointerover /template/ ContentPresenter\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushBgSurfaceHover}\"/>\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushTextPrimary}\"/>\n    </Style>\n    <Style Selector=\"Button.tab.active\">\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushTabActiveText}\"/>\n      <Setter Property=\"BorderBrush\" Value=\"{DynamicResource BrushAccentGreen}\"/>\n    </Style>\n    <Style Selector=\"Button.tab.active /template/ ContentPresenter\">\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushTabActiveText}\"/>\n      <Setter Property=\"BorderBrush\" Value=\"{DynamicResource BrushAccentGreen}\"/>\n    </Style>\n\n    <Style Selector=\"Button.titleBtn\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushTransparent}\"/>\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushTextPrimary}\"/>\n      <Setter Property=\"Width\" Value=\"50\"/>\n      <Setter Property=\"Height\" Value=\"32\"/>\n      <Setter Property=\"VerticalAlignment\" Value=\"Top\"/>\n      <Setter Property=\"HorizontalContentAlignment\" Value=\"Center\"/>\n      <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n      <Setter Property=\"BorderThickness\" Value=\"0\"/>\n      <Setter Property=\"Padding\" Value=\"0\"/>\n      <Setter Property=\"CornerRadius\" Value=\"0\"/>\n    </Style>\n    <Style Selector=\"Button.titleBtn:pointerover /template/ ContentPresenter\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushBgSurfaceHover}\"/>\n    </Style>\n    <Style Selector=\"Button.closeBtn:pointerover /template/ ContentPresenter\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushDanger}\"/>\n    </Style>\n    <Style Selector=\"Button.linkBtn\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushTransparent}\"/>\n      <Setter Property=\"BorderThickness\" Value=\"0\"/>\n      <Setter Property=\"Padding\" Value=\"0\"/>\n      <Setter Property=\"CornerRadius\" Value=\"0\"/>\n      <Setter Property=\"HorizontalContentAlignment\" Value=\"Left\"/>\n    </Style>\n    <Style Selector=\"Button.linkBtn:pointerover /template/ ContentPresenter\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushTransparent}\"/>\n      <Setter Property=\"Opacity\" Value=\"0.85\"/>\n    </Style>\n    <Style Selector=\"DatePickerPresenter\">\n      <Setter Property=\"MinWidth\" Value=\"0\" />\n      <Setter Property=\"Width\" Value=\"140\" />\n    </Style>\n    <Style Selector=\"DatePickerPresenter /template/ DateTimePickerPanel > ListBoxItem\">\n      <Setter Property=\"FontSize\" Value=\"{DynamicResource FontSizeSm}\" />\n      <Setter Property=\"Padding\" Value=\"2,0\" />\n    </Style>\n\n    <Style Selector=\"RadioButton.pill\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushBgSubtle}\"/>\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushTextSecondary}\"/>\n      <Setter Property=\"CornerRadius\" Value=\"6\"/>\n      <Setter Property=\"Padding\" Value=\"16,8\"/>\n      <Setter Property=\"BorderBrush\" Value=\"{DynamicResource BrushBorderSubtle}\"/>\n      <Setter Property=\"BorderThickness\" Value=\"1\"/>\n      <Setter Property=\"Cursor\" Value=\"Hand\"/>\n      <Setter Property=\"Template\">\n        <ControlTemplate>\n          <Border Background=\"{TemplateBinding Background}\"\n                  BorderBrush=\"{TemplateBinding BorderBrush}\"\n                  BorderThickness=\"{TemplateBinding BorderThickness}\"\n                  CornerRadius=\"{TemplateBinding CornerRadius}\"\n                  Padding=\"{TemplateBinding Padding}\">\n            <ContentPresenter Content=\"{TemplateBinding Content}\"\n                              HorizontalAlignment=\"Center\"/>\n          </Border>\n        </ControlTemplate>\n      </Setter>\n    </Style>\n    <Style Selector=\"RadioButton.pill:pointerover\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushBorderMain}\"/>\n      <Setter Property=\"BorderBrush\" Value=\"{DynamicResource BrushAccentBlue}\"/>\n    </Style>\n    <Style Selector=\"RadioButton.pill:checked\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushPillCheckedBg}\"/>\n      <Setter Property=\"BorderBrush\" Value=\"{DynamicResource BrushAccentBlue}\"/>\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushAccentBlue}\"/>\n    </Style>\n    <Style Selector=\"RadioButton.pill:checked /template/ ContentPresenter\">\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushAccentBlue}\"/>\n    </Style>\n  </Window.Styles>\n</Window>\n"
  },
  {
    "path": "OpenNetMeter/Views/MainWindow.axaml.cs",
    "content": "using System;\nusing Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Input;\nusing Avalonia.Controls.Primitives;\nusing Avalonia.Threading;\nusing OpenNetMeter.Services;\nusing OpenNetMeter.ViewModels;\nusing OpenNetMeter.Properties;\n\nnamespace OpenNetMeter.Views;\n\npublic partial class MainWindow : Window\n{\n    private readonly DispatcherTimer resizeTimer;\n    private readonly DispatcherTimer relocationTimer;\n    private IMiniWidgetService? miniWidgetService;\n    private ITrayNotificationService? trayNotificationService;\n    private bool allowClose;\n\n    public MainWindow()\n    {\n        InitializeComponent();\n\n        resizeTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };\n        relocationTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };\n        resizeTimer.Tick += ResizeTimer_Tick;\n        relocationTimer.Tick += RelocationTimer_Tick;\n\n        Opened += MainWindow_Opened;\n        Closing += MainWindow_Closing;\n        PositionChanged += MainWindow_PositionChanged;\n        SizeChanged += MainWindow_SizeChanged;\n    }\n\n    public void InitializeWindowState(IMiniWidgetService miniWidgetService, ITrayNotificationService trayNotificationService)\n    {\n        this.miniWidgetService = miniWidgetService;\n        this.trayNotificationService = trayNotificationService;\n\n        if (SettingsManager.Current.MainWindowPositionInitialized)\n        {\n            WindowStartupLocation = WindowStartupLocation.Manual;\n            Width = Math.Max(MinWidth, SettingsManager.Current.WinWidth);\n            Height = Math.Max(MinHeight, SettingsManager.Current.WinHeight);\n            Position = new PixelPoint(SettingsManager.Current.WinPosX, SettingsManager.Current.WinPosY);\n            return;\n        }\n\n        WindowStartupLocation = WindowStartupLocation.CenterScreen;\n    }\n\n    public void OpenFromTray()\n    {\n        if (!IsVisible)\n            Show();\n\n        if (WindowState == WindowState.Minimized)\n            WindowState = WindowState.Normal;\n\n        Activate();\n    }\n\n    public void PrepareForExit()\n    {\n        allowClose = true;\n    }\n\n    public void ResetWindowPositions()\n    {\n        CenterOnPrimaryScreen();\n        SaveWindowGeometry();\n        miniWidgetService?.ResetPosition(this);\n    }\n\n    private void TitleBar_PointerPressed(object? sender, PointerPressedEventArgs e)\n    {\n        BeginMoveDrag(e);\n    }\n\n    private void ResizeTop_PointerPressed(object? sender, PointerPressedEventArgs e) => TryBeginResize(WindowEdge.North, e);\n    private void ResizeBottom_PointerPressed(object? sender, PointerPressedEventArgs e) => TryBeginResize(WindowEdge.South, e);\n    private void ResizeLeft_PointerPressed(object? sender, PointerPressedEventArgs e) => TryBeginResize(WindowEdge.West, e);\n    private void ResizeRight_PointerPressed(object? sender, PointerPressedEventArgs e) => TryBeginResize(WindowEdge.East, e);\n    private void ResizeTopLeft_PointerPressed(object? sender, PointerPressedEventArgs e) => TryBeginResize(WindowEdge.NorthWest, e);\n    private void ResizeTopRight_PointerPressed(object? sender, PointerPressedEventArgs e) => TryBeginResize(WindowEdge.NorthEast, e);\n    private void ResizeBottomLeft_PointerPressed(object? sender, PointerPressedEventArgs e) => TryBeginResize(WindowEdge.SouthWest, e);\n    private void ResizeBottomRight_PointerPressed(object? sender, PointerPressedEventArgs e) => TryBeginResize(WindowEdge.SouthEast, e);\n\n    private void TryBeginResize(WindowEdge edge, PointerPressedEventArgs e)\n    {\n        if (WindowState == WindowState.Normal)\n            BeginResizeDrag(edge, e);\n    }\n\n    protected override void OnClosed(System.EventArgs e)\n    {\n        resizeTimer.Stop();\n        relocationTimer.Stop();\n\n        if (DataContext is MainWindowViewModel vm)\n            vm.Dispose();\n\n        base.OnClosed(e);\n    }\n\n    private void MainWindow_Closing(object? sender, WindowClosingEventArgs e)\n    {\n        if (allowClose)\n            return;\n\n        e.Cancel = true;\n        trayNotificationService?.ShowMinimizedToTrayOnce(this);\n        Hide();\n    }\n\n    private void MainWindow_Opened(object? sender, EventArgs e)\n    {\n        if (!SettingsManager.Current.MainWindowPositionInitialized)\n        {\n            SaveWindowGeometry();\n            miniWidgetService?.ResetPosition(this);\n            return;\n        }\n\n        if (!IsWindowInBounds(this))\n        {\n            CenterOnPrimaryScreen();\n            SaveWindowGeometry();\n        }\n\n        miniWidgetService?.EnsurePositionOnScreen(this);\n    }\n\n    private void MainWindow_PositionChanged(object? sender, PixelPointEventArgs e)\n    {\n        if (WindowState != WindowState.Normal)\n            return;\n\n        RestartTimer(relocationTimer);\n    }\n\n    private void MainWindow_SizeChanged(object? sender, SizeChangedEventArgs e)\n    {\n        if (WindowState != WindowState.Normal)\n            return;\n\n        RestartTimer(resizeTimer);\n    }\n\n    private void ResizeTimer_Tick(object? sender, EventArgs e)\n    {\n        resizeTimer.Stop();\n        SaveWindowGeometry();\n    }\n\n    private void RelocationTimer_Tick(object? sender, EventArgs e)\n    {\n        relocationTimer.Stop();\n        SaveWindowGeometry();\n    }\n\n    private void SaveWindowGeometry()\n    {\n        if (WindowState != WindowState.Normal)\n            return;\n\n        SettingsManager.Current.WinPosX = Position.X;\n        SettingsManager.Current.WinPosY = Position.Y;\n        SettingsManager.Current.WinWidth = Math.Max((int)MinWidth, (int)Math.Round(Bounds.Width > 0 ? Bounds.Width : Width));\n        SettingsManager.Current.WinHeight = Math.Max((int)MinHeight, (int)Math.Round(Bounds.Height > 0 ? Bounds.Height : Height));\n        SettingsManager.Current.MainWindowPositionInitialized = true;\n        SettingsManager.Save();\n    }\n\n    private void CenterOnPrimaryScreen()\n    {\n        var screen = Screens?.Primary ?? Screens?.All[0];\n        if (screen == null)\n            return;\n\n        var width = Math.Max((int)MinWidth, (int)Math.Round(Bounds.Width > 0 ? Bounds.Width : Width));\n        var height = Math.Max((int)MinHeight, (int)Math.Round(Bounds.Height > 0 ? Bounds.Height : Height));\n        var area = screen.WorkingArea;\n\n        Position = new PixelPoint(\n            area.X + Math.Max(0, (area.Width - width) / 2),\n            area.Y + Math.Max(0, (area.Height - height) / 2));\n    }\n\n    private static void RestartTimer(DispatcherTimer timer)\n    {\n        timer.Stop();\n        timer.Start();\n    }\n\n    private static bool IsWindowInBounds(Window target)\n    {\n        var screens = target.Screens?.All;\n        if (screens is null || screens.Count == 0)\n            return true;\n\n        var width = Math.Max(1, (int)Math.Round(target.Bounds.Width > 0 ? target.Bounds.Width : target.Width));\n        var height = Math.Max(1, (int)Math.Round(target.Bounds.Height > 0 ? target.Bounds.Height : target.Height));\n        const int margin = 32;\n\n        foreach (var screen in screens)\n        {\n            var area = screen.WorkingArea;\n            var areaRight = area.X + area.Width;\n            var areaBottom = area.Y + area.Height;\n            var targetRight = target.Position.X + width;\n            var targetBottom = target.Position.Y + height;\n\n            if (area.X < targetRight - margin &&\n                areaRight > target.Position.X + margin &&\n                area.Y < targetBottom - margin &&\n                areaBottom > target.Position.Y + margin)\n            {\n                return true;\n            }\n        }\n\n        return false;\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter/Views/MiniWidgetWindow.axaml",
    "content": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:vm=\"clr-namespace:OpenNetMeter.ViewModels\"\n        x:Class=\"OpenNetMeter.Views.MiniWidgetWindow\"\n        x:DataType=\"vm:MiniWidgetViewModel\"\n        Width=\"260\"\n        Height=\"44\"\n        CanResize=\"False\"\n        SystemDecorations=\"None\"\n        ShowInTaskbar=\"False\"\n        Topmost=\"True\"\n        Background=\"Transparent\"\n        TransparencyLevelHint=\"Transparent\">\n  <Design.DataContext>\n    <vm:MiniWidgetViewModel />\n  </Design.DataContext>\n\n  <Window.Resources>\n    <ResourceDictionary>\n      <ResourceDictionary.ThemeDictionaries>\n        <ResourceDictionary x:Key=\"Dark\">\n          <SolidColorBrush x:Key=\"BrushWidgetBorder\" Color=\"#616161\"/>\n          <SolidColorBrush x:Key=\"BrushWidgetText\" Color=\"#F0F0F0\"/>\n          <SolidColorBrush x:Key=\"BrushWidgetTextMuted\" Color=\"#C8C8C8\"/>\n          <SolidColorBrush x:Key=\"BrushPinHover\" Color=\"#1AFFFFFF\"/>\n          <SolidColorBrush x:Key=\"BrushPinActive\" Color=\"#261A3A5C\"/>\n        </ResourceDictionary>\n        <ResourceDictionary x:Key=\"Light\">\n          <SolidColorBrush x:Key=\"BrushWidgetBorder\" Color=\"#C7CDD4\"/>\n          <SolidColorBrush x:Key=\"BrushWidgetText\" Color=\"#1F2937\"/>\n          <SolidColorBrush x:Key=\"BrushWidgetTextMuted\" Color=\"#6B7280\"/>\n          <SolidColorBrush x:Key=\"BrushPinHover\" Color=\"#14000000\"/>\n          <SolidColorBrush x:Key=\"BrushPinActive\" Color=\"#261A3A5C\"/>\n        </ResourceDictionary>\n      </ResourceDictionary.ThemeDictionaries>\n\n      <SolidColorBrush x:Key=\"BrushDownloadBadge\" Color=\"#367061\"/>\n      <SolidColorBrush x:Key=\"BrushUploadBadge\" Color=\"#D98868\"/>\n    </ResourceDictionary>\n  </Window.Resources>\n  <Window.Styles>\n    <Style Selector=\"TextBlock.speedValue\">\n      <Setter Property=\"FontSize\" Value=\"11\"/>\n      <Setter Property=\"FontWeight\" Value=\"SemiBold\"/>\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushWidgetText}\"/>\n      <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n    </Style>\n    <Style Selector=\"TextBlock.totalLabel\">\n      <Setter Property=\"FontSize\" Value=\"10\"/>\n      <Setter Property=\"FontWeight\" Value=\"SemiBold\"/>\n      <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n    </Style>\n    <Style Selector=\"TextBlock.totalValue\">\n      <Setter Property=\"FontSize\" Value=\"10\"/>\n      <Setter Property=\"Foreground\" Value=\"{DynamicResource BrushWidgetTextMuted}\"/>\n      <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n    </Style>\n    <Style Selector=\"ToggleButton.pinButton\">\n      <Setter Property=\"Width\" Value=\"18\"/>\n      <Setter Property=\"Height\" Value=\"18\"/>\n      <Setter Property=\"Background\" Value=\"Transparent\"/>\n      <Setter Property=\"BorderBrush\" Value=\"{DynamicResource BrushWidgetText}\"/>\n      <Setter Property=\"BorderThickness\" Value=\"1\"/>\n      <Setter Property=\"CornerRadius\" Value=\"2\"/>\n      <Setter Property=\"Padding\" Value=\"1\"/>\n    </Style>\n    <Style Selector=\"ToggleButton.pinButton:pointerover\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushPinHover}\"/>\n    </Style>\n    <Style Selector=\"ToggleButton.pinButton:checked\">\n      <Setter Property=\"Background\" Value=\"{DynamicResource BrushPinActive}\"/>\n    </Style>\n  </Window.Styles>\n\n  <Border Margin=\"1\"\n          CornerRadius=\"0\"\n          BorderThickness=\"1\"\n          BorderBrush=\"{DynamicResource BrushWidgetBorder}\"\n          Background=\"{Binding BackgroundColor}\"\n          Padding=\"4,2\"\n          PointerPressed=\"WidgetChrome_PointerPressed\">\n    <Border.ContextMenu>\n      <ContextMenu>\n        <MenuItem Header=\"Open\"\n                  Command=\"{Binding OpenMainWindowCommand}\"/>\n        <MenuItem Header=\"Hide\"\n                  Command=\"{Binding HideWidgetCommand}\"/>\n      </ContextMenu>\n    </Border.ContextMenu>\n\n    <Grid ColumnDefinitions=\"*,*,Auto\">\n      <StackPanel Grid.Column=\"0\"\n                  Orientation=\"Horizontal\"\n                  Spacing=\"6\"\n                  Margin=\"0,0,8,0\"\n                  VerticalAlignment=\"Center\">\n        <Border Width=\"20\"\n                Height=\"24\"\n                Background=\"{DynamicResource BrushDownloadBadge}\"\n                CornerRadius=\"4\"\n                VerticalAlignment=\"Center\">\n          <TextBlock Text=\"↓\"\n                     FontSize=\"16\"\n                     FontWeight=\"Bold\"\n                     Foreground=\"White\"\n                     HorizontalAlignment=\"Center\"\n                     VerticalAlignment=\"Center\"/>\n        </Border>\n        <StackPanel Spacing=\"1\"\n                    VerticalAlignment=\"Center\">\n          <TextBlock Classes=\"speedValue\"\n                     Text=\"{Binding DownloadSpeedText}\"/>\n          <StackPanel Orientation=\"Horizontal\"\n                      Spacing=\"3\">\n            <TextBlock Classes=\"totalLabel\"\n                       Text=\"Total:\"\n                       Foreground=\"{DynamicResource BrushDownloadBadge}\"/>\n            <TextBlock Classes=\"totalValue\"\n                       Text=\"{Binding CurrentSessionDownloadText}\"/>\n          </StackPanel>\n        </StackPanel>\n      </StackPanel>\n\n      <StackPanel Grid.Column=\"1\"\n                  Orientation=\"Horizontal\"\n                  Spacing=\"6\"\n                  Margin=\"0,0,8,0\"\n                  VerticalAlignment=\"Center\">\n        <Border Width=\"20\"\n                Height=\"24\"\n                Background=\"{DynamicResource BrushUploadBadge}\"\n                CornerRadius=\"4\"\n                VerticalAlignment=\"Center\">\n          <TextBlock Text=\"↑\"\n                     FontSize=\"16\"\n                     FontWeight=\"Bold\"\n                     Foreground=\"White\"\n                     HorizontalAlignment=\"Center\"\n                     VerticalAlignment=\"Center\"/>\n        </Border>\n        <StackPanel Spacing=\"1\"\n                    VerticalAlignment=\"Center\">\n          <TextBlock Classes=\"speedValue\"\n                     Text=\"{Binding UploadSpeedText}\"/>\n          <StackPanel Orientation=\"Horizontal\"\n                      Spacing=\"3\">\n            <TextBlock Classes=\"totalLabel\"\n                       Text=\"Total:\"\n                       Foreground=\"{DynamicResource BrushUploadBadge}\"/>\n            <TextBlock Classes=\"totalValue\"\n                       Text=\"{Binding CurrentSessionUploadText}\"/>\n          </StackPanel>\n        </StackPanel>\n      </StackPanel>\n\n      <ToggleButton Grid.Column=\"2\"\n                    Classes=\"pinButton\"\n                    Margin=\"2,0,0,0\"\n                    HorizontalAlignment=\"Right\"\n                    VerticalAlignment=\"Top\"\n                    IsChecked=\"{Binding IsPinned, Mode=TwoWay}\"\n                    ToolTip.Tip=\"{Binding PinToolTip}\">\n        <Image Source=\"{Binding PinIconSource}\"\n               Width=\"12\"\n               Height=\"12\"/>\n      </ToggleButton>\n    </Grid>\n  </Border>\n</Window>\n"
  },
  {
    "path": "OpenNetMeter/Views/MiniWidgetWindow.axaml.cs",
    "content": "using Avalonia.Controls;\nusing Avalonia.Input;\nusing Avalonia.Markup.Xaml;\nusing OpenNetMeter.ViewModels;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.Views;\n\npublic partial class MiniWidgetWindow : Window\n{\n    public MiniWidgetWindow()\n    {\n        InitializeComponent();\n    }\n\n    private void InitializeComponent()\n    {\n        AvaloniaXamlLoader.Load(this);\n    }\n\n    private void WidgetChrome_PointerPressed(object? sender, PointerPressedEventArgs e)\n    {\n        if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)\n            return;\n\n        if (DataContext is MiniWidgetViewModel { IsPinned: true })\n            return;\n\n        try\n        {\n            BeginMoveDrag(e);\n        }\n        catch (System.Exception ex)\n        {\n            EventLogger.Error(\"Failed to drag mini widget window\", ex);\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter/Views/Themes.axaml",
    "content": "<ResourceDictionary xmlns=\"https://github.com/avaloniaui\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n  <ResourceDictionary.ThemeDictionaries>\n    <ResourceDictionary x:Key=\"Dark\">\n      <SolidColorBrush x:Key=\"BrushBgWindow\" Color=\"#151515\"/>\n      <SolidColorBrush x:Key=\"BrushBgTitleBar\" Color=\"#202020\"/>\n      <SolidColorBrush x:Key=\"BrushBgSurface\" Color=\"#303030\"/>\n      <SolidColorBrush x:Key=\"BrushBgSurfaceHover\" Color=\"#2A2A2A\"/>\n      <SolidColorBrush x:Key=\"BrushBgSubtle\" Color=\"#252525\"/>\n      <SolidColorBrush x:Key=\"BrushBorderMain\" Color=\"#616161\"/>\n      <SolidColorBrush x:Key=\"BrushBorderSubtle\" Color=\"#555555\"/>\n      <SolidColorBrush x:Key=\"BrushTextPrimary\" Color=\"#FFFFFF\"/>\n      <SolidColorBrush x:Key=\"BrushTextSecondary\" Color=\"#A9ABAB\"/>\n      <SolidColorBrush x:Key=\"BrushSurfacePanel\" Color=\"#303030\"/>\n      <SolidColorBrush x:Key=\"BrushPanelAlt\" Color=\"#252525\"/>\n      <SolidColorBrush x:Key=\"BrushHeaderRow\" Color=\"#2A2A2A\"/>\n      <SolidColorBrush x:Key=\"BrushDivider\" Color=\"#555555\"/>\n      <SolidColorBrush x:Key=\"BrushBorderEmphasis\" Color=\"#616161\"/>\n      <SolidColorBrush x:Key=\"BrushTextUi\" Color=\"#F0F0F0\"/>\n      <SolidColorBrush x:Key=\"BrushTextBody\" Color=\"#F0F0F0\"/>\n      <SolidColorBrush x:Key=\"BrushTextMuted\" Color=\"#A9ABAB\"/>\n      <SolidColorBrush x:Key=\"BrushTextFaint\" Color=\"#6B7280\"/>\n      <SolidColorBrush x:Key=\"BrushTextHeader\" Color=\"#F0F0F0\"/>\n      <SolidColorBrush x:Key=\"BrushTabActiveText\" Color=\"#FFFFFF\"/>\n      <SolidColorBrush x:Key=\"BrushIconMuted\" Color=\"#A9ABAB\"/>\n      <SolidColorBrush x:Key=\"BrushTextSecondaryAlt\" Color=\"#A9ABAB\"/>\n      <SolidColorBrush x:Key=\"BrushDangerBorderSoft\" Color=\"#3D2020\"/>\n      <SolidColorBrush x:Key=\"BrushDangerBgSoft\" Color=\"#1A1215\"/>\n      <SolidColorBrush x:Key=\"BrushPillCheckedBg\" Color=\"#1A3A5C\"/>\n      <SolidColorBrush x:Key=\"BrushGraphBackground\" Color=\"#252525\"/>\n      <SolidColorBrush x:Key=\"BrushLink\" Color=\"#5FA8FF\"/>\n      <SolidColorBrush x:Key=\"BrushShadowStrong\" Color=\"#33000000\"/>\n      <SolidColorBrush x:Key=\"BrushShadowSoft\" Color=\"#20000000\"/>\n      <SolidColorBrush x:Key=\"BrushAccentGreen\" Color=\"#367061\"/>\n      <SolidColorBrush x:Key=\"BrushAccentBlue\" Color=\"#00A6A0\"/>\n      <SolidColorBrush x:Key=\"BrushSuccess\" Color=\"#367061\"/>\n      <SolidColorBrush x:Key=\"BrushInfo\" Color=\"#D98868\"/>\n      <SolidColorBrush x:Key=\"BrushWarning\" Color=\"#FFA37E\"/>\n      <SolidColorBrush x:Key=\"BrushAccentPurple\" Color=\"#8957E5\"/>\n      <SolidColorBrush x:Key=\"BrushDangerBright\" Color=\"#E81123\"/>\n      <BoxShadows x:Key=\"ShadowStrong\">0 2 10 0 #33000000</BoxShadows>\n      <BoxShadows x:Key=\"ShadowSoft\">0 1 4 0 #20000000</BoxShadows>\n    </ResourceDictionary>\n    <ResourceDictionary x:Key=\"Light\">\n      <SolidColorBrush x:Key=\"BrushBgWindow\" Color=\"#eaeaea\"/>\n      <SolidColorBrush x:Key=\"BrushBgTitleBar\" Color=\"#FFFFFF\"/>\n      <SolidColorBrush x:Key=\"BrushBgSurface\" Color=\"#FFFFFF\"/>\n      <SolidColorBrush x:Key=\"BrushBgSurfaceHover\" Color=\"#F9FAFB\"/>\n      <SolidColorBrush x:Key=\"BrushBgSubtle\" Color=\"#F8F8F8\"/>\n      <SolidColorBrush x:Key=\"BrushBorderMain\" Color=\"#4F5050\"/>\n      <SolidColorBrush x:Key=\"BrushBorderSubtle\" Color=\"#E5E7EB\"/>\n      <SolidColorBrush x:Key=\"BrushTextPrimary\" Color=\"#000000\"/>\n      <SolidColorBrush x:Key=\"BrushTextSecondary\" Color=\"#666666\"/>\n      <SolidColorBrush x:Key=\"BrushSurfacePanel\" Color=\"#FFFFFF\"/>\n      <SolidColorBrush x:Key=\"BrushPanelAlt\" Color=\"#d8d8d8\"/>\n      <SolidColorBrush x:Key=\"BrushHeaderRow\" Color=\"#F0F0F0\"/>\n      <SolidColorBrush x:Key=\"BrushDivider\" Color=\"#E5E7EB\"/>\n      <SolidColorBrush x:Key=\"BrushBorderEmphasis\" Color=\"#888888\"/>\n      <SolidColorBrush x:Key=\"BrushTextUi\" Color=\"#000000\"/>\n      <SolidColorBrush x:Key=\"BrushTextBody\" Color=\"#1F2937\"/>\n      <SolidColorBrush x:Key=\"BrushTextMuted\" Color=\"#6B7280\"/>\n      <SolidColorBrush x:Key=\"BrushTextFaint\" Color=\"#AAAAAA\"/>\n      <SolidColorBrush x:Key=\"BrushTextHeader\" Color=\"#1A1A1A\"/>\n      <SolidColorBrush x:Key=\"BrushTabActiveText\" Color=\"#000000\"/>\n      <SolidColorBrush x:Key=\"BrushIconMuted\" Color=\"#6B7280\"/>\n      <SolidColorBrush x:Key=\"BrushTextSecondaryAlt\" Color=\"#4F5050\"/>\n      <SolidColorBrush x:Key=\"BrushDangerBorderSoft\" Color=\"#E5BABA\"/>\n      <SolidColorBrush x:Key=\"BrushDangerBgSoft\" Color=\"#FCEDED\"/>\n      <SolidColorBrush x:Key=\"BrushPillCheckedBg\" Color=\"#F0FDFA\"/>\n      <SolidColorBrush x:Key=\"BrushGraphBackground\" Color=\"#FFFFFF\"/>\n      <SolidColorBrush x:Key=\"BrushLink\" Color=\"#4287F5\"/>\n      <SolidColorBrush x:Key=\"BrushShadowStrong\" Color=\"#1ADDDDDD\"/>\n      <SolidColorBrush x:Key=\"BrushShadowSoft\" Color=\"#14000000\"/>\n      <SolidColorBrush x:Key=\"BrushAccentGreen\" Color=\"#7FB6A8\"/>\n      <SolidColorBrush x:Key=\"BrushAccentBlue\" Color=\"#00A6A0\"/>\n      <SolidColorBrush x:Key=\"BrushSuccess\" Color=\"#6DA395\"/>\n      <SolidColorBrush x:Key=\"BrushInfo\" Color=\"#C97C5D\"/>\n      <SolidColorBrush x:Key=\"BrushWarning\" Color=\"#FFA37E\"/>\n      <SolidColorBrush x:Key=\"BrushAccentPurple\" Color=\"#8957E5\"/>\n      <SolidColorBrush x:Key=\"BrushDangerBright\" Color=\"#E81123\"/>\n      <BoxShadows x:Key=\"ShadowStrong\">0 2 10 0 #1ADDDDDD</BoxShadows>\n      <BoxShadows x:Key=\"ShadowSoft\">0 1 4 0 #14000000</BoxShadows>\n    </ResourceDictionary>\n  </ResourceDictionary.ThemeDictionaries>\n\n  <SolidColorBrush x:Key=\"BrushTransparent\" Color=\"Transparent\"/>\n  <SolidColorBrush x:Key=\"BrushWhite\" Color=\"#FFFFFF\"/>\n  <SolidColorBrush x:Key=\"BrushDanger\" Color=\"#E81123\"/>\n  <SolidColorBrush x:Key=\"BrushOverlayScrim\" Color=\"#A0000000\"/>\n\n  <x:Double x:Key=\"FontSizeXs\">10</x:Double>\n  <x:Double x:Key=\"FontSizeSm\">11</x:Double>\n  <x:Double x:Key=\"FontSizeMd\">12</x:Double>\n  <x:Double x:Key=\"FontSizeLg\">13</x:Double>\n</ResourceDictionary>\n"
  },
  {
    "path": "OpenNetMeter/app.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <assemblyIdentity version=\"1.0.0.0\" name=\"OpenNetMeter.app\"/>\n  <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <security>\n      <requestedPrivileges>\n        <requestedExecutionLevel level=\"requireAdministrator\" uiAccess=\"false\" />\n      </requestedPrivileges>\n    </security>\n  </trustInfo>\n</assembly>\n"
  },
  {
    "path": "OpenNetMeter.Core/OpenNetMeter.Core.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\OpenNetMeter.PlatformAbstractions\\OpenNetMeter.PlatformAbstractions.csproj\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "OpenNetMeter.Core/ViewModels/ConfirmationDialogVM.cs",
    "content": "﻿using System.ComponentModel;\nusing System.Windows.Input;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.ViewModels\n{\n    public class ConfirmationDialogVM : INotifyPropertyChanged\n    {\n        private UiVisibility isVisible;\n\n        public UiVisibility IsVisible\n        {\n            get => isVisible;\n            set\n            {\n                isVisible = value;\n                OnPropertyChanged(nameof(IsVisible));\n            }\n        }\n\n        public string? DialogMessage { get; set; }\n\n        public ICommand? BtnCommand { get; set; }\n\n        public ConfirmationDialogVM()\n        {\n            IsVisible = UiVisibility.Hidden;\n        }\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n\n        private void OnPropertyChanged(string propName) =>\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Core/ViewModels/MainShellTabsViewModel.cs",
    "content": "using System.ComponentModel;\n\nnamespace OpenNetMeter.Core.ViewModels;\n\npublic class MainShellTabsViewModel : INotifyPropertyChanged\n{\n    private int selectedTabIndex;\n\n    public virtual int SelectedTabIndex\n    {\n        get => selectedTabIndex;\n        set\n        {\n            if (selectedTabIndex == value)\n                return;\n\n            selectedTabIndex = value;\n            OnPropertyChanged(nameof(SelectedTabIndex));\n        }\n    }\n\n    public event PropertyChangedEventHandler? PropertyChanged;\n\n    protected void OnPropertyChanged(string propertyName) =>\n        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/DatabaseEngine/Connection.cs",
    "content": "﻿using System.Data.SQLite;\nusing System.IO;\n\nnamespace DatabaseEngine\n{\n    internal class Connection\n    {\n        public string ConnectionString { get; set; }\n        public Connection(string path, string dbFileName)\n        {\n            var filePath = Path.Combine(path, dbFileName + \".sqlite\");\n            ConnectionString = @\"Data Source=\" + Path.Combine(path, dbFileName + \".sqlite;\" + \" foreign keys=true\");\n            if (!File.Exists(filePath))\n            {\n                SQLiteConnection.CreateFile(filePath);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/DatabaseEngine/Database.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Data.SQLite;\nusing System.Diagnostics;\n\nnamespace DatabaseEngine\n{\n    public class Database : IDisposable\n    {\n        private SQLiteConnection? connection;\n        private bool disposed = false;\n\n        /// <summary>\n        /// Creates and opens a database connection.\n        /// </summary>\n        /// <param name=\"path\">Directory path for the database file</param>\n        /// <param name=\"dbFileName\">Database file name (without extension)</param>\n        /// <param name=\"extraParams\">Additional connection string parameters</param>\n        public Database(string path, string dbFileName, string[]? extraParams = null)\n        {\n            string connectionString = new Connection(path, dbFileName).ConnectionString;\n\n            // Append any extra parameters to connection string\n            if (extraParams != null)\n            {\n                foreach (var param in extraParams)\n                {\n                    connectionString += $\";{param}\";\n                }\n            }\n\n            connection = new SQLiteConnection(connectionString);\n            connection.Open();\n        }\n\n        /// <summary>\n        /// Configures the database for optimal concurrent access.\n        /// Call once after opening the connection.\n        /// </summary>\n        public void ConfigureForConcurrency()\n        {\n            // WAL mode allows concurrent reads during writes\n            RunSQLiteNonQuery(\"PRAGMA journal_mode=WAL;\");\n\n            // Wait up to 5 seconds if database is locked instead of failing immediately\n            RunSQLiteNonQuery(\"PRAGMA busy_timeout=5000;\");\n\n            // Good balance of durability and performance with WAL\n            RunSQLiteNonQuery(\"PRAGMA synchronous=NORMAL;\");\n        }\n\n        /// <summary>\n        /// Executes a non-query SQL command (INSERT, UPDATE, DELETE, CREATE, etc.)\n        /// </summary>\n        /// <param name=\"query\">SQL query string</param>\n        /// <returns>Number of rows affected, or negative value on error</returns>\n        public int RunSQLiteNonQuery(string query)\n        {\n            if (connection == null) return -2;\n\n            try\n            {\n                using var command = new SQLiteCommand(query, connection);\n                return command.ExecuteNonQuery();\n            }\n            catch (Exception ex)\n            {\n                Debug.WriteLine($\"SQLite Error: {ex.Message}\");\n                return -1;\n            }\n        }\n\n        /// <summary>\n        /// Executes a non-query SQL command with parameters.\n        /// </summary>\n        /// <param name=\"query\">SQL query string with parameter placeholders</param>\n        /// <param name=\"paramAndValue\">2D array of parameter names and values</param>\n        /// <returns>Number of rows affected, or negative value on error</returns>\n        public int RunSQLiteNonQuery(string query, string[,] paramAndValue)\n        {\n            if (connection == null) return -2;\n\n            try\n            {\n                using var command = new SQLiteCommand(query, connection);\n\n                for (int i = 0; i < paramAndValue.GetLength(0); i++)\n                {\n                    command.Parameters.AddWithValue(paramAndValue[i, 0], paramAndValue[i, 1]);\n                }\n\n                command.Prepare();\n                return command.ExecuteNonQuery();\n            }\n            catch (Exception ex)\n            {\n                Debug.WriteLine($\"SQLite Error: {ex.Message}\");\n                return -1;\n            }\n        }\n\n        /// <summary>\n        /// Executes multiple non-query commands within a single transaction.\n        /// More efficient for batch operations.\n        /// </summary>\n        /// <param name=\"action\">Action that performs the database operations</param>\n        /// <returns>True if transaction committed successfully</returns>\n        public bool RunInTransaction(Action<Database> action)\n        {\n            if (connection == null) return false;\n\n            using var transaction = connection.BeginTransaction();\n            try\n            {\n                action(this);\n                transaction.Commit();\n                return true;\n            }\n            catch (Exception ex)\n            {\n                Debug.WriteLine($\"SQLite Transaction Error: {ex.Message}\");\n                transaction.Rollback();\n                return false;\n            }\n        }\n\n        /// <summary>\n        /// Executes a query and returns all rows/columns as a list of lists.\n        /// </summary>\n        public List<List<object>> GetMultipleCellData(string query)\n        {\n            var result = new List<List<object>>();\n\n            if (connection == null) return result;\n\n            try\n            {\n                using var command = new SQLiteCommand(query, connection);\n                using var reader = command.ExecuteReader();\n\n                while (reader.Read())\n                {\n                    var row = new List<object>();\n                    for (int i = 0; i < reader.FieldCount; i++)\n                    {\n                        row.Add(reader[i]);\n                    }\n                    result.Add(row);\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.WriteLine($\"SQLite read error: {ex.Message}\");\n            }\n\n            return result;\n        }\n\n        /// <summary>\n        /// Executes a parameterized query and returns all rows/columns as a list of lists.\n        /// </summary>\n        public List<List<object>> GetMultipleCellData(string query, string[,] paramAndValue)\n        {\n            var result = new List<List<object>>();\n\n            if (connection == null) return result;\n\n            try\n            {\n                using var command = new SQLiteCommand(query, connection);\n\n                for (int i = 0; i < paramAndValue.GetLength(0); i++)\n                {\n                    command.Parameters.AddWithValue(paramAndValue[i, 0], paramAndValue[i, 1]);\n                }\n\n                command.Prepare();\n\n                using var reader = command.ExecuteReader();\n                while (reader.Read())\n                {\n                    var row = new List<object>();\n                    for (int i = 0; i < reader.FieldCount; i++)\n                    {\n                        row.Add(reader[i]);\n                    }\n                    result.Add(row);\n                }\n            }\n            catch (Exception ex)\n            {\n                Debug.WriteLine($\"SQLite read error: {ex.Message}\");\n            }\n\n            return result;\n        }\n\n        /// <summary>\n        /// Executes a parameterized query and returns the first cell of the first row.\n        /// </summary>\n        public object? GetSingleCellData(string query, string[,] paramAndValue)\n        {\n            if (connection == null) return null;\n\n            try\n            {\n                using var command = new SQLiteCommand(query, connection);\n\n                for (int i = 0; i < paramAndValue.GetLength(0); i++)\n                {\n                    command.Parameters.AddWithValue(paramAndValue[i, 0], paramAndValue[i, 1]);\n                }\n\n                command.Prepare();\n                return command.ExecuteScalar();\n            }\n            catch (Exception ex)\n            {\n                Debug.WriteLine($\"SQLite read error: {ex.Message}\");\n                return null;\n            }\n        }\n\n        public void Dispose()\n        {\n            if (disposed) return;\n            disposed = true;\n\n            connection?.Dispose();\n            connection = null;\n        }\n    }\n}"
  },
  {
    "path": "OpenNetMeter.Old/DatabaseEngine/DatabaseEngine.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0-windows</TargetFramework>\n    <Nullable>enable</Nullable>\n    <UseWPF>true</UseWPF>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"System.Data.SQLite.Core\" Version=\"1.0.116\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/App.xaml",
    "content": "﻿<Application x:Class=\"OpenNetMeter.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:local=\"clr-namespace:OpenNetMeter\"\n             xmlns:v=\"clr-namespace:OpenNetMeter.Views\"\n             Startup=\"Application_Startup\"\n             Exit=\"Application_Exit\">\n    <Application.Resources>\n        <ResourceDictionary>\n            <v:UiVisibilityToWpfVisibilityConverter x:Key=\"UiVisibilityToWpfVisibilityConverter\"/>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary Source=\"Views/ResourceDictionaries/ThemeResources.xaml\"/>\n            </ResourceDictionary.MergedDictionaries>\n        </ResourceDictionary>\n    </Application.Resources>\n\n</Application>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/App.xaml.cs",
    "content": "﻿using OpenNetMeter.Views;\nusing System.Windows;\nusing OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing System;\n\nnamespace OpenNetMeter\n{\n    /// <summary>\n    /// Interaction logic for App.xaml\n    /// </summary>\n    public partial class App : Application\n    {\n        private void Application_Startup(object sender, StartupEventArgs e)\n        {\n            //log any unhandled exceptions\n            AppDomain.CurrentDomain.UnhandledException += (s, ex) =>\n            {\n                EventLogger.Error(\"Unhandled exception\", (Exception)ex.ExceptionObject);\n            };\n\n            EventLogger.Info(\"Application starting\");\n\n            bool startMinimized = false;\n            for (int i = 0; i != e.Args.Length; ++i)\n            {\n                if (e.Args[i] == \"/StartMinimized\")\n                    startMinimized = true;\n            }\n            MainWindow window = new MainWindow();\n            if (startMinimized)\n                window.Exit_Button_Click(null, null);\n            else\n                window.Show();\n        }\n\n        private void Application_Exit(object sender, ExitEventArgs e)\n        {\n            EventLogger.Info(\"Application exiting\");\n            SettingsManager.Save();\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/AssemblyInfo.cs",
    "content": "using System.Windows;\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n                                     //(used if a resource is not found in the page,\n                                     // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n                                              //(used if a resource is not found in the page,\n                                              // app, or any theme specific resource dictionaries)\n)]\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/ApplicationDB.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing DatabaseEngine;\n\nnamespace OpenNetMeter.Models\n{\n    /// <summary>\n    /// Provides access to the application's SQLite database for storing network usage data.\n    /// Uses a singleton connection pattern to prevent database locking issues.\n    /// \n    /// Thread-safe: All write operations are serialized via a lock.\n    /// </summary>\n    internal class ApplicationDB : IDisposable\n    {\n        private static string LogTime() => DateTime.Now.ToString(\"HH:mm:ss.fff\");\n\n        //\n        //                                        THE ER DIAGRAM\n        //                                      ------------------\n        //\n        //  |-----------------------|       |-----------------------|       |-----------------------|    \n        //  |       Process         |       |      ProcessDate      |       |         Date          |       \n        //  |-----------------------|       |-----------------------|       |-----------------------|\n        //  |   PK  |   ID          |---|   |   PK  |   ID          |   |---|   PK  |   ID          |\n        //  |-------|---------------|   |   |-------|---------------|   |   |-------|---------------|\n        //  |       |   Name        |   |==+|   FK  |   ProcessID   |   |   |       |   Year        |\n        //  |-------|---------------|       |-------|---------------|   |   |-------|---------------|\n        //                                  |   FK  |   DateID      |+==|   |       |   Month       |\n        //                                  |-------|---------------|       |-------|---------------|\n        //                                  |       |   DataReceived|       |       |   Day         |\n        //                                  |-------|---------------|       |-------|---------------|\n        //                                  |       |   DataSent    |\n        //                                  |-------|---------------|\n        //             \n\n        //---------- Singleton Connection ------------//\n\n        private static Database? sharedDb;\n        private static readonly object dbLock = new object();\n        private static int refCount = 0;\n\n        //---------- Constants ------------//\n\n        public const string UnifiedDBFileName = \"OpenNetMeter\";\n        public const int DataStoragePeriodInDays = 60;\n\n        //---------- Instance State ------------//\n\n        private readonly string adapterName;\n        private bool disposed = false;\n\n        //---------- Constructor / Disposal ------------//\n\n        /// <summary>\n        /// Creates a database accessor for the specified adapter.\n        /// The underlying connection is shared across all instances.\n        /// </summary>\n        /// <param name=\"dBFileName\">Adapter name (used for filtering data)</param>\n        /// <param name=\"extraParams\">Additional connection string parameters (optional)</param>\n        public ApplicationDB(string dBFileName, string[]? extraParams = null)\n        {\n            adapterName = dBFileName;\n\n            lock (dbLock)\n            {\n                if (sharedDb == null)\n                {\n                    Debug.WriteLine(\"ApplicationDB: Creating shared database connection\");\n                    sharedDb = new Database(Properties.Global.GetFilePath(), UnifiedDBFileName, extraParams);\n                    sharedDb.ConfigureForConcurrency();\n                }\n                refCount++;\n            }\n        }\n\n        public static string GetUnifiedDBFullPath()\n        {\n            return Path.Combine(Properties.Global.GetFilePath(), UnifiedDBFileName + \".sqlite\");\n        }\n\n        public void Dispose()\n        {\n            if (disposed) return;\n            disposed = true;\n\n            lock (dbLock)\n            {\n                refCount--;\n                if (refCount == 0 && sharedDb != null)\n                {\n                    Debug.WriteLine(\"ApplicationDB: Closing shared database connection\");\n                    sharedDb.Dispose();\n                    sharedDb = null;\n                }\n            }\n        }\n\n        //---------- Helper to Access Shared DB ------------//\n\n        /// <summary>\n        /// Gets the shared database instance. Throws if not initialized.\n        /// </summary>\n        private static Database DB\n        {\n            get\n            {\n                if (sharedDb == null)\n                    throw new InvalidOperationException(\"Database not initialized\");\n                return sharedDb;\n            }\n        }\n\n        //---------- Public Write Operations (Thread-Safe) ------------//\n\n        /// <summary>\n        /// Pushes process network data to the database.\n        /// Thread-safe: serialized with other write operations.\n        /// </summary>\n        public void PushToDB(string processName, long totalDataRecv, long totalDataSend)\n        {\n            lock (dbLock)\n            {\n                Console.WriteLine($\"[{LogTime()}] [SQLite][WPF] PushToDB adapter='{adapterName}' process='{processName}' recv={totalDataRecv} sent={totalDataSend}\");\n                InsertUniqueRow_ProcessTable(processName);\n                InsertUniqueRow_AdapterTable(adapterName);\n\n                long dateID = GetID_DateTable(DateTime.Today);\n                long processID = GetID_ProcessTable(processName);\n                long adapterID = GetID_AdapterTable(adapterName);\n\n                // If insert fails (row exists), update instead\n                if (InsertUniqueRow_ProcessDateTable(processID, dateID, adapterID, totalDataRecv, totalDataSend) < 1)\n                {\n                    UpdateRow_ProcessDateTable(processID, dateID, adapterID, totalDataRecv, totalDataSend);\n                }\n            }\n        }\n\n        /// <summary>\n        /// Creates all required tables if they don't exist.\n        /// Thread-safe: serialized with other write operations.\n        /// </summary>\n        public int CreateTable()\n        {\n            lock (dbLock)\n            {\n                Console.WriteLine($\"[{LogTime()}] [SQLite][WPF] CreateTable adapter='{adapterName}' db='{GetUnifiedDBFullPath()}'\");\n                // If any function returns negative, result will be negative\n                return CreateAdapterTable() >> 31 |\n                       CreateProcessTable() >> 31 |\n                       CreateDateTable() >> 31 |\n                       CreateProcessDateTable() >> 31;\n            }\n        }\n\n        /// <summary>\n        /// Inserts today's date and removes old data beyond retention period.\n        /// Thread-safe: serialized with other write operations.\n        /// </summary>\n        public void UpdateDatesInDB()\n        {\n            lock (dbLock)\n            {\n                Console.WriteLine($\"[{LogTime()}] [SQLite][WPF] UpdateDatesInDB adapter='{adapterName}' today='{DateTime.Today:yyyy-MM-dd}' retentionDays={DataStoragePeriodInDays}\");\n                InsertUniqueRow_DateTable(DateTime.Today);\n                RemoveOldDate();\n                RemoveOldProcess();\n            }\n        }\n\n        /// <summary>\n        /// Ensures the adapter exists in the Adapter table.\n        /// Thread-safe: serialized with other write operations.\n        /// </summary>\n        public int InsertUniqueRow_AdapterTable(string adapter)\n        {\n            lock (dbLock)\n            {\n                Console.WriteLine($\"[{LogTime()}] [SQLite][WPF] InsertUniqueRow_AdapterTable adapter='{adapter}'\");\n                return DB.RunSQLiteNonQuery(\n                    \"INSERT OR IGNORE INTO Adapter(Name) VALUES(@Name)\",\n                    new string[,] { { \"@Name\", adapter } });\n            }\n        }\n\n        //---------- Public Read Operations ------------//\n        // Reads don't need the lock with WAL mode, but we use it for safety\n\n        public List<List<object>> GetDataSum_ProcessDateTable(DateTime date1, DateTime date2)\n        {\n            lock (dbLock)\n            {\n                return DB.GetMultipleCellData(\n                    \"SELECT p1.Name, SUM(pd1.DataReceived), SUM(pd1.DataSent) \" +\n                    \"FROM ProcessDate pd1 \" +\n                    \"JOIN Process p1 ON p1.ID = pd1.ProcessID \" +\n                    \"WHERE DateID IN \" +\n                    \"(SELECT ID FROM Date WHERE \" +\n                    \"(Year * 10000 + Month * 100 + Day) \" +\n                    \"BETWEEN \" +\n                    $\"({date1.Year * 10000 + date1.Month * 100 + date1.Day}) \" +\n                    \"AND \" +\n                    $\"({date2.Year * 10000 + date2.Month * 100 + date2.Day})) \" +\n                    \"AND AdapterID = @AdapterID \" +\n                    \"GROUP BY ProcessID\",\n                    new string[,] { { \"@AdapterID\", GetID_AdapterTable_Internal(adapterName).ToString() } });\n            }\n        }\n\n        public (long, long) GetDataSumBetweenDates(DateTime startDate, DateTime endDate)\n        {\n            DateTime fromDate = startDate.Date;\n            DateTime toDate = endDate.Date;\n\n            if (toDate < fromDate)\n            {\n                (fromDate, toDate) = (toDate, fromDate);\n            }\n\n            lock (dbLock)\n            {\n                var sum = DB.GetMultipleCellData(\n                    \"SELECT SUM(DataReceived), SUM(DataSent) FROM ProcessDate \" +\n                    \"WHERE DateID IN \" +\n                    \"(SELECT ID FROM Date \" +\n                    \"WHERE (Year * 10000 + Month * 100 + Day) \" +\n                    \"BETWEEN \" +\n                    $\"({fromDate.Year * 10000 + fromDate.Month * 100 + fromDate.Day}) \" +\n                    \"AND \" +\n                    $\"({toDate.Year * 10000 + toDate.Month * 100 + toDate.Day})) \" +\n                    \"AND AdapterID = @AdapterID\",\n                    new string[,] { { \"@AdapterID\", GetID_AdapterTable_Internal(adapterName).ToString() } });\n\n                if (sum.Count == 1 && sum[0].Count == 2)\n                {\n                    long download = Convert.IsDBNull(sum[0][0]) ? 0 : Convert.ToInt64(sum[0][0]);\n                    long upload = Convert.IsDBNull(sum[0][1]) ? 0 : Convert.ToInt64(sum[0][1]);\n                    return (download, upload);\n                }\n            }\n\n            return (0, 0);\n        }\n\n        public (long, long) GetTodayDataSum_ProcessDateTable()\n        {\n            lock (dbLock)\n            {\n                var sum = DB.GetMultipleCellData(\n                    \"SELECT SUM(DataReceived), SUM(DataSent) FROM ProcessDate \" +\n                    \"WHERE DateID IN \" +\n                    \"(SELECT ID FROM Date \" +\n                    \"WHERE (Year * 10000 + Month * 100 + day) = (@Year * 10000 + @Month * 100 + @Day)) \" +\n                    \"AND AdapterID = @AdapterID\",\n                    new string[,]\n                    {\n                        { \"@Year\", DateTime.Today.Year.ToString() },\n                        { \"@Month\", DateTime.Today.Month.ToString() },\n                        { \"@Day\", DateTime.Today.Day.ToString() },\n                        { \"@AdapterID\", GetID_AdapterTable_Internal(adapterName).ToString() }\n                    });\n\n                if (sum.Count == 1 && sum[0].Count == 2)\n                {\n                    if (!Convert.IsDBNull(sum[0][0]) && !Convert.IsDBNull(sum[0][1]))\n                        return (Convert.ToInt64(sum[0][0]), Convert.ToInt64(sum[0][1]));\n                }\n            }\n\n            return (0, 0);\n        }\n\n        public long GetID_AdapterTable(string adapter)\n        {\n            lock (dbLock)\n            {\n                return GetID_AdapterTable_Internal(adapter);\n            }\n        }\n\n        public List<string> GetAllAdapters()\n        {\n            var adapters = new List<string>();\n\n            lock (dbLock)\n            {\n                var rows = DB.GetMultipleCellData(\"SELECT Name FROM Adapter ORDER BY Name\");\n                foreach (var row in rows)\n                {\n                    if (row.Count > 0 && !Convert.IsDBNull(row[0]))\n                        adapters.Add(Convert.ToString(row[0])!);\n                }\n            }\n\n            return adapters;\n        }\n\n        //---------- Private Table Creation ------------//\n        // These are called within lock from CreateTable()\n\n        private int CreateProcessTable()\n        {\n            return DB.RunSQLiteNonQuery(\n                \"CREATE TABLE IF NOT EXISTS Process(\" +\n                \"ID INTEGER PRIMARY KEY NOT NULL, \" +\n                \"Name TEXT NOT NULL UNIQUE)\");\n        }\n\n        private int CreateDateTable()\n        {\n            return DB.RunSQLiteNonQuery(\n                \"CREATE TABLE IF NOT EXISTS Date(\" +\n                \"ID INTEGER PRIMARY KEY NOT NULL, \" +\n                \"Year INTEGER NOT NULL, \" +\n                \"Month INTEGER NOT NULL, \" +\n                \"Day INTEGER NOT NULL, \" +\n                \"UNIQUE (Year, Month, Day) ON CONFLICT IGNORE)\");\n        }\n\n        private int CreateProcessDateTable()\n        {\n            return DB.RunSQLiteNonQuery(\n                \"CREATE TABLE IF NOT EXISTS ProcessDate(\" +\n                \"ID INTEGER PRIMARY KEY NOT NULL, \" +\n                \"ProcessID INTEGER NOT NULL, \" +\n                \"DateID INTEGER NOT NULL, \" +\n                \"AdapterID INTEGER NOT NULL, \" +\n                \"DataReceived INTEGER NOT NULL, \" +\n                \"DataSent INTEGER NOT NULL, \" +\n                \"FOREIGN KEY(ProcessID) REFERENCES Process(ID) ON DELETE CASCADE, \" +\n                \"FOREIGN KEY(DateID) REFERENCES Date(ID) ON DELETE CASCADE, \" +\n                \"FOREIGN KEY(AdapterID) REFERENCES Adapter(ID) ON DELETE CASCADE, \" +\n                \"UNIQUE (ProcessID, DateID, AdapterID) ON CONFLICT IGNORE)\");\n        }\n\n        private int CreateAdapterTable()\n        {\n            return DB.RunSQLiteNonQuery(\n                \"CREATE TABLE IF NOT EXISTS Adapter(\" +\n                \"ID INTEGER PRIMARY KEY NOT NULL, \" +\n                \"Name TEXT NOT NULL UNIQUE)\");\n        }\n\n        //---------- Private Insert/Update Operations ------------//\n        // These are called within lock from PushToDB() and UpdateDatesInDB()\n\n        private int InsertUniqueRow_ProcessTable(string appName)\n        {\n            return DB.RunSQLiteNonQuery(\n                \"INSERT OR IGNORE INTO Process(Name) VALUES(@Name)\",\n                new string[,] { { \"@Name\", appName } });\n        }\n\n        private int InsertUniqueRow_DateTable(DateTime date)\n        {\n            return DB.RunSQLiteNonQuery(\n                \"INSERT OR IGNORE INTO Date(Year, Month, Day) VALUES(@Year, @Month, @Day)\",\n                new string[,]\n                {\n                    { \"@Year\", date.Year.ToString() },\n                    { \"@Month\", date.Month.ToString() },\n                    { \"@Day\", date.Day.ToString() }\n                });\n        }\n\n        private void RemoveOldDate()\n        {\n            var cutoff = DateTime.Now.AddDays(-DataStoragePeriodInDays);\n            Console.WriteLine($\"[{LogTime()}] [SQLite][WPF] RemoveOldDate cutoff='{cutoff:yyyy-MM-dd}'\");\n            DB.RunSQLiteNonQuery(\n                \"DELETE FROM Date WHERE (Year * 10000 + Month * 100 + Day) < \" +\n                $\"({cutoff.Year * 10000 + cutoff.Month * 100 + cutoff.Day})\");\n        }\n\n        private void RemoveOldProcess()\n        {\n            Console.WriteLine($\"[{LogTime()}] [SQLite][WPF] RemoveOldProcess\");\n            DB.RunSQLiteNonQuery(\n                \"DELETE FROM Process WHERE ID NOT IN \" +\n                \"(SELECT DISTINCT ProcessID FROM ProcessDate)\");\n        }\n\n        private int InsertUniqueRow_ProcessDateTable(long processID, long dateID, long adapterID, long dataReceived, long dataSent)\n        {\n            return DB.RunSQLiteNonQuery(\n                \"INSERT OR IGNORE INTO ProcessDate(ProcessID, DateID, AdapterID, DataReceived, DataSent) \" +\n                \"VALUES(@ProcessID, @DateID, @AdapterID, @DataReceived, @DataSent)\",\n                new string[,]\n                {\n                    { \"@ProcessID\", processID.ToString() },\n                    { \"@DateID\", dateID.ToString() },\n                    { \"@AdapterID\", adapterID.ToString() },\n                    { \"@DataReceived\", dataReceived.ToString() },\n                    { \"@DataSent\", dataSent.ToString() }\n                });\n        }\n\n        private int UpdateRow_ProcessDateTable(long processID, long dateID, long adapterID, long dataReceived, long dataSent)\n        {\n            return DB.RunSQLiteNonQuery(\n                \"UPDATE ProcessDate SET \" +\n                \"DataReceived = DataReceived + @DataReceived, \" +\n                \"DataSent = DataSent + @DataSent \" +\n                \"WHERE ProcessID = @ProcessID AND DateID = @DateID AND AdapterID = @AdapterID\",\n                new string[,]\n                {\n                    { \"@DataReceived\", dataReceived.ToString() },\n                    { \"@DataSent\", dataSent.ToString() },\n                    { \"@ProcessID\", processID.ToString() },\n                    { \"@DateID\", dateID.ToString() },\n                    { \"@AdapterID\", adapterID.ToString() }\n                });\n        }\n\n        //---------- Private ID Lookups ------------//\n        // Internal versions without lock (caller must hold lock)\n\n        private long GetID_DateTable(DateTime time)\n        {\n            var result = DB.GetSingleCellData(\n                \"SELECT ID FROM Date WHERE Year = @Year AND Month = @Month AND Day = @Day\",\n                new string[,]\n                {\n                    { \"@Year\", time.Year.ToString() },\n                    { \"@Month\", time.Month.ToString() },\n                    { \"@Day\", time.Day.ToString() }\n                });\n\n            return Convert.ToInt64(result ?? -1);\n        }\n\n        private long GetID_ProcessTable(string appName)\n        {\n            var result = DB.GetSingleCellData(\n                \"SELECT ID FROM Process WHERE Name = @Name\",\n                new string[,] { { \"@Name\", appName } });\n\n            return Convert.ToInt64(result ?? -1);\n        }\n\n        private long GetID_AdapterTable_Internal(string adapter)\n        {\n            var result = DB.GetSingleCellData(\n                \"SELECT ID FROM Adapter WHERE Name = @Name\",\n                new string[,] { { \"@Name\", adapter } });\n\n            return Convert.ToInt64(result ?? -1);\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/MyProcess.cs",
    "content": "﻿using OpenNetMeter.Utilities;\nusing System.ComponentModel;\nusing System.Windows.Media;\n\nnamespace OpenNetMeter.Models\n{\n    public class MyProcess_Small\n    {\n        public string? Name { get; set; }\n\n        public long CurrentDataRecv { get; set; }\n\n        public long CurrentDataSend { get; set; }\n\n        public ImageSource? Icon { get; set; }\n\n        public MyProcess_Small(string nameP, long currentDataRecvP, long currentDataSendP, ImageSource? icon = null)\n        {\n            Name = nameP;\n            CurrentDataRecv = currentDataRecvP;\n            CurrentDataSend = currentDataSendP;\n            Icon = icon;\n        }\n    }\n\n\n    public class MyProcess_Big : INotifyPropertyChanged\n    {\n        private string? name;\n        public string? Name\n        {\n            get { return name; }\n            set { name = value; OnPropertyChanged(\"Name\"); }\n        }\n\n        private long currentdataRecv;\n        public long CurrentDataRecv\n        {\n            get { return currentdataRecv; }\n            set \n            { \n                if(currentdataRecv != value)\n                {\n                    currentdataRecv = value;\n                    OnPropertyChanged(\"CurrentDataRecv\");\n                }\n            }\n        }\n\n        private long currentdataSend;\n        public long CurrentDataSend\n        {\n            get { return currentdataSend; }\n            set \n            {\n                if(currentdataSend != value)\n                {\n                    currentdataSend = value;\n                    OnPropertyChanged(\"CurrentDataSend\");\n                }\n            }\n        }\n\n        private long totaldataRecv;\n        public long TotalDataRecv\n        {\n            get { return totaldataRecv; }\n            set \n            { \n                if(totaldataRecv != value)\n                {\n                    totaldataRecv = value;\n                    OnPropertyChanged(\"TotalDataRecv\");\n                }\n            }\n        }\n\n        private long totaldataSend;\n        public long TotalDataSend\n        {\n            get { return totaldataSend; }\n            set \n            { \n                if(totaldataSend != value)\n                {\n                    totaldataSend = value;\n                    OnPropertyChanged(\"TotalDataSend\");\n                }\n            }\n        }\n\n        private ImageSource? icon;\n        public ImageSource? Icon\n        {\n            get { return icon; }\n            set\n            {\n                if (icon != value)\n                {\n                    icon = value;\n                    OnPropertyChanged(nameof(Icon));\n                }\n            }\n        }\n        \n        public MyProcess_Big(string nameP, long currentDataRecvP, long currentDataSendP, long totalDataRecvP, long totalDataSendP, ImageSource? iconP = null)\n        {\n            Name = nameP;\n            CurrentDataRecv = currentDataRecvP;\n            CurrentDataSend = currentDataSendP;\n            TotalDataRecv = totalDataRecvP;\n            TotalDataSend = totalDataSendP;\n            Icon = iconP;\n        }\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n        private void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/NativeMethods.cs",
    "content": "﻿using System;\nusing System.Drawing;\nusing System.Runtime.InteropServices;\n\nnamespace OpenNetMeter.Models\n{\n    public static class NativeMethods\n    {\n        \n        internal static IntPtr GetWindowByClassName(IntPtr parentHandle, string className) => FindWindowEx(parentHandle, IntPtr.Zero, className, string.Empty);\n\n        [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        internal static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);\n\n        public enum GW : uint\n        {\n            HWNDFIRST = 0,\n            HWNDLAST = 1,\n            HWNDNEXT = 2,\n            HWNDPREV = 3,\n            OWNER = 4,\n            CHILD = 5,\n            ENABLEDPOPUP = 6\n        }\n\n        [DllImport(\"user32.dll\")]\n        internal static extern IntPtr GetWindow(IntPtr hwnd, uint cmd);\n\n        public enum SWP\n        {\n            ASYNCWINDOWPOS = 0x4000,\n            NOACTIVATE = 0x0010,\n            NOMOVE = 0x0002,\n            NOSIZE = 0x0001,\n        }\n\n        [DllImport(\"user32.dll\", EntryPoint = \"SetWindowPos\")]\n        internal static extern IntPtr SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/NetworkProcess.TestHooks.cs",
    "content": "using System.Net;\n\nnamespace OpenNetMeter.Models\n{\n    // Test-only helpers kept internal to avoid exposing implementation details publicly.\n    public partial class NetworkProcess\n    {\n        internal void TestSetLocalIPs(byte[] ipv4, byte[] ipv6)\n        {\n            localIPv4 = ipv4;\n            localIPv6 = ipv6;\n        }\n\n        internal void TestInvokeRecvProcess(IPAddress src, IPAddress dest, int size, string name)\n        {\n            ProcessPacket(src, dest, size, name, isRecv: true);\n        }\n\n        internal void TestInvokeSendProcess(IPAddress src, IPAddress dest, int size, string name)\n        {\n            ProcessPacket(src, dest, size, name, isRecv: false);\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/NetworkProcess.cs",
    "content": "﻿using System;\nusing System.Net;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Net.NetworkInformation;\nusing System.Threading.Tasks;\nusing Microsoft.Diagnostics.Tracing.Parsers;\nusing Microsoft.Diagnostics.Tracing.Parsers.Kernel;\nusing Microsoft.Diagnostics.Tracing.Session;\nusing System.Threading;\nusing System.Net.Sockets;\nusing System.Collections.Generic;\nusing OpenNetMeter.Utilities;\nusing OpenNetMeter.Properties;\nusing System.Diagnostics.Eventing.Reader;\nusing System.Text.RegularExpressions;\n\nnamespace OpenNetMeter.Models\n{\n    public partial class NetworkProcess : IDisposable\n    {\n        //---------- Constants ------------//\n\n        private const int OneSec = 1000;\n        private const int DebounceDelayMs = 300; // Delay before processing network change events\n\n        // Default IP values indicating \"not connected\"\n        private static readonly byte[] DefaultIPv4 = { 0, 0, 0, 0 };\n        private static readonly byte[] DefaultIPv6 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };\n\n        //---------- Network State ------------//\n\n        /// <summary>\n        /// Immutable snapshot of current network connection state.\n        /// Used to detect connection changes without race conditions.\n        /// </summary>\n        private sealed record NetworkSnapshot(\n            string AdapterName,  // Display name, includes SSID for Wi-Fi\n            string AdapterId,    // GUID for the adapter\n            byte[] IPv4,         // Assigned IPv4 address bytes\n            byte[] IPv6          // Assigned IPv6 address bytes\n        );\n\n        // Lock for thread-safe access to local IP addresses\n        private readonly object stateLock = new object();\n\n        // Currently assigned local IP addresses (used for packet filtering)\n        private byte[] localIPv4 = DefaultIPv4;\n        private byte[] localIPv6 = DefaultIPv6;\n\n        //---------- Debounce & Synchronization ------------//\n\n        // Timer-based debounce: each network change event resets the timer.\n        // When it finally fires (after DebounceDelayMs of silence), HandleNetworkChange runs.\n        private Timer? debounceTimer;\n        private volatile bool isDisposed;\n\n        // Lock to ensure only one HandleNetworkChange runs at a time\n        private readonly object networkChangeLock = new object();\n\n        //---------- Periodic Tasks ------------//\n\n        // Periodic task for updating network speed display\n        private PeriodicWork? networkSpeedWork;\n\n        // Periodic task for pushing process data to database\n        private PeriodicWork? dbPushWork;\n\n        //---------- ETW Session ------------//\n\n        // ETW kernel session for capturing network packets\n        private TraceEventSession? kernelSession;\n\n        private const string KernelSessionName = \"OpenNetMeter-Kernel\";\n\n        // Task running the ETW packet capture loop\n        public Task? PacketTask;\n\n        //---------- Public State ------------//\n\n        // Current network adapter name (includes SSID for Wi-Fi connections)\n        public string AdapterName { get; private set; } = \"\";\n\n        // Current adapter's unique ID (GUID) - used for comparison to detect changes\n        private string currentAdapterId = \"\";\n        public string CurrentAdapterId => currentAdapterId;\n\n        /// <summary>\n        /// Primary buffer for storing process network data.\n        /// Alternates with MyProcessesBuffer to allow lock-free reading.\n        /// </summary>\n        public Dictionary<string, MyProcess_Small?> MyProcesses { get; } = new();\n\n        /// <summary>\n        /// Secondary buffer for storing process network data.\n        /// While GUI reads from MyProcesses, new data goes here (and vice versa).\n        /// This double-buffering prevents lock contention with the UI thread.\n        /// </summary>\n        public Dictionary<string, MyProcess_Small?> MyProcessesBuffer { get; } = new();\n\n        /// <summary>\n        /// Buffer for data pending database write.\n        /// Cleared after each successful DB push.\n        /// </summary>\n        public Dictionary<string, MyProcess_Small?> PushToDBBuffer { get; } = new();\n\n        /// <summary>\n        /// Controls which buffer receives incoming packet data.\n        /// true = write to MyProcessesBuffer, read from MyProcesses\n        /// false = write to MyProcesses, read from MyProcessesBuffer\n        /// Toggled by the UI layer when extracting data for display.\n        /// </summary>\n        public bool IsBufferTime { get; set; }\n\n        // Running totals for current session (reset on adapter change)\n        public long CurrentSessionDownloadData;\n        public long CurrentSessionUploadData;\n        public long UploadSpeed;\n\n        //---------- Properties with Change Notification ------------//\n\n        private long downloadSpeed;\n        public long DownloadSpeed\n        {\n            get => downloadSpeed;\n            set { downloadSpeed = value; OnPropertyChanged(nameof(DownloadSpeed)); }\n        }\n\n        private string isNetworkOnline = \"Disconnected\";\n        /// <summary>\n        /// Current connection status. Either \"Disconnected\" or the adapter name.\n        /// </summary>\n        public string IsNetworkOnline\n        {\n            get => isNetworkOnline;\n            private set { isNetworkOnline = value; OnPropertyChanged(nameof(IsNetworkOnline)); }\n        }\n\n        //---------- Initialization ------------//\n\n        /// <summary>\n        /// Call after subscribing to property handlers in MainWindowVM.\n        /// Sets up network change monitoring and performs initial connection check.\n        /// </summary>\n        public void Initialize()\n        {\n            IsNetworkOnline = \"Disconnected\";\n\n            // Create the debounce timer (initially not running)\n            debounceTimer = new Timer(_ => HandleNetworkChange(), null, Timeout.Infinite, Timeout.Infinite);\n\n            // Subscribe to network address changes (fires on connect/disconnect/IP change)\n            NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;\n\n            // Check current connection state on startup\n            HandleNetworkChange();\n        }\n\n        //---------- Network Detection (Snapshot-Based) ------------//\n\n        /// <summary>\n        /// Queries the system for the current active network connection.\n        /// Returns an immutable snapshot of the connection, or null if disconnected.\n        /// \n        /// This replaces the old socket-based GetLocalIP() approach which was unreliable\n        /// during network transitions (socket connect to 8.8.8.8 would fail transiently).\n        /// </summary>\n        private NetworkSnapshot? GetCurrentNetworkSnapshot()\n        {\n            var adapters = NetworkInterface.GetAllNetworkInterfaces();\n\n            foreach (var adapter in adapters)\n            {\n                // Skip adapters that aren't connected\n                if (adapter.OperationalStatus != OperationalStatus.Up)\n                    continue;\n\n                // Skip loopback (127.0.0.1)\n                if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback)\n                    continue;\n\n                var props = adapter.GetIPProperties();\n\n                // No gateway = not a real internet connection (e.g., virtual adapters)\n                if (props.GatewayAddresses.Count == 0)\n                    continue;\n\n                byte[]? ipv4 = null;\n                byte[]? ipv6 = null;\n\n                // Extract assigned IP addresses\n                foreach (var unicast in props.UnicastAddresses)\n                {\n                    var addr = unicast.Address;\n\n                    if (addr.AddressFamily == AddressFamily.InterNetwork)\n                    {\n                        ipv4 = addr.GetAddressBytes();\n                    }\n                    else if (addr.AddressFamily == AddressFamily.InterNetworkV6\n                             && !addr.IsIPv6LinkLocal) // Skip link-local (fe80::) addresses\n                    {\n                        ipv6 = addr.GetAddressBytes();\n                    }\n                }\n\n                // Need at least one usable IP to consider this a valid connection\n                if (ipv4 == null && ipv6 == null)\n                    continue;\n\n                // Build display name (include SSID for Wi-Fi)\n                var name = adapter.Name;\n                if (adapter.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)\n                {\n                    var ssid = GetConnectedSsid(adapter.Id);\n                    if (!string.IsNullOrEmpty(ssid))\n                        name += $\"({ssid})\";\n                }\n\n                Debug.WriteLine($\"{adapter.Name} is up, IP: {(ipv4 != null ? new IPAddress(ipv4) : new IPAddress(ipv6!))}\");\n\n                return new NetworkSnapshot(\n                    name,\n                    adapter.Id,\n                    ipv4 ?? DefaultIPv4,\n                    ipv6 ?? DefaultIPv6\n                );\n            }\n\n            return null; // No valid connection found\n        }\n\n        /// <summary>\n        /// Gets the SSID of the currently connected Wi-Fi network by reading\n        /// from the Windows WLAN AutoConfig event log.\n        /// \n        /// This is more reliable than the WinRT API on Windows 11 which requires\n        /// location permissions to retrieve SSID.\n        /// </summary>\n        private static string? GetConnectedSsid(string adapterGuid)\n        {\n            try\n            {\n                // Query for EventID 8001 (successful connection) in WLAN-AutoConfig log\n                var query = new EventLogQuery(\n                    \"Microsoft-Windows-WLAN-AutoConfig/Operational\",\n                    PathType.LogName,\n                    \"*[System[EventID=8001]]\"\n                )\n                {\n                    ReverseDirection = true // Get most recent first\n                };\n\n                using var reader = new EventLogReader(query);\n                if (reader.ReadEvent() is EventRecord evt)\n                {\n                    var message = evt.FormatDescription();\n                    var match = Regex.Match(message, @\"^SSID:\\s*(.+)$\", RegexOptions.Multiline);\n                    if (match.Success)\n                        return match.Groups[1].Value.Trim();\n                }\n            }\n            catch\n            {\n                // Silently fail - SSID is nice-to-have, not critical\n            }\n\n            return null;\n        }\n\n        //---------- Network Change Handling ------------//\n\n        /// <summary>\n        /// Event handler for NetworkChange.NetworkAddressChanged.\n        /// Resets the debounce timer so that HandleNetworkChange only runs\n        /// after DebounceDelayMs of silence (no rapid-fire events).\n        /// </summary>\n        private void OnNetworkAddressChanged(object? sender, EventArgs? e)\n        {\n            if (isDisposed)\n                return;\n\n            // Reset the timer countdown. If another event arrives before it fires,\n            // the timer resets again. No allocations, no CTS, no race conditions.\n            try\n            {\n                debounceTimer?.Change(DebounceDelayMs, Timeout.Infinite);\n            }\n            catch (ObjectDisposedException)\n            {\n                // Timer was disposed between the isDisposed check and the Change call — harmless.\n            }\n        }\n\n        /// <summary>\n        /// Core network state machine. Compares current network state to tracked state\n        /// and triggers appropriate actions (connect/disconnect/switch/refresh).\n        /// \n        /// State transitions:\n        /// - Disconnected -> Connected: Start monitoring\n        /// - Connected -> Disconnected: Stop monitoring  \n        /// - Connected -> Different adapter: Stop then start monitoring\n        /// - Connected -> Same adapter:\n        ///   - IP/name changed: Refresh tracked snapshot (no restart)\n        ///   - No meaningful change: Ignore transient event\n        /// \n        /// Protected by networkChangeLock to prevent concurrent execution.\n        /// </summary>\n        private void HandleNetworkChange()\n        {\n            if (isDisposed)\n                return;\n\n            lock (networkChangeLock)\n            {\n                if (isDisposed)\n                    return;\n\n                var snapshot = GetCurrentNetworkSnapshot();\n\n                if (snapshot == null)\n                {\n                    // No valid connection found\n                    if (IsNetworkOnline != \"Disconnected\")\n                    {\n                        Debug.WriteLine(\"Ash: Connection lost\");\n                        EndNetworkProcess();\n                    }\n                    return;\n                }\n\n                // Valid connection found - determine if it's new or changed\n                if (IsNetworkOnline == \"Disconnected\")\n                {\n                    // Was disconnected, now connected\n                    Debug.WriteLine(\"Ash: Connection established\");\n                    ApplySnapshot(snapshot);\n                    StartNetworkProcess();\n                }\n                else if (currentAdapterId != snapshot.AdapterId)\n                {\n                    // Was connected to different adapter (e.g., switched Wi-Fi networks)\n                    // Compare by ID, not name, because SSID retrieval can race with event log\n                    Debug.WriteLine(\"Ash: Network adapter changed\");\n                    EndNetworkProcess();\n                    ApplySnapshot(snapshot);\n                    StartNetworkProcess();\n                }\n                else\n                {\n                    // Same adapter can still receive DHCP/IPv6 address changes or an SSID/name update.\n                    // Refresh tracked snapshot so packet filtering keeps matching local traffic.\n                    bool ipChanged = HasSnapshotAddressChanged(snapshot);\n                    bool nameChanged = !string.Equals(AdapterName, snapshot.AdapterName, StringComparison.Ordinal);\n                    if (ipChanged || nameChanged)\n                    {\n                        Debug.WriteLine(\"Ash: Adapter IP/name changed on same adapter - refreshing snapshot\");\n                        ApplySnapshot(snapshot);\n\n                        // Keep status text in sync when SSID/name changes without adapter GUID change.\n                        if (nameChanged)\n                            IsNetworkOnline = AdapterName;\n                    }\n                }\n            }\n        }\n\n        private bool HasSnapshotAddressChanged(NetworkSnapshot snapshot)\n        {\n            lock (stateLock)\n            {\n                return !ByteArray.Compare(localIPv4, snapshot.IPv4) ||\n                       !ByteArray.Compare(localIPv6, snapshot.IPv6);\n            }\n        }\n\n        /// <summary>\n        /// Updates tracked state from a network snapshot.\n        /// Called when establishing a new connection, switching adapters,\n        /// or refreshing state on same-adapter IP/name changes.\n        /// </summary>\n        private void ApplySnapshot(NetworkSnapshot snapshot)\n        {\n            lock (stateLock)\n            {\n                localIPv4 = snapshot.IPv4;\n                localIPv6 = snapshot.IPv6;\n            }\n            currentAdapterId = snapshot.AdapterId;\n            AdapterName = snapshot.AdapterName;\n        }\n\n        //---------- Start/Stop Network Monitoring ------------//\n\n        /// <summary>\n        /// Starts all network monitoring components:\n        /// - Database table setup\n        /// - ETW packet capture\n        /// - Speed monitoring periodic task\n        /// - Database push periodic task\n        /// </summary>\n        public void StartNetworkProcess()\n        {\n            // Ensure database table exists for this adapter\n            using (var db = new ApplicationDB(AdapterName))\n            {\n                if (db.CreateTable() < 0)\n                {\n                    EventLogger.Error(\"Error: Create table\");\n                }\n                else\n                {\n                    db.InsertUniqueRow_AdapterTable(AdapterName);\n                    db.UpdateDatesInDB();\n                }\n            }\n\n            TraceEventSession.GetActiveSession(KernelSessionName)?.Stop();\n            // Start packet capture and periodic tasks\n            CaptureNetworkPackets();\n            StartSpeedMonitoring();\n            StartDbPush();\n\n            // Update connection status (triggers UI update via PropertyChanged)\n            IsNetworkOnline = AdapterName;\n        }\n\n        /// <summary>\n        /// Stops all network monitoring components and resets state.\n        /// Called on disconnect or before switching to a different adapter.\n        /// </summary>\n        public void EndNetworkProcess()\n        {\n            // Stop ETW session first (generates most activity)\n            StopKernelSession();\n\n            // Stop periodic tasks\n            StopPeriodicWork(ref networkSpeedWork, \"Network speed\");\n            StopPeriodicWork(ref dbPushWork, \"DB push\");\n\n            // Clear process data buffers\n            lock (MyProcesses) MyProcesses.Clear();\n            lock (MyProcessesBuffer) MyProcessesBuffer.Clear();\n\n            // Reset speed counters\n            CurrentSessionDownloadData = 0;\n            CurrentSessionUploadData = 0;\n            UploadSpeed = 0;\n            DownloadSpeed = 0;\n\n            // Reset adapter tracking\n            currentAdapterId = \"\";\n            IsNetworkOnline = \"Disconnected\";\n        }\n\n        /// <summary>\n        /// Stops the ETW kernel session and waits for the capture task to complete.\n        /// </summary>\n        private void StopKernelSession()\n        {\n            // Disposing the session causes Source.Process() to return\n            kernelSession?.Dispose();\n            kernelSession = null;\n\n            var task = PacketTask;\n            if (task != null)\n            {\n                try\n                {\n                    task.Wait(TimeSpan.FromMilliseconds(OneSec));\n                }\n                catch (AggregateException ex)\n                {\n                    EventLogger.Error(\"Packet capture stop error\", ex);\n                }\n                finally\n                {\n                    if (task.IsCompleted)\n                        task.Dispose();\n                    else\n                        task.ContinueWith(t => t.Dispose(), TaskScheduler.Default);\n\n                    PacketTask = null;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Safely stops a periodic work task with error handling.\n        /// </summary>\n        private void StopPeriodicWork(ref PeriodicWork? work, string name)\n        {\n            try\n            {\n                work?.Stop();\n            }\n            catch (Exception ex)\n            {\n                EventLogger.Error($\"{name} stop error\", ex);\n            }\n            finally\n            {\n                work = null;\n            }\n        }\n\n        //---------- Periodic Tasks ------------//\n\n        /// <summary>\n        /// Starts the periodic task that calculates current network speed.\n        /// Runs every second and computes speed as delta from previous totals.\n        /// </summary>\n        private void StartSpeedMonitoring()\n        {\n            long tempDownload = 0;\n            long tempUpload = 0;\n\n            networkSpeedWork = new PeriodicWork(\"Network speed\", TimeSpan.FromSeconds(1));\n            networkSpeedWork.Start(_ =>\n            {\n                // Read current totals (atomic reads)\n                long currentDown = Interlocked.Read(ref CurrentSessionDownloadData);\n                long currentUp = Interlocked.Read(ref CurrentSessionUploadData);\n\n                // Calculate speed based on user's preferred format\n                if (SettingsManager.Current.NetworkSpeedFormat == 0)\n                {\n                    // Bits per second\n                    DownloadSpeed = (currentDown - tempDownload) * 8;\n                    UploadSpeed = (currentUp - tempUpload) * 8;\n                }\n                else\n                {\n                    // Bytes per second\n                    DownloadSpeed = currentDown - tempDownload;\n                    UploadSpeed = currentUp - tempUpload;\n                }\n\n                // Store for next iteration's delta calculation\n                tempDownload = currentDown;\n                tempUpload = currentUp;\n\n                return Task.CompletedTask;\n            });\n        }\n\n        /// <summary>\n        /// Starts the periodic task that pushes accumulated process data to the database.\n        /// Runs every 5 seconds to batch writes and reduce I/O.\n        /// </summary>\n        private void StartDbPush()\n        {\n            dbPushWork = new PeriodicWork(\"DB push\", TimeSpan.FromSeconds(5));\n            dbPushWork.Start(_ =>\n            {\n#if DEBUG\n                var sw = Stopwatch.StartNew();\n#endif\n                lock (PushToDBBuffer)\n                {\n                    if (PushToDBBuffer.Count > 0)\n                    {\n                        using var db = new ApplicationDB(AdapterName);\n                        foreach (var (key, proc) in PushToDBBuffer)\n                        {\n                            if (proc != null)\n                                db.PushToDB(key, proc.CurrentDataRecv, proc.CurrentDataSend);\n                        }\n                        PushToDBBuffer.Clear();\n                    }\n                }\n#if DEBUG\n                sw.Stop();\n                Debug.WriteLine($\"elapsed time (DBpush): {sw.ElapsedMilliseconds} | time {DateTime.Now:O}\");\n#endif\n                return Task.CompletedTask;\n            });\n        }\n\n        //---------- ETW Packet Capture ------------//\n\n        /// <summary>\n        /// Starts the ETW (Event Tracing for Windows) kernel session to capture\n        /// all TCP/IP network packets on the system.\n        /// \n        /// Uses the NT Kernel Logger session which requires admin privileges.\n        /// Packets are filtered to only count those matching our local IP.\n        /// </summary>\n        private void CaptureNetworkPackets()\n        {\n            PacketTask = Task.Run(() =>\n            {\n                if (kernelSession != null) return;\n\n                try\n                {\n                    // Create kernel trace session (requires admin)\n                    kernelSession = new TraceEventSession(KernelSessionName);\n                    kernelSession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP);\n\n                    // Subscribe to all TCP/UDP send/receive events\n                    // All events funnel through ProcessPacket for unified handling\n                    var kernel = kernelSession.Source.Kernel;\n\n                    // Receive events (download)\n                    kernel.TcpIpRecv += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);\n                    kernel.TcpIpRecvIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);\n                    kernel.UdpIpRecv += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);\n                    kernel.UdpIpRecvIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: true);\n\n                    // Send events (upload)\n                    kernel.TcpIpSend += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);\n                    kernel.TcpIpSendIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);\n                    kernel.UdpIpSend += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);\n                    kernel.UdpIpSendIPV6 += data => ProcessPacket(data.saddr, data.daddr, data.size, data.ProcessName, isRecv: false);\n\n                    // Blocks until session is disposed\n                    kernelSession.Source.Process();\n                }\n                catch (Exception ex)\n                {\n                    EventLogger.Error(\"Packet capture loop error\", ex);\n                }\n            });\n        }\n\n        /// <summary>\n        /// Processes a single network packet captured by ETW.\n        /// Filters packets to only count those involving our local IP,\n        /// then records to the appropriate buffer based on direction.\n        /// </summary>\n        /// <param name=\"src\">Source IP address</param>\n        /// <param name=\"dest\">Destination IP address</param>\n        /// <param name=\"size\">Packet size in bytes</param>\n        /// <param name=\"processName\">Name of the process that sent/received the packet</param>\n        /// <param name=\"isRecv\">True for download, false for upload</param>\n        private void ProcessPacket(IPAddress src, IPAddress dest, int size, string processName, bool isRecv)\n        {\n            // Get current local IP for this address family\n            byte[] localIp;\n            lock (stateLock)\n            {\n                localIp = src.AddressFamily == AddressFamily.InterNetwork ? localIPv4 : localIPv6;\n            }\n\n            // Cache address bytes to avoid repeated allocations\n            var srcBytes = src.GetAddressBytes();\n            var destBytes = dest.GetAddressBytes();\n\n            // Check if packet involves our local IP\n            bool isSrc = ByteArray.Compare(srcBytes, localIp);\n            bool isDest = ByteArray.Compare(destBytes, localIp);\n\n            // XOR: exactly one should match (either we sent it or received it)\n            // If both match (loopback) or neither match (other adapter), skip\n            if (!(isSrc ^ isDest))\n                return;\n\n            // Apply network type filter (private/public/both)\n            if (!ShouldProcessByNetworkType(isSrc, src, dest))\n                return;\n\n            // Record the packet to appropriate counter and buffer\n            if (isRecv)\n                RecordRecv(processName, size);\n            else\n                RecordSend(processName, size);\n        }\n\n        /// <summary>\n        /// Checks if a packet should be counted based on the user's network type filter setting.\n        /// </summary>\n        /// <param name=\"isLocalSrc\">True if local IP is the source (upload), false if destination (download)</param>\n        /// <param name=\"src\">Source IP</param>\n        /// <param name=\"dest\">Destination IP</param>\n        /// <returns>True if packet should be counted</returns>\n        private bool ShouldProcessByNetworkType(bool isLocalSrc, IPAddress src, IPAddress dest)\n        {\n            // Remote IP is the one that isn't ours\n            var remoteIp = isLocalSrc ? dest : src;\n\n            return SettingsManager.Current.NetworkType switch\n            {\n                0 => IsPrivateIP(remoteIp),   // Private only (LAN traffic)\n                1 => !IsPrivateIP(remoteIp),  // Public only (internet traffic)\n                2 => true,                     // Both\n                _ => false\n            };\n        }\n\n        /// <summary>\n        /// Records a received (download) packet.\n        /// </summary>\n        private void RecordRecv(string name, int size)\n        {\n            // Thread-safe increment of running total\n            Interlocked.Add(ref CurrentSessionDownloadData, size);\n            RecordToBuffer(name, size, isRecv: true);\n        }\n\n        /// <summary>\n        /// Records a sent (upload) packet.\n        /// </summary>\n        private void RecordSend(string name, int size)\n        {\n            // Thread-safe increment of running total\n            Interlocked.Add(ref CurrentSessionUploadData, size);\n            RecordToBuffer(name, size, isRecv: false);\n        }\n\n        /// <summary>\n        /// Records packet data to the active process buffer.\n        /// Uses double-buffering to minimize lock contention with UI reads.\n        /// </summary>\n        private void RecordToBuffer(string name, int size, bool isRecv)\n        {\n            // Empty process name means kernel/system traffic\n            if (string.IsNullOrEmpty(name))\n                name = \"System\";\n\n            // Select buffer based on current phase\n            var dict = IsBufferTime ? MyProcessesBuffer : MyProcesses;\n\n            lock (dict)\n            {\n                // Get or create process entry (single lookup)\n                if (!dict.TryGetValue(name, out var proc))\n                {\n                    proc = new MyProcess_Small(name, 0, 0);\n                    dict[name] = proc;\n                }\n\n                // Accumulate data\n                if (isRecv)\n                    proc!.CurrentDataRecv += size;\n                else\n                    proc!.CurrentDataSend += size;\n            }\n        }\n\n        //---------- IP Classification ------------//\n\n        /// <summary>\n        /// Determines if an IP address is in a private (non-routable) range.\n        /// Used for filtering traffic by network type (LAN vs internet).\n        /// \n        /// Private ranges:\n        /// - IPv4: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16\n        /// - IPv6: Link-local (fe80::), site-local, unique-local (fc00::/7)\n        /// </summary>\n        private bool IsPrivateIP(IPAddress ip)\n        {\n            // Handle IPv4-mapped IPv6 addresses (::ffff:x.x.x.x)\n            if (ip.IsIPv4MappedToIPv6)\n                ip = ip.MapToIPv4();\n\n            // Loopback is always private\n            if (IPAddress.IsLoopback(ip))\n                return true;\n\n            if (ip.AddressFamily == AddressFamily.InterNetwork)\n            {\n                var bytes = ip.GetAddressBytes();\n                return bytes[0] == 10 ||                                        // 10.0.0.0/8 (Class A)\n                       (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) || // 172.16.0.0/12 (Class B)\n                       (bytes[0] == 192 && bytes[1] == 168) ||                  // 192.168.0.0/16 (Class C)\n                       (bytes[0] == 169 && bytes[1] == 254);                    // 169.254.0.0/16 (link-local/APIPA)\n            }\n\n            if (ip.AddressFamily == AddressFamily.InterNetworkV6)\n            {\n                return ip.IsIPv6LinkLocal ||     // fe80::/10\n#if NET6_0_OR_GREATER\n                       ip.IsIPv6UniqueLocal ||   // fc00::/7 (only available in .NET 6+)\n#endif\n                       ip.IsIPv6SiteLocal;       // fec0::/10 (deprecated but still checked)\n            }\n\n            return false;\n        }\n\n        //---------- Property Changed ------------//\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n\n        private void OnPropertyChanged(string propName) =>\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n\n        //---------- Dispose ------------//\n\n        /// <summary>\n        /// Cleans up all resources: unsubscribes from events, stops monitoring,\n        /// and flushes any pending data to the database.\n        /// </summary>\n        public void Dispose()\n        {\n            isDisposed = true;\n\n            // Unsubscribe from network change events\n            NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged;\n\n            // Dispose the debounce timer (prevents any pending callback from firing)\n            debounceTimer?.Dispose();\n            debounceTimer = null;\n\n            // Stop monitoring if active\n            if (IsNetworkOnline != \"Disconnected\")\n                EndNetworkProcess();\n\n            // Flush any remaining buffered data to database\n            lock (PushToDBBuffer)\n            {\n                if (PushToDBBuffer.Count > 0)\n                {\n                    using var db = new ApplicationDB(AdapterName);\n                    foreach (var (key, proc) in PushToDBBuffer)\n                    {\n                        if (proc != null)\n                            db.PushToDB(key, proc.CurrentDataRecv, proc.CurrentDataSend);\n                    }\n                    PushToDBBuffer.Clear();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Models/SpeedGraph.cs",
    "content": "﻿using OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace OpenNetMeter.Models\n{\n    public class MyLine : INotifyPropertyChanged\n    {\n        private Point from;\n        public Point From\n        {\n            get { return from; }\n            set\n            {\n                from = value;\n                OnPropertyChanged(\"From\");\n            }\n        }\n\n        private Point to;\n        public Point To\n        {\n            get { return to; }\n            set\n            {\n                to = value;\n                OnPropertyChanged(\"To\");\n            }\n        }\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n        private void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n    }\n    public class SpeedGraph\n    {\n        // ---------- Graph geometries ------------//\n        public List<MyLine> XLines { get; set; }\n        public List<MyLine> YLines { get; set; }\n        public List<MyLine> Borders { get; set; }\n        public ObservableCollection<MyLine> DownloadLines { get; private set; }\n        public ObservableCollection<MyLine> UploadLines { get; private set; }\n        public List<MyLine> DownloadPoints { get; private set; }\n        public List<MyLine> UploadPoints { get; private set; }\n\n        // ----------- Graph Labels ----------------//\n        public List<TextBlock> Xlabels { get; private set; }\n        public double Xstart { get; set; }\n        public List<TextBlock> Ylabels { get; private set; }\n        private Size maxYtextSize;\n\n        // ----------- Other Graph info ----------- //\n        public double GraphWidth { get; set; }\n        public double GraphHeight { get; set; }\n\n        public int GridXCount;\n        public int GridYCount;\n\n        public int XaxisResolution { get; set; } \n        public bool resumeDraw { get; set; }\n        public bool firstDrawAfterResume { get; set; }\n        public SpeedGraph(int XlineCount, int YlineCount)\n        {\n            GridXCount = XlineCount;\n            GridYCount = YlineCount;\n            DownloadPoints = new List<MyLine>();\n            UploadPoints = new List<MyLine>();\n            DownloadLines = new ObservableCollection<MyLine>();\n            UploadLines = new ObservableCollection<MyLine>();\n            Xlabels = new List<TextBlock>();\n            Ylabels = new List<TextBlock>();\n            XLines = new List<MyLine>();\n            YLines = new List<MyLine>();\n            Borders = new List<MyLine>();\n\n            firstDrawAfterResume = false;\n        }\n\n        public void Init()\n        {\n            XaxisResolution = (GridYCount - 1) * 10;\n            bool useBytes = SettingsManager.Current.NetworkSpeedFormat != 0;\n            string sampleLabel = DataSizeSuffix.InStr(512 * 1024 * 1024, 1, useBytes, SpeedMagnitude.Auto);\n            maxYtextSize =  UIMeasure.Shape(new TextBlock { Text = sampleLabel, FontSize = 11, Padding = new Thickness(0) });\n            maxYtextSize.Width += 2.0;\n            Xstart = maxYtextSize.Width;\n\n            // populate the lists\n            for (int i = 0; i < XaxisResolution; i++)\n            {\n                DownloadLines.Add(new MyLine { From = new Point(0, 0), To = new Point(0, 0) });\n                UploadLines.Add(new MyLine { From = new Point(0, 0), To = new Point(0, 0) });\n                DownloadPoints.Add(new MyLine { From = new Point(0, 0), To = new Point(0, 0) });\n                UploadPoints.Add(new MyLine { From = new Point(0, 0), To = new Point(0, 0) });\n            }\n\n            // Add X labels and Y lines\n            for (int i = 0; i < GridYCount; i++)\n            {\n                Xlabels.Add(\n                new TextBlock\n                {\n                    Text = i < (GridYCount - 1) ? (i * 10).ToString() : \"seconds\",\n                    FontSize = 11,\n                    Padding = new Thickness(0)\n                });\n\n                if (i > 0 && i < GridYCount - 1)\n                {\n                    YLines.Add(new MyLine());\n                }\n            }\n\n            // Add Y labels and X lines\n            long temp = 1;\n            for (int i = 0; i < GridXCount; i++)\n            {\n\n                if (i == 0 || i == GridXCount - 1)\n                {\n                    Ylabels.Add(new TextBlock\n                    {\n                        Text = \"\",\n                        FontSize = 11,\n                        Padding = new Thickness(0, 0, 0, 0)\n                    });\n                }\n                else\n                {\n                    if (i % 2 == 0)\n                        temp *= 2;\n                    else\n                        temp *= 512;\n\n                    Ylabels.Add(new TextBlock\n                    {\n                        Text = DataSizeSuffix.InStr(temp, 1, useBytes, SpeedMagnitude.Auto),\n                        FontSize = 11,\n                        Padding = new Thickness(0, 0, 0, 0)\n                    });\n\n                    XLines.Add(new MyLine());\n                }\n            }\n\n            // Add borders\n            for (int i = 0; i < 4; i++)\n            {\n                Borders.Add(new MyLine());\n            }\n        }\n\n        private int drawPointCount;\n        public void DrawPoints(long downloadSpeed, long uploadSpeed)\n        {\n            // reset the graph after it completes a full run\n            if (drawPointCount >= XaxisResolution)\n            {\n                //shift xaxis label\n                App.Current.Dispatcher.Invoke(() =>\n                {\n                    for (int i = 0; i < Xlabels.Count / 2; i++)\n                    {\n                        string temp = Xlabels[i].Text;\n                        Xlabels[i].Text = Xlabels[i + Xlabels.Count / 2].Text;\n                        Xlabels[i + Xlabels.Count / 2].Text = temp;\n                    }\n                });\n\n                drawPointCount = XaxisResolution / 2;\n                for (int i = 0; i < DownloadPoints.Count; i++)\n                {\n                    if (i < XaxisResolution / 2)\n                    {\n                        DownloadPoints[i].From = new Point(DownloadPoints[XaxisResolution / 2 + i].From.X - XaxisResolution / 2, DownloadPoints[XaxisResolution / 2 + i].From.Y);\n                        DownloadPoints[i].To = new Point(DownloadPoints[XaxisResolution / 2 + i].To.X - XaxisResolution / 2, DownloadPoints[XaxisResolution / 2 + i].To.Y);\n                    }\n                    else\n                    {\n                        DownloadPoints[i].From = new Point(0, 0);\n                        DownloadPoints[i].To = new Point(0, 0);\n                    }\n\n                }\n\n                for (int i = 0; i < UploadPoints.Count; i++)\n                {\n                    if (i < XaxisResolution / 2)\n                    {\n                        UploadPoints[i].From = new Point(UploadPoints[XaxisResolution / 2 + i].From.X - XaxisResolution / 2, UploadPoints[XaxisResolution / 2 + i].From.Y);\n                        UploadPoints[i].To = new Point(UploadPoints[XaxisResolution / 2 + i].To.X - XaxisResolution / 2, UploadPoints[XaxisResolution / 2 + i].To.Y);\n                    }\n                    else\n                    {\n                        UploadPoints[i].From = new Point(0, 0);\n                        UploadPoints[i].To = new Point(0, 0);\n                    }\n\n                }\n\n                if (resumeDraw)\n                {\n                    //reset the chart\n\n                    for (int i = 0; i < DownloadLines.Count; i++)\n                    {\n                        DownloadLines[i].From = new Point(Xstart + DownloadPoints[i].From.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(DownloadPoints[i].From.Y, GraphHeight));\n                        DownloadLines[i].To = new Point(Xstart + DownloadPoints[i].To.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(DownloadPoints[i].To.Y, GraphHeight));\n                    }\n                    for (int i = 0; i < UploadLines.Count; i++)\n                    {\n                        UploadLines[i].From = new Point(Xstart + UploadPoints[i].From.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(UploadPoints[i].From.Y, GraphHeight));\n                        UploadLines[i].To = new Point(Xstart + UploadPoints[i].To.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(UploadPoints[i].To.Y, GraphHeight));\n                    }\n                }\n            }\n\n            if (drawPointCount == 0)\n            {\n                DownloadPoints[drawPointCount].From = new Point(0, 0);\n                DownloadPoints[drawPointCount].To = new Point(1, downloadSpeed);\n\n                UploadPoints[drawPointCount].From = new Point(0, 0);\n                UploadPoints[drawPointCount].To = new Point(1, uploadSpeed);\n            }\n            else\n            {\n                DownloadPoints[drawPointCount].From = new Point(drawPointCount, DownloadPoints[drawPointCount - 1].To.Y);\n                DownloadPoints[drawPointCount].To = new Point((drawPointCount + 1), downloadSpeed);\n\n                UploadPoints[drawPointCount].From = new Point(drawPointCount, UploadPoints[drawPointCount - 1].To.Y);\n                UploadPoints[drawPointCount].To = new Point((drawPointCount + 1), uploadSpeed);\n            }\n\n            if (resumeDraw)\n            {\n                        \n                if (firstDrawAfterResume && drawPointCount > 0)\n                {\n                    DownloadLines[drawPointCount - 1].From = new Point(Xstart + DownloadPoints[drawPointCount - 1].From.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(DownloadPoints[drawPointCount - 1].From.Y, GraphHeight));\n                    DownloadLines[drawPointCount - 1].To = new Point(Xstart + DownloadPoints[drawPointCount - 1].To.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(DownloadPoints[drawPointCount - 1].To.Y, GraphHeight));\n\n                    UploadLines[drawPointCount - 1].From = new Point(Xstart + UploadPoints[drawPointCount - 1].From.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(UploadPoints[drawPointCount - 1].From.Y, GraphHeight));\n                    UploadLines[drawPointCount - 1].To = new Point(Xstart + UploadPoints[drawPointCount - 1].To.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(UploadPoints[drawPointCount - 1].To.Y, GraphHeight));\n                }\n\n                firstDrawAfterResume = false;\n\n                DownloadLines[drawPointCount].From = new Point(Xstart + DownloadPoints[drawPointCount].From.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(DownloadPoints[drawPointCount].From.Y, GraphHeight));\n                DownloadLines[drawPointCount].To = new Point(Xstart + DownloadPoints[drawPointCount].To.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(DownloadPoints[drawPointCount].To.Y, GraphHeight));\n\n                UploadLines[drawPointCount].From = new Point(Xstart + UploadPoints[drawPointCount].From.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(UploadPoints[drawPointCount].From.Y, GraphHeight));\n                UploadLines[drawPointCount].To = new Point(Xstart + UploadPoints[drawPointCount].To.X * (GraphWidth / (double)XaxisResolution), ConvToGraphCoords(UploadPoints[drawPointCount].To.Y, GraphHeight));\n\n            }\n            drawPointCount++;\n        }\n\n        public void DrawClear()\n        {\n            drawPointCount = 0;\n            for (int i = 0; i < DownloadPoints.Count; i++)\n            {\n                DownloadLines[i].From = new Point(Xstart, ConvToGraphCoords(0, GraphHeight));  \n                DownloadLines[i].To = new Point(Xstart, ConvToGraphCoords(0, GraphHeight));\n\n                UploadLines[i].From = new Point(Xstart, ConvToGraphCoords(0, GraphHeight));  \n                UploadLines[i].To = new Point(Xstart, ConvToGraphCoords(0, GraphHeight));\n\n                DownloadPoints[i].From = new Point(0, 0);\n                DownloadPoints[i].To = new Point(0, 0);\n\n                UploadPoints[i].From = new Point(0, 0);\n                UploadPoints[i].To = new Point(0, 0);\n            }\n\n            App.Current.Dispatcher.Invoke(() => \n            {\n                for (int i = 0; i < (Xlabels.Count - 1); i++)\n                {\n                    Xlabels[i].Text = (i * 10).ToString();\n                }\n            });\n        }\n\n        public void ChangeYLabel()\n        {\n            App.Current.Dispatcher.Invoke(() =>\n            {\n                long temp = 1;\n                for (int i = 0; i < GridXCount; i++)\n                {\n\n                    if (i == 0 || i == GridXCount - 1)\n                    {\n                        Ylabels[i].Text = \"\";\n                    }\n                    else\n                    {\n                        if (i % 2 == 0)\n                            temp *= 2;\n                        else\n                            temp *= 512;\n\n                        bool useBytes = SettingsManager.Current.NetworkSpeedFormat != 0;\n                        Ylabels[i].Text = DataSizeSuffix.InStr(temp, 1, useBytes, SpeedMagnitude.Auto);\n                    }\n                }\n            });\n        }\n\n        public double ConvToGraphCoords(double value, double height)\n        {\n            if (value > Math.Pow(1024, 2))\n            {\n                return (height) * ((1.0 / 3.0) - (value) / (1024.0 * 1024.0 * 1024.0 * 3.0));\n            }\n            else if (value > Math.Pow(1024, 1))\n            {\n                return (height) * ((2.0 / 3.0) - (value) / (1024.0 * 1024.0 * 3.0));\n            }\n            else\n            {\n                return (height) * ((3.0 / 3.0) - (value) / (1024.0 * 3.0));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/OpenNetMeter.Old.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n\t<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>\n\t<UseWindowsSDKContracts>true</UseWindowsSDKContracts>\n    <UseWindowsForms>true</UseWindowsForms>\n    <UseWPF>true</UseWPF>\n    <ApplicationIcon>..\\..\\Resources\\AppIcon.ico</ApplicationIcon>\n    <RootNamespace>OpenNetMeter</RootNamespace>\n    <Version>$(ProductVersion)</Version>\n    <AssemblyName>$(ProductName)</AssemblyName>\n    <Win32Resource />\n    <ApplicationManifest>app.manifest</ApplicationManifest>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n <ItemGroup>\n    <PackageReference Include=\"Microsoft.Diagnostics.Tracing.TraceEvent\" Version=\"3.1.15\" />\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\n    <PackageReference Include=\"ParallelExtensionsCore\" Version=\"2.1.0\" />\n    <PackageReference Include=\"TaskScheduler\" Version=\"2.11.0\" />\n    <PackageReference Include=\"Microsoft.Extensions.Logging.EventLog\" Version=\"9.0.9\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\DatabaseEngine\\DatabaseEngine.csproj\" />\n    <ProjectReference Include=\"..\\..\\OpenNetMeter.Core\\OpenNetMeter.Core.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Compile Update=\"Properties\\Resources.Designer.cs\">\n      <DesignTime>True</DesignTime>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Update=\"Properties\\Settings.Designer.cs\">\n      <DesignTimeSharedInput>True</DesignTimeSharedInput>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Settings.settings</DependentUpon>\n    </Compile>\n  </ItemGroup>\n\n  <ItemGroup>\n    <EmbeddedResource Update=\"Properties\\Resources.resx\">\n      <Generator>PublicResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Update=\"Properties\\Settings.settings\">\n      <Generator>SettingsSingleFileGenerator</Generator>\n      <LastGenOutput>Settings.Designer.cs</LastGenOutput>\n    </None>\n  </ItemGroup>\n\n  <ItemGroup>\n    <Resource Include=\"..\\..\\Resources\\pin\\pin.png\" Link=\"Resources\\pin\\pin.png\" />\n    <Resource Include=\"..\\..\\Resources\\pin\\pin-dark.png\" Link=\"Resources\\pin\\pin-dark.png\" />\n    <Resource Include=\"..\\..\\Resources\\pin\\unpin.png\" Link=\"Resources\\pin\\unpin.png\" />\n    <Resource Include=\"..\\..\\Resources\\pin\\unpin-dark.png\" Link=\"Resources\\pin\\unpin-dark.png\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/AppSettings.cs",
    "content": "﻿using System.ComponentModel;\nusing System.Diagnostics;\nusing System.Drawing;\n\nnamespace OpenNetMeter.Properties\n{\n    public class AppSettings : INotifyPropertyChanged\n    {\n\n        private bool darkMode;\n        public bool DarkMode { get => darkMode; set { if (darkMode != value) { darkMode = value; OnPropertyChanged(\"DarkMode\"); } } }\n\n        private bool startWithWin;\n        public bool StartWithWin { get => startWithWin; set { if (startWithWin != value) { startWithWin = value; OnPropertyChanged(\"StartWithWin\"); } } }\n\n        private bool minimizeOnStart = true;\n        public bool MinimizeOnStart { get => minimizeOnStart; set { if (minimizeOnStart != value) { minimizeOnStart = value; OnPropertyChanged(\"MinimizeOnStart\"); } } }\n\n        private int networkType = 2;\n        public int NetworkType { get => networkType; set { if (networkType != value) { networkType = value; OnPropertyChanged(\"NetworkType\"); } } }\n\n        private Point winPos;\n        public Point WinPos { get => winPos; set { if (winPos != value) { winPos = value; OnPropertyChanged(\"WinPos\"); } } }\n\n        private Size winSize;\n        public Size WinSize { get => winSize; set { if (winSize != value) { winSize = value; OnPropertyChanged(\"WinSize\"); } } }\n\n        private bool launchFirstTime = true;\n        public bool LaunchFirstTime { get => launchFirstTime; set { if (launchFirstTime != value) { launchFirstTime = value; OnPropertyChanged(\"LaunchFirstTime\"); } } }\n\n        private int launchPage;\n        public int LaunchPage { get => launchPage; set { if (launchPage != value) { launchPage = value; OnPropertyChanged(\"LaunchPage\"); } } }\n\n        private Point miniWidgetPos;\n        public Point MiniWidgetPos { get => miniWidgetPos; set { if (miniWidgetPos != value) { miniWidgetPos = value; OnPropertyChanged(\"MiniWidgetPos\"); } } }\n\n        private bool miniWidgetVisibility;\n        public bool MiniWidgetVisibility { get => miniWidgetVisibility; set { if (miniWidgetVisibility != value) { miniWidgetVisibility = value; OnPropertyChanged(\"MiniWidgetVisibility\"); } } }\n\n        private bool miniWidgetPinned;\n        public bool MiniWidgetPinned { get => miniWidgetPinned; set { if (miniWidgetPinned != value) { miniWidgetPinned = value; OnPropertyChanged(\"MiniWidgetPinned\"); } } }\n\n        private int networkSpeedFormat;\n        public int NetworkSpeedFormat { get => networkSpeedFormat; set { if (networkSpeedFormat != value) { networkSpeedFormat = value; OnPropertyChanged(\"NetworkSpeedFormat\"); } } }\n\n        private int networkSpeedMagnitude;\n        public int NetworkSpeedMagnitude { get => networkSpeedMagnitude; set { if (networkSpeedMagnitude != value) { networkSpeedMagnitude = value; OnPropertyChanged(\"NetworkSpeedMagnitude\"); } } }\n\n        private int miniWidgetTransparentSlider;\n        public int MiniWidgetTransparentSlider { get => miniWidgetTransparentSlider; set { if (miniWidgetTransparentSlider != value) { miniWidgetTransparentSlider = value; OnPropertyChanged(\"MiniWidgetTransparentSlider\"); } } }\n\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n        private void OnPropertyChanged(string propName) {\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n        }\n\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/Global.cs",
    "content": "using System;\nusing System.IO;\nusing System.Reflection;\n\nnamespace OpenNetMeter.Properties\n{\n    internal class Global\n    {\n        public const string AppName = \"OpenNetMeter\";\n\n        public static string GetFilePath()\n        {\n            string path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\n            path = Path.Combine(path, AppName);\n            Directory.CreateDirectory(path);\n            return path;\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/InternalsVisibleTo.cs",
    "content": "using System.Runtime.CompilerServices;\n\n[assembly: InternalsVisibleTo(\"OpenNetMeter.Tests\")]\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace OpenNetMeter.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"16.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    public class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        public static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"OpenNetMeter.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        public static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Icon similar to (Icon).\n        /// </summary>\n        public static System.Drawing.Icon AppIcon {\n            get {\n                object obj = ResourceManager.GetObject(\"AppIcon\", resourceCulture);\n                return ((System.Drawing.Icon)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        public static System.Drawing.Bitmap x48 {\n            get {\n                object obj = ResourceManager.GetObject(\"x48\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized resource of type System.Drawing.Bitmap.\n        /// </summary>\n        public static System.Drawing.Bitmap x64 {\n            get {\n                object obj = ResourceManager.GetObject(\"x64\", resourceCulture);\n                return ((System.Drawing.Bitmap)(obj));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <assembly alias=\"System.Windows.Forms\" name=\"System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n  <data name=\"AppIcon\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\..\\..\\Resources\\AppIcon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"x48\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\..\\..\\Resources\\x48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n  <data name=\"x64\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">\n    <value>..\\..\\..\\Resources\\x64.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\n  </data>\n</root>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Properties/SettingsManager.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\nusing Newtonsoft.Json;\nusing OpenNetMeter.Utilities;\n\nnamespace OpenNetMeter.Properties\n{\n    internal static class SettingsManager\n    {\n        private static readonly string _filePath;\n\n        // Create the one and only instance of our settings. \n        // The UI will bind to this instance and it will never be replaced.\n        public static AppSettings Current { get; } = new AppSettings();\n\n        static SettingsManager()\n        {\n            // Set the path to settings.json in the same directory as the .exe\n            _filePath = Path.Combine(Global.GetFilePath(), \"settings.json\");\n            Load();\n        }\n\n        public static void Load()\n        {\n            // If the file doesn't exist, we just keep the default values \n            // that 'Current' was created with.\n            if (!File.Exists(_filePath))\n                return;\n\n            try\n            {\n                var json = File.ReadAllText(_filePath);\n\n                // Instead of creating a new object,\n                // this updates the properties of the EXISTING 'Current' object.\n                JsonConvert.PopulateObject(json, Current);\n            }\n            catch (Exception ex)\n            {\n                // Log an error if the json file is corrupt, for example.\n                EventLogger.Error(\"Error loading settings\", ex);\n            }\n        }\n\n        public static void Save()\n        {\n            var json = JsonConvert.SerializeObject(Current, Formatting.Indented);\n            File.WriteAllText(_filePath, json);\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/BaseCommand.cs",
    "content": "﻿using System;\nusing System.Windows.Input;\n\nnamespace OpenNetMeter.Utilities\n{\n    internal class BaseCommand : ICommand\n    {\n        private Action<object?> action;\n        private bool canExecute;\n        public BaseCommand(Action<object?> action, bool canExecute)\n        {\n            this.action = action;\n            this.canExecute = canExecute;\n        }\n\n        public bool CanExecute(object? parameter)\n        {\n            return canExecute;\n        }\n\n        public event EventHandler? CanExecuteChanged\n        {\n            add { }    // to silence warning\n            remove { } // to silence warning\n        }\n\n        public void Execute(object? parameter)\n        {\n            action(parameter);\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/ByteArray.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace OpenNetMeter.Utilities\n{\n    internal class ByteArray\n    {\n        public static bool Compare(ReadOnlySpan<byte> a1, ReadOnlySpan<byte> a2)\n        {\n            return a1.SequenceEqual(a2);\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/DataSizeSuffix.cs",
    "content": "﻿using System;\n\nnamespace OpenNetMeter.Utilities\n{\n    public enum SpeedMagnitude\n    {\n        Auto = 0,\n        Kilo = 1,\n        Mega = 2,\n        Giga = 3\n    }\n\n    internal static class DataSizeSuffix\n    {\n        //Bytes = false will give the suffix in bits. Size passed to this should aready be multiplied by 8 for bits\n        internal static string InStr(long value, int decimalPlaces = 1, bool bytes = true, SpeedMagnitude magnitude = SpeedMagnitude.Auto)\n        {\n            (decimal adjustedSize, int mag) = GetAdjustedSize(value, decimalPlaces, magnitude);\n\n            if (bytes)\n                return decimal.Round(adjustedSize, 2).ToString() + InBytes(mag);\n            else\n                return decimal.Round(adjustedSize, 2).ToString() + InBits(mag);\n        }\n\n        internal static (double, int) InInt(long value, int decimalPlaces = 1, SpeedMagnitude magnitude = SpeedMagnitude.Auto)\n        {\n            (decimal adjustedSize, int mag) = GetAdjustedSize(value, decimalPlaces, magnitude);\n\n            return ((double)decimal.Round(adjustedSize, 2), mag);\n        }\n\n        internal static SpeedMagnitude NormalizeMagnitude(int magnitude)\n        {\n            return Enum.IsDefined(typeof(SpeedMagnitude), magnitude) ? (SpeedMagnitude)magnitude : SpeedMagnitude.Auto;\n        }\n\n        private static (decimal adjustedSize, int mag) GetAdjustedSize(long value, int decimalPlaces, SpeedMagnitude magnitude)\n        {\n            int mag;\n            decimal adjustedSize;\n\n            if (magnitude == SpeedMagnitude.Auto)\n            {\n                // mag is 0 for bytes, 1 for KB, 2 for MB, etc.\n                mag = value > 0 ? (int)Math.Log(value, 1024) : 0;\n                if (mag < 0)\n                    mag = 0;\n\n                // 1L << (mag * 10) == 2 ^ (10 * mag)\n                adjustedSize = (decimal)value / (1L << mag * 10);\n\n                if (Math.Round(adjustedSize, decimalPlaces) >= 1000)\n                {\n                    mag += 1;\n                    adjustedSize /= 1024;\n                }\n            }\n            else\n            {\n                mag = (int)magnitude;\n                adjustedSize = (decimal)value / (1L << mag * 10);\n            }\n\n            return (adjustedSize, mag);\n        }\n\n        private static string InBytes(int value)\n        {\n            return value == 6 ? \"EB\" : value == 5 ? \"PB\" : value == 4 ? \"TB\" : value == 3 ? \"GB\" : value == 2 ? \"MB\" : value == 1 ? \"KB\" : value == 0 ? \"B\" : \"Error\";\n        }\n\n        private static string InBits(int value)\n        {\n            return value == 6 ? \"Eb\" : value == 5 ? \"Pb\" : value == 4 ? \"Tb\" : value == 3 ? \"Gb\" : value == 2 ? \"Mb\" : value == 1 ? \"Kb\" : value == 0 ? \"b\" : \"Error\";\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/EventLogger.cs",
    "content": "using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Runtime.CompilerServices;\n\nnamespace OpenNetMeter.Utilities\n{\n    public static class EventLogger\n    {\n        private const string EventSourceName = \"OpenNetMeter\";\n        private const int MaxEventLogMessageLength = 31839;\n\n        public static void Info(string message, int eventId = 1000, short category = 0)\n        {\n            WriteEntrySafe(message, EventLogEntryType.Information, eventId, category);\n        }\n\n        public static void Warn(string message, int eventId = 2000, short category = 0)\n        {\n            WriteEntrySafe(message, EventLogEntryType.Warning, eventId, category);\n        }\n\n        public static void Error(\n            string message,\n            int eventId = 3000,\n            short category = 0,\n            [CallerMemberName] string memberName = \"\",\n            [CallerFilePath] string filePath = \"\",\n            [CallerLineNumber] int lineNumber = 0)\n        {\n            string logMessage = AddCallerContext(message, memberName, filePath, lineNumber);\n            WriteEntrySafe(logMessage, EventLogEntryType.Error, eventId, category);\n        }\n\n        public static void Error(\n            Exception ex,\n            int eventId = 3001,\n            short category = 0,\n            [CallerMemberName] string memberName = \"\",\n            [CallerFilePath] string filePath = \"\",\n            [CallerLineNumber] int lineNumber = 0)\n        {\n            Error(\"Unhandled exception\", ex, eventId, category, memberName, filePath, lineNumber);\n        }\n\n        public static void Error(\n            string message,\n            Exception ex,\n            int eventId = 3001,\n            short category = 0,\n            [CallerMemberName] string memberName = \"\",\n            [CallerFilePath] string filePath = \"\",\n            [CallerLineNumber] int lineNumber = 0)\n        {\n            string exceptionText = ex is AggregateException aggregateEx\n                ? aggregateEx.Flatten().ToString()\n                : ex.ToString();\n            string errorMessage = $\"{message}{Environment.NewLine}{exceptionText}\";\n            string logMessage = AddCallerContext(errorMessage, memberName, filePath, lineNumber);\n            WriteEntrySafe(logMessage, EventLogEntryType.Error, eventId, category);\n        }\n\n        private static string AddCallerContext(string message, string memberName, string filePath, int lineNumber)\n        {\n            string fileName = string.IsNullOrWhiteSpace(filePath) ? \"unknown\" : Path.GetFileName(filePath);\n            return $\"[{fileName}:{lineNumber} in {memberName}] {message}\";\n        }\n\n        private static void WriteEntrySafe(string message, EventLogEntryType entryType, int eventId, short category)\n        {\n            string safeMessage = Truncate(message);\n            Debug.WriteLine(safeMessage);\n\n            try\n            {\n                EventLog.WriteEntry(EventSourceName, safeMessage, entryType, eventId, category);\n            }\n            catch (Exception writeEx)\n            {\n                Debug.WriteLine($\"[EventLogger fallback] Failed to write to Windows Event Log: {writeEx}\");\n            }\n        }\n\n        private static string Truncate(string message)\n        {\n            if (message.Length <= MaxEventLogMessageLength)\n                return message;\n\n            const string suffix = \"... [truncated]\";\n            int maxLength = MaxEventLogMessageLength - suffix.Length;\n            return message[..Math.Max(0, maxLength)] + suffix;\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/IconToImgSource.cs",
    "content": "﻿using System.Drawing;\nusing System.Windows;\nusing System.Windows.Interop;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\n\nnamespace OpenNetMeter.Utilities\n{\n    public static class IconToImgSource\n    {\n        public static ImageSource ToImageSource(this Icon icon)\n        {\n            ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(\n                icon.Handle,\n                Int32Rect.Empty,\n                BitmapSizeOptions.FromEmptyOptions());\n\n            return imageSource;\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/JsonHelper.cs",
    "content": "﻿using System.Net.Http;\nusing System.Threading.Tasks;\nusing Newtonsoft.Json; \n\nnamespace OpenNetMeter.Utilities\n{\n    public static class JsonHelper\n    {\n        /// <summary>\n        /// Serializes any C# object into a pretty-printed (indented) JSON string.\n        /// </summary>\n        /// <param name=\"obj\">The object to serialize.</param>\n        /// <param name=\"settings\">Optional custom JSON settings.</param>\n        /// <returns>An indented JSON string representation of the object.</returns>\n        public static string PrettyPrint(object obj, JsonSerializerSettings? settings = null)\n        {\n            if (obj == null)\n            {\n                return \"null\"; // Standard JSON representation for null\n            }\n\n            try\n            {\n                // Use default settings if none are provided\n                var defaultSettings = new JsonSerializerSettings\n                {\n                    Formatting = Formatting.Indented,\n                    // Add other common settings here if you like, e.g.:\n                    // ReferenceLoopHandling = ReferenceLoopHandling.Ignore,\n                    // NullValueHandling = NullValueHandling.Ignore\n                };\n\n                return JsonConvert.SerializeObject(obj, settings ?? defaultSettings);\n            }\n            catch (JsonException ex)\n            {\n                // Handle serialization errors (e.g., self-referencing loops)\n                return $\"Error serializing object: {ex.Message}\";\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/PeriodicWork.cs",
    "content": "using System;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace OpenNetMeter.Utilities\n{\n    internal sealed class PeriodicWork : IDisposable, IAsyncDisposable\n    {\n        private readonly string name;\n        private readonly TimeSpan interval;\n        private readonly SemaphoreSlim stopLock = new SemaphoreSlim(1, 1);\n\n        private Task? runTask;\n        private PeriodicTimer? timer;\n        private CancellationTokenSource? cts;\n\n        public PeriodicWork(string name, TimeSpan interval)\n        {\n            this.name = name;\n            this.interval = interval;\n        }\n\n        public void Start(Func<CancellationToken, Task> onTick)\n        {\n            if (runTask != null)\n                return; // already running\n\n            cts = new CancellationTokenSource();\n            timer = new PeriodicTimer(interval);\n            runTask = RunAsync(onTick, cts.Token, timer);\n        }\n\n        private async Task RunAsync(Func<CancellationToken, Task> onTick, CancellationToken token, PeriodicTimer timer)\n        {\n            try\n            {\n                while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))\n                {\n                    try\n                    {\n                        await onTick(token).ConfigureAwait(false);\n                    }\n                    catch (OperationCanceledException)\n                    {\n                        throw;\n                    }\n                    catch (Exception ex)\n                    {\n                        EventLogger.Error($\"{name} tick error\", ex);\n                    }\n                }\n            }\n            catch (OperationCanceledException)\n            {\n                Debug.WriteLine($\"{name} task cancelled\");\n            }\n        }\n\n        public async Task StopAsync()\n        {\n            await stopLock.WaitAsync().ConfigureAwait(false);\n            try\n            {\n                if (runTask == null)\n                    return;\n\n                cts?.Cancel();\n                try\n                {\n                    await runTask.ConfigureAwait(false);\n                }\n                catch (OperationCanceledException)\n                {\n                    // expected on cancellation\n                }\n            }\n            finally\n            {\n                timer?.Dispose();\n                cts?.Dispose();\n                runTask = null;\n                timer = null;\n                cts = null;\n                stopLock.Release();\n            }\n        }\n\n        public void Stop()\n        {\n            StopAsync().GetAwaiter().GetResult();\n        }\n\n        public void Dispose()\n        {\n            Stop();\n        }\n\n        public ValueTask DisposeAsync()\n        {\n            return new ValueTask(StopAsync());\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/ProcessIconCache.cs",
    "content": "using System;\nusing System.Collections.Concurrent;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.IO;\nusing System.Linq;\nusing System.Windows.Media;\n\nnamespace OpenNetMeter.Utilities\n{\n    public static class ProcessIconCache\n    {\n        private static readonly ConcurrentDictionary<string, ImageSource?> IconCache = new(StringComparer.OrdinalIgnoreCase);\n        private static readonly ImageSource? DefaultIcon = CreateDefaultIcon();\n\n        public static ImageSource? GetIcon(string processName)\n        {\n            if (string.IsNullOrWhiteSpace(processName))\n                return DefaultIcon;\n\n            return IconCache.GetOrAdd(processName, FetchIcon);\n        }\n\n        private static ImageSource? FetchIcon(string processName)\n        {\n            try\n            {\n                string nameWithoutExtension = Path.GetFileNameWithoutExtension(processName);\n                Process[] processes = Process.GetProcessesByName(nameWithoutExtension);\n                try\n                {\n                    Process? process = processes.FirstOrDefault();\n                    string? path = process?.MainModule?.FileName;\n\n                    if (!string.IsNullOrWhiteSpace(path) && File.Exists(path))\n                    {\n                        using Icon? icon = Icon.ExtractAssociatedIcon(path);\n                        if (icon != null)\n                        {\n                            ImageSource image = IconToImgSource.ToImageSource(icon);\n                            if (image.CanFreeze)\n                                image.Freeze();\n                            return image;\n                        }\n                    }\n                }\n                finally\n                {\n                    foreach (Process proc in processes)\n                    {\n                        proc.Dispose();\n                    }\n                }\n            }\n            catch (Win32Exception winError)\n            {\n                EventLogger.Error(\"Failed to fetch process icon (Win32 error)\", winError);\n            }\n            catch (SystemException sysExError)\n            {\n                EventLogger.Error(\"Failed to fetch process icon\", sysExError);\n            }\n\n            return DefaultIcon;\n        }\n\n        private static ImageSource? CreateDefaultIcon()\n        {\n            try\n            {\n                using Icon icon = (Icon)SystemIcons.Application.Clone();\n                ImageSource image = IconToImgSource.ToImageSource(icon);\n                if (image.CanFreeze)\n                    image.Freeze();\n                return image;\n            }\n            catch\n            {\n                return null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/UIMeasure.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace OpenNetMeter.Utilities\n{\n    public static class UIMeasure\n    {\n        public static Size Shape(TextBlock tb)\n        {\n            // Measured Size is bounded to be less than maxSize\n            Size maxSize = new Size(\n                 double.PositiveInfinity,\n                 double.PositiveInfinity);\n            tb.Measure(maxSize);\n            return tb.DesiredSize;\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/UpdateChecker.cs",
    "content": "using Newtonsoft.Json.Linq;\nusing System;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nnamespace OpenNetMeter.Utilities\n{\n    public class UpdateChecker\n    {\n        private const string owner = \"Ashfaaq18\";\n        private const string repo = \"OpenNetMeter\";\n\n        public static async Task<(Version? latestVersion, string? downloadUrl)> CheckForUpdates()\n        {\n            using (var client = new HttpClient())\n            {\n                client.DefaultRequestHeaders.Add(\"User-Agent\", \"OpenNetMeter\");\n                var response = await client.GetAsync($\"https://api.github.com/repos/{owner}/{repo}/releases/latest\");\n\n                if (response.IsSuccessStatusCode)\n                {\n                    var jsonString = await response.Content.ReadAsStringAsync();\n                    var jsonObject = JObject.Parse(jsonString);\n                    string prettyJson = JsonHelper.PrettyPrint(jsonObject);\n                    Debug.WriteLine(prettyJson);\n                    var latestVersion = jsonObject[\"tag_name\"]?.ToString();\n                    if (latestVersion == null) { return (null, null); }\n\n                    var assets = jsonObject[\"assets\"] as JArray;\n                    var firstAsset = assets?.FirstOrDefault();\n                    var downloadUrl = firstAsset?[\"browser_download_url\"]?.ToString();\n\n                    return (new Version(latestVersion.Substring(1)), downloadUrl);\n                }\n            }\n            return (null, null);\n        }\n    }\n}"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/WindowsNetworkCaptureService.cs",
    "content": "using System;\nusing System.ComponentModel;\nusing OpenNetMeter.Models;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Utilities\n{\n    public sealed class WindowsNetworkCaptureService : INetworkCaptureService\n    {\n        private NetworkProcess? networkProcess;\n        private readonly object syncLock = new object();\n        private bool disposed;\n\n        public event EventHandler<NetworkSnapshotChangedEventArgs>? NetworkChanged;\n        public event EventHandler<NetworkTrafficEventArgs>? TrafficObserved;\n\n        public void Start()\n        {\n            lock (syncLock)\n            {\n                ThrowIfDisposed();\n                if (networkProcess != null)\n                    return;\n\n                networkProcess = new NetworkProcess();\n                networkProcess.PropertyChanged += NetworkProcess_PropertyChanged;\n                networkProcess.Initialize();\n            }\n        }\n\n        public void Stop()\n        {\n            lock (syncLock)\n            {\n                if (networkProcess == null)\n                    return;\n\n                networkProcess.PropertyChanged -= NetworkProcess_PropertyChanged;\n                networkProcess.Dispose();\n                networkProcess = null;\n            }\n        }\n\n        public void Dispose()\n        {\n            lock (syncLock)\n            {\n                if (disposed)\n                    return;\n\n                Stop();\n                disposed = true;\n            }\n        }\n\n        private void NetworkProcess_PropertyChanged(object? sender, PropertyChangedEventArgs e)\n        {\n            if (networkProcess == null)\n                return;\n\n            switch (e.PropertyName)\n            {\n                case nameof(NetworkProcess.IsNetworkOnline):\n                    NetworkChanged?.Invoke(\n                        this,\n                        new NetworkSnapshotChangedEventArgs(\n                            networkProcess.AdapterName,\n                            networkProcess.CurrentAdapterId));\n                    break;\n                case nameof(NetworkProcess.DownloadSpeed):\n                    if (networkProcess.DownloadSpeed > 0)\n                    {\n                        TrafficObserved?.Invoke(\n                            this,\n                            new NetworkTrafficEventArgs(\"Aggregate\", networkProcess.DownloadSpeed, isReceive: true));\n                    }\n\n                    if (networkProcess.UploadSpeed > 0)\n                    {\n                        TrafficObserved?.Invoke(\n                            this,\n                            new NetworkTrafficEventArgs(\"Aggregate\", networkProcess.UploadSpeed, isReceive: false));\n                    }\n                    break;\n            }\n        }\n\n        private void ThrowIfDisposed()\n        {\n            if (disposed)\n                throw new ObjectDisposedException(nameof(WindowsNetworkCaptureService));\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/WindowsProcessIconService.cs",
    "content": "using OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Utilities\n{\n    public sealed class WindowsProcessIconService : IProcessIconService\n    {\n        public object? GetProcessIcon(string processName)\n        {\n            return ProcessIconCache.GetIcon(processName);\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/WindowsStartupRegistrationService.cs",
    "content": "using System;\nusing System.IO;\nusing OpenNetMeter.PlatformAbstractions;\nusing TaskScheduler = Microsoft.Win32.TaskScheduler;\n\nnamespace OpenNetMeter.Utilities\n{\n    public sealed class WindowsStartupRegistrationService : IStartupRegistrationService\n    {\n        private const string TaskFolder = \"OpenNetMeter\";\n        private const string TaskName = \"OpenNetMeterLogon\";\n\n        public bool IsEnabled()\n        {\n            try\n            {\n                TaskScheduler.TaskFolder folder = TaskScheduler.TaskService.Instance.RootFolder.SubFolders[TaskFolder];\n                return folder.Tasks.Exists(TaskName);\n            }\n            catch\n            {\n                return false;\n            }\n        }\n\n        public void SetEnabled(bool enabled, bool startMinimized)\n        {\n            try\n            {\n                TaskScheduler.TaskFolder folder = TaskScheduler.TaskService.Instance.RootFolder.SubFolders[TaskFolder];\n                if (!enabled)\n                {\n                    for (int i = 0; i < folder.Tasks.Count; i++)\n                    {\n                        folder.DeleteTask(folder.Tasks[i].Name);\n                    }\n\n                    TaskScheduler.TaskService.Instance.RootFolder.DeleteFolder(TaskFolder);\n                    return;\n                }\n            }\n            catch (Exception ex)\n            {\n                EventLogger.Error(\"Error while updating startup task registration\", ex);\n            }\n\n            if (enabled)\n            {\n                try\n                {\n                    TaskScheduler.TaskService.Instance.RootFolder.CreateFolder(TaskFolder);\n                    CreateTask(startMinimized);\n                }\n                catch (Exception ex)\n                {\n                    EventLogger.Error(\"Error creating startup task folder/definition\", ex);\n                }\n            }\n        }\n\n        private static void CreateTask(bool startMinimized)\n        {\n            try\n            {\n                TaskScheduler.TaskDefinition td = TaskScheduler.TaskService.Instance.NewTask();\n                td.RegistrationInfo.Description = \"Run OpenNetMeter on system log on\";\n                td.Principal.RunLevel = TaskScheduler.TaskRunLevel.Highest;\n                td.Principal.LogonType = TaskScheduler.TaskLogonType.InteractiveToken;\n                td.Settings.DisallowStartIfOnBatteries = false;\n                td.Settings.StopIfGoingOnBatteries = false;\n                td.Settings.Compatibility = TaskScheduler.TaskCompatibility.V2_3;\n\n                TaskScheduler.LogonTrigger logonTrigger = new TaskScheduler.LogonTrigger\n                {\n                    Enabled = true,\n                    UserId = null\n                };\n                td.Triggers.Add(logonTrigger);\n\n                TaskScheduler.ExecAction action = new TaskScheduler.ExecAction\n                {\n                    Path = Path.Combine(AppContext.BaseDirectory, \"OpenNetMeter.exe\")\n                };\n                if (startMinimized)\n                    action.Arguments = \"/StartMinimized\";\n                td.Actions.Add(action);\n\n                TaskScheduler.TaskService.Instance.RootFolder.SubFolders[TaskFolder].RegisterTaskDefinition(TaskName, td);\n            }\n            catch (Exception ex)\n            {\n                EventLogger.Error(\"Error creating startup task\", ex);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Utilities/WpfUiDispatcher.cs",
    "content": "using System;\nusing System.Windows.Threading;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Utilities\n{\n    public sealed class WpfUiDispatcher : IUiDispatcher\n    {\n        private readonly Dispatcher dispatcher;\n\n        public WpfUiDispatcher(Dispatcher? dispatcher = null)\n        {\n            this.dispatcher = dispatcher ?? Dispatcher.CurrentDispatcher;\n        }\n\n        public bool CheckAccess() => dispatcher.CheckAccess();\n\n        public void Post(Action action)\n        {\n            dispatcher.Invoke(action);\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/ViewModels/DataUsageHistoryVM.cs",
    "content": "﻿using OpenNetMeter.Models;\nusing OpenNetMeter.PlatformAbstractions;\nusing OpenNetMeter.Utilities;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Windows.Input;\nusing System.Windows.Media;\n\nnamespace OpenNetMeter.ViewModels\n{\n    \n    public class DataUsageHistoryVM : INotifyPropertyChanged\n    {\n        public DateTime DateMax { get; private set; }\n        public DateTime DateMin { get; private set; }\n        public DateTime DateStart { get; set; }\n        public DateTime DateEnd { get; set; }\n\n        private string? selectedProfile;\n        public string? SelectedProfile \n        {\n            get { return selectedProfile; }\n            set\n            {\n                if(value != selectedProfile)\n                {\n                    selectedProfile = value;\n                    OnPropertyChanged(\"SelectedProfile\");\n                }\n            }\n        }\n\n        public ObservableCollection<string>? Profiles { get; set; }\n        public ObservableCollection<MyProcess_Small> MyProcesses { get; set; }\n\n        private long totalDownloadData;\n        public long TotalDownloadData \n        {\n            get { return totalDownloadData; }\n            set\n            {\n                if (value != totalDownloadData)\n                {\n                    totalDownloadData = value;\n                    OnPropertyChanged(\"TotalDownloadData\");\n                }\n            }\n        }\n\n        private long totalUploadData;\n        public long TotalUploadData\n        {\n            get { return totalUploadData; }\n            set\n            {\n                if (value != totalUploadData)\n                {\n                    totalUploadData = value;\n                    OnPropertyChanged(\"TotalUploadData\");\n                }\n            }\n        }\n\n        private readonly IUiDispatcher uiDispatcher;\n        private readonly IProcessIconService processIconService;\n\n        public ICommand FilterBtn { get; set; }\n\n        public DataUsageHistoryVM()\n            : this(new WpfUiDispatcher(App.Current?.Dispatcher), new WindowsProcessIconService())\n        {\n        }\n\n        public DataUsageHistoryVM(IUiDispatcher uiDispatcher, IProcessIconService processIconService)\n        {\n            this.uiDispatcher = uiDispatcher;\n            this.processIconService = processIconService;\n            UpdateDates();\n            TotalDownloadData = 0;\n            TotalUploadData = 0;\n\n            PropertyChanged += DataUsageHistoryVM_PropertyChanged;\n\n            Profiles = new ObservableCollection<string>();\n            MyProcesses = new ObservableCollection<MyProcess_Small>();;\n\n            //set button command\n            FilterBtn = new BaseCommand(Filter, true);\n\n            // initial load\n            GetAllDBFiles();\n        }\n\n        public void UpdateDates()\n        {\n            DateStart = DateTime.Today;\n            DateEnd = DateTime.Today;\n            DateMax = DateTime.Today;\n            DateMin = DateTime.Today.AddDays(-1 * ApplicationDB.DataStoragePeriodInDays);\n        }\n\n        private void DataUsageHistoryVM_PropertyChanged(object? sender, PropertyChangedEventArgs e)\n        {\n            switch (e.PropertyName)\n            {\n                case \"SelectedProfile\":\n                    MyProcesses.Clear();\n                    TotalDownloadData = 0;\n                    TotalUploadData = 0;\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        private void Filter(object? obj)\n        {\n            MyProcesses.Clear();\n            TotalDownloadData = 0;\n            TotalUploadData = 0;\n            Debug.WriteLine($\"Filter {DateStart.ToString(\"d\")} | {DateEnd.ToString(\"d\")}\");\n            if(SelectedProfile != null)\n            {\n                using (ApplicationDB dB = new ApplicationDB(SelectedProfile, new string[] { \"Read Only=True\"}))\n                {\n                    List<List<object>> dataStats = dB.GetDataSum_ProcessDateTable(DateStart, DateEnd);\n                    for(int i = 0; i< dataStats.Count; i++)\n                    {\n                        if(dataStats[i].Count == 3)\n                        {\n                            if(!Convert.IsDBNull(dataStats[i][0]) && !Convert.IsDBNull(dataStats[i][1]) && !Convert.IsDBNull(dataStats[i][2]))\n                            {\n                                string processName = Convert.ToString(dataStats[i][0])!;\n                                ImageSource? icon = processIconService.GetProcessIcon(processName) as ImageSource;\n                                MyProcesses.Add(new MyProcess_Small(processName, Convert.ToInt64(dataStats[i][1]), Convert.ToInt64(dataStats[i][2]), icon));\n\n                                TotalDownloadData += Convert.ToInt64(dataStats[i][1]);\n                                TotalUploadData += Convert.ToInt64(dataStats[i][2]);\n                            }\n                            //Debug.WriteLine($\"processID: {dataStats[i][0]}, dataRecieved: {dataStats[i][1]}, dataSent: {dataStats[i][2]}\");\n                        }\n                    }\n                }\n            }\n        }\n\n        public void GetAllDBFiles()\n        {\n            // Ensure collection updates occur on the UI thread\n            if (!uiDispatcher.CheckAccess())\n            {\n                uiDispatcher.Post(GetAllDBFiles);\n                return;\n            }\n\n            Profiles?.Clear();\n\n            // Ensure DB exists and read adapters from it\n            using (ApplicationDB dB = new ApplicationDB(string.Empty))\n            {\n                dB.CreateTable();\n                var adapters = dB.GetAllAdapters();\n                foreach (var a in adapters)\n                {\n                    Profiles?.Add(a);\n                }\n            }\n\n            if (Profiles?.Count > 0)\n                SelectedProfile = Profiles?[0];\n        }\n\n        public void DeleteAllDBFiles()\n        {\n            try\n            {\n                string path = ApplicationDB.GetUnifiedDBFullPath();\n                if (File.Exists(path))\n                    File.Delete(path);\n                Profiles?.Clear();\n                SelectedProfile = null;\n            }\n            catch (IOException ex)\n            {\n                EventLogger.Error(\"Failed to delete usage database file\", ex);\n            }\n        }\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n        private void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n\n    }\n}\n\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/ViewModels/DataUsageSummaryVM.cs",
    "content": "﻿using System;\nusing OpenNetMeter.Models;\nusing System.ComponentModel;\nusing System.Collections.Concurrent;\n\nnamespace OpenNetMeter.ViewModels\n{\n    public class DataUsageSummaryVM : INotifyPropertyChanged\n    {\n        private long todayDownloadData;\n        public long TodayDownloadData\n        {\n            get { return todayDownloadData; }\n            set\n            {\n                todayDownloadData = value;\n                OnPropertyChanged(\"TodayDownloadData\");\n            }\n        }\n\n        private long todayUploadData;\n        public long TodayUploadData\n        {\n            get { return todayUploadData; }\n            set\n            {\n                todayUploadData = value;\n                OnPropertyChanged(\"TodayUploadData\");\n            }\n        }\n\n        private DateTime dateMin;\n        public DateTime DateMin\n        {\n            get { return dateMin; }\n            private set\n            {\n                if (dateMin != value)\n                {\n                    dateMin = value;\n                    OnPropertyChanged(\"DateMin\");\n                }\n            }\n        }\n\n        private DateTime dateMax;\n        public DateTime DateMax\n        {\n            get { return dateMax; }\n            private set\n            {\n                if (dateMax != value)\n                {\n                    dateMax = value;\n                    OnPropertyChanged(\"DateMax\");\n                }\n            }\n        }\n\n        private DateTime sinceDate;\n        public DateTime SinceDate\n        {\n            get { return sinceDate; }\n            set\n            {\n                DateTime newDate = value.Date;\n\n                if (newDate > DateMax)\n                    newDate = DateMax;\n                else if (newDate < DateMin)\n                    newDate = DateMin;\n\n                if (sinceDate != newDate)\n                {\n                    sinceDate = newDate;\n                    OnPropertyChanged(\"SinceDate\");\n                }\n            }\n        }\n\n        private long currentSessionDownloadData;\n        public long CurrentSessionDownloadData\n        {\n            get { return currentSessionDownloadData; }\n            set\n            {\n                currentSessionDownloadData = value;\n                OnPropertyChanged(\"CurrentSessionDownloadData\");\n            }\n        }\n\n        private long currentSessionUploadData;\n        public long CurrentSessionUploadData\n        {\n            get { return currentSessionUploadData; }\n            set\n            {\n                currentSessionUploadData = value;\n                OnPropertyChanged(\"CurrentSessionUploadData\");\n            }\n        }\n\n        public ObservableConcurrentDictionary<string, MyProcess_Big> MyProcesses { get; set; }\n\n        public SpeedGraph Graph { get; set; }\n\n        public DataUsageSummaryVM()\n        {\n            TodayDownloadData = 0;\n            TodayUploadData = 0;\n            CurrentSessionDownloadData = 0;\n            CurrentSessionUploadData = 0;\n\n            MyProcesses = new ObservableConcurrentDictionary<string, MyProcess_Big>();\n\n            Graph = new SpeedGraph(7, 7);\n            Graph.Init();\n\n            RefreshDateBounds();\n            SinceDate = DateTime.Today;\n        }\n\n        public void RefreshDateBounds()\n        {\n            DateMax = DateTime.Today;\n            DateMin = DateTime.Today.AddDays(-1 * ApplicationDB.DataStoragePeriodInDays);\n\n            if (sinceDate == default)\n                sinceDate = DateMax;\n\n            if (SinceDate > DateMax)\n            {\n                SinceDate = DateMax;\n            }\n            else if (SinceDate < DateMin)\n            {\n                SinceDate = DateMin;\n            }\n        }\n\n        //------property changers---------------//\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n\n        private void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/ViewModels/MainWindowVM.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Windows.Input;\nusing OpenNetMeter.Models;\nusing System.Linq;\nusing OpenNetMeter.Utilities;\nusing OpenNetMeter.Properties;\nusing OpenNetMeter.PlatformAbstractions;\nusing System.Windows.Media;\n\nnamespace OpenNetMeter.ViewModels\n{\n    public class MainWindowVM : INotifyPropertyChanged, IDisposable\n    {\n        private readonly DataUsageSummaryVM dusvm;\n        private readonly DataUsageHistoryVM duhvm;\n        private readonly MiniWidgetVM mwvm;\n        private readonly IProcessIconService processIconService;\n        public SettingsVM svm;\n        private readonly NetworkProcess netProc;\n        public ICommand SwitchTabCommand { get; set; }\n        private int tabBtnToggle;\n        public int TabBtnToggle\n        {\n            get { return tabBtnToggle; }\n            set { tabBtnToggle = value; OnPropertyChanged(\"TabBtnToggle\"); }\n        }\n\n        private object? selectedViewModel;\n        public object? SelectedViewModel\n        {\n            get { return selectedViewModel; }\n            set { selectedViewModel = value; OnPropertyChanged(\"SelectedViewModel\"); }\n        }\n        public long downloadSpeed;\n        public long DownloadSpeed\n        {\n            get { return downloadSpeed; }\n            set { downloadSpeed = value; OnPropertyChanged(\"DownloadSpeed\"); }\n        }\n        public long uploadSpeed;\n        public long UploadSpeed\n        {\n            get { return uploadSpeed; }\n            set { uploadSpeed = value; OnPropertyChanged(\"UploadSpeed\"); }\n        }\n\n        private string networkStatus;\n        public string NetworkStatus\n        {\n            get { return networkStatus; }\n            set { networkStatus = value; OnPropertyChanged(\"NetworkStatus\"); }\n        }\n\n        private DateTime date1;\n        private DateTime date2;\n\n        private long initSinceDateTotalDownloadData = 0;\n        private long initSinceDateTotalUploadData = 0;\n        private long sinceDateSessionDownloadBaseline = 0;\n        private long sinceDateSessionUploadBaseline = 0;\n\n        private enum TabPage\n        {\n            Summary,\n            History,\n            Settings\n        }\n\n        public MainWindowVM(MiniWidgetVM mw_DataContext, ConfirmationDialogVM cd_DataContext) //runs once during app init\n            : this(mw_DataContext, cd_DataContext, new WindowsProcessIconService())\n        {\n        }\n\n        public MainWindowVM(MiniWidgetVM mw_DataContext, ConfirmationDialogVM cd_DataContext, IProcessIconService processIconService) //runs once during app init\n        {\n            this.processIconService = processIconService;\n            DownloadSpeed = 0;\n            UploadSpeed = 0;\n            date1 = DateTime.Now;\n            date2 = DateTime.Now;\n\n            networkStatus = \"\";\n\n            mwvm = mw_DataContext;\n            svm = new SettingsVM(mw_DataContext, cd_DataContext);\n            svm.PropertyChanged += Svm_PropertyChanged;\n            dusvm = new DataUsageSummaryVM();\n            duhvm = new DataUsageHistoryVM();\n\n            netProc = new NetworkProcess();\n            netProc.PropertyChanged += NetProc_PropertyChanged;\n            netProc.Initialize(); //have to call this after subscribing to property changer\n            dusvm.PropertyChanged += Dusvm_PropertyChanged;\n\n            // Populate adapters list from the unified DB\n            duhvm.GetAllDBFiles();\n\n            //intial startup page\n            TabBtnToggle = SettingsManager.Current.LaunchPage;\n            switch (TabBtnToggle)\n            {\n                case ((int)TabPage.Summary):\n                    SelectedViewModel = dusvm;\n                    break;\n                case ((int)TabPage.History):\n                    SelectedViewModel = duhvm;\n                    break;\n                case ((int)TabPage.Settings):\n                    SelectedViewModel = svm;\n                    break;\n                default:\n                    SelectedViewModel = dusvm;\n                    TabBtnToggle = ((int)TabPage.Summary);\n                    break;\n            }\n\n            //assign basecommand\n            SwitchTabCommand = new BaseCommand(SwitchTab, true);\n\n            //get initial data usage details from the database\n            RefreshSummaryBaseline();\n            UpdateSummaryTab();\n        }\n\n        private void Svm_PropertyChanged(object? sender, PropertyChangedEventArgs e)\n        {\n            switch (e.PropertyName)\n            {\n                case \"DeleteAllFiles\":\n                    if (svm.DeleteAllFiles)\n                    {\n                        if (netProc.IsNetworkOnline != \"Disconnected\")\n                        {\n                            netProc.EndNetworkProcess();\n                            duhvm.DeleteAllDBFiles();\n                            netProc.StartNetworkProcess();\n                        }\n                        else\n                        {\n                            duhvm.DeleteAllDBFiles();\n                        }\n                        svm.DeleteAllFiles = false;\n                    }\n                    break;\n                case \"NetworkSpeedFormat\":\n                    dusvm.Graph.ChangeYLabel();\n                    dusvm.Graph.DrawClear();\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        private void UpdateMainWinSpeed()\n        {\n            DownloadSpeed = netProc.DownloadSpeed;\n            UploadSpeed = netProc.UploadSpeed;\n        }\n\n        private void UpdateMiniWidgetValues()\n        {\n            mwvm.DownloadSpeed = DownloadSpeed;\n            mwvm.CurrentSessionDownloadData = netProc.CurrentSessionDownloadData;\n            mwvm.UploadSpeed = UploadSpeed;\n            mwvm.CurrentSessionUploadData = netProc.CurrentSessionUploadData;\n        }\n\n        private void RefreshSummaryBaseline()\n        {\n            using (ApplicationDB dB = new ApplicationDB(netProc.AdapterName))\n            {\n                (long, long) totals = dB.GetDataSumBetweenDates(dusvm.SinceDate, DateTime.Today);\n                initSinceDateTotalDownloadData = totals.Item1;\n                initSinceDateTotalUploadData = totals.Item2;\n            }\n\n            sinceDateSessionDownloadBaseline = netProc.CurrentSessionDownloadData;\n            sinceDateSessionUploadBaseline = netProc.CurrentSessionUploadData;\n\n            UpdateTodayTotals();\n        }\n\n        private void UpdateTodayTotals()\n        {\n            long sessionDownloadDelta = netProc.CurrentSessionDownloadData - sinceDateSessionDownloadBaseline;\n            long sessionUploadDelta = netProc.CurrentSessionUploadData - sinceDateSessionUploadBaseline;\n\n            if (sessionDownloadDelta < 0)\n                sessionDownloadDelta = 0;\n            if (sessionUploadDelta < 0)\n                sessionUploadDelta = 0;\n\n            dusvm.TodayDownloadData = initSinceDateTotalDownloadData + sessionDownloadDelta;\n            dusvm.TodayUploadData = initSinceDateTotalUploadData + sessionUploadDelta;\n        }\n\n        private void UpdateSummaryTab()\n        {\n            //summary tab graph points\n            dusvm.Graph.DrawPoints(DownloadSpeed, UploadSpeed);\n\n            //summary tab session usage variables\n            dusvm.CurrentSessionDownloadData = netProc.CurrentSessionDownloadData;\n            dusvm.CurrentSessionUploadData = netProc.CurrentSessionUploadData;\n\n            UpdateTodayTotals();\n\n            UpdateMyProcessTable();\n        }\n\n        private void UpdateMyProcessTable()\n        {\n            if (netProc.MyProcesses != null && netProc.MyProcessesBuffer != null && dusvm.MyProcesses != null && netProc.PushToDBBuffer != null)\n            {\n                using (ApplicationDB dB = new ApplicationDB(netProc.AdapterName))\n                {\n                    if (dB.CreateTable() < 0)\n                        Debug.WriteLine(\"Error: Create table\");\n                    else\n                    {\n                        //when the application stays open during a day transition\n                        if ((date2.Date - date1.Date).Days > 0)\n                        {\n                            dusvm.TodayDownloadData = 0;\n                            dusvm.TodayUploadData = 0;\n                            dB.UpdateDatesInDB();\n                            duhvm.UpdateDates();\n                            dusvm.RefreshDateBounds();\n                            RefreshSummaryBaseline();\n                            date1 = date2;\n                        }\n\n                        foreach (KeyValuePair<string, MyProcess_Big> app in dusvm.MyProcesses)\n                        {\n                            dusvm.MyProcesses[app.Key].CurrentDataRecv = 0;\n                            dusvm.MyProcesses[app.Key].CurrentDataSend = 0;\n                        }\n\n                        netProc.IsBufferTime = true;\n\n                        //this dictionary is locked from being accessible by the other threads like the network data capture Recv()\n                        lock (netProc.MyProcesses)\n                        {\n                            foreach (KeyValuePair<string, MyProcess_Small?> app in netProc.MyProcesses) //the contents of this loops remain only for a sec (related to NetworkProcess.cs=>CaptureNetworkSpeed())\n                            {\n                                EnsureProcessEntry(app.Key);\n                                if (app.Value!.CurrentDataRecv == 0 && app.Value!.CurrentDataSend == 0)\n                                {\n                                    Debug.WriteLine($\"Both zero {app.Key}\");\n                                }\n                                dusvm.MyProcesses[app.Key].CurrentDataRecv = app.Value!.CurrentDataRecv;\n                                dusvm.MyProcesses[app.Key].CurrentDataSend = app.Value!.CurrentDataSend;\n                                dusvm.MyProcesses[app.Key].TotalDataRecv += app.Value!.CurrentDataRecv;\n                                dusvm.MyProcesses[app.Key].TotalDataSend += app.Value!.CurrentDataSend;\n\n                                /*\n                                Debug.WriteLine($\"CurrentDataRecv:  {dusvm.MyProcesses[app.Key].CurrentDataRecv} , \"    +\n                                                $\"CurrentDataSend:  {dusvm.MyProcesses[app.Key].CurrentDataSend} , \"    +\n                                                $\"TotalDataRecv:    {dusvm.MyProcesses[app.Key].TotalDataRecv} , \"      +\n                                                $\"TotalDataSend:    {dusvm.MyProcesses[app.Key].TotalDataSend} , \"      );\n                                */\n\n                                lock (netProc.PushToDBBuffer)\n                                {\n                                    //push data to a buffer which will be pushed to the DB later\n                                    netProc.PushToDBBuffer!.TryAdd(app.Key, new MyProcess_Small(app.Key, 0, 0));\n                                    netProc.PushToDBBuffer[app.Key]!.CurrentDataRecv += dusvm.MyProcesses[app.Key].CurrentDataRecv;\n                                    netProc.PushToDBBuffer[app.Key]!.CurrentDataSend += dusvm.MyProcesses[app.Key].CurrentDataSend;\n                                }\n                            }\n\n                            netProc.MyProcesses.Clear();\n                        }\n\n                        netProc.IsBufferTime = false;\n\n                        lock (netProc.MyProcessesBuffer)\n                        {\n                            foreach (KeyValuePair<string, MyProcess_Small?> app in netProc.MyProcessesBuffer) //the contents of this loops remain only for a sec (related to NetworkProcess.cs=>CaptureNetworkSpeed())\n                            {\n                                Debug.WriteLine(\"BUFFEEERRRRR!!!!!\");\n                                EnsureProcessEntry(app.Key);\n                                if (app.Value!.CurrentDataRecv == 0 && app.Value!.CurrentDataSend == 0)\n                                {\n                                    Debug.WriteLine($\"Both zero {app.Key}\");\n                                }\n                                dusvm.MyProcesses[app.Key].CurrentDataRecv += app.Value!.CurrentDataRecv;\n                                dusvm.MyProcesses[app.Key].CurrentDataSend += app.Value!.CurrentDataSend;\n                                dusvm.MyProcesses[app.Key].TotalDataRecv += app.Value!.CurrentDataRecv;\n                                dusvm.MyProcesses[app.Key].TotalDataSend += app.Value!.CurrentDataSend;\n\n                                lock (netProc.PushToDBBuffer)\n                                {\n                                    //push data to a buffer which will be pushed to the DB later\n                                    netProc.PushToDBBuffer!.TryAdd(app.Key, new MyProcess_Small(app.Key, 0, 0));\n                                    netProc.PushToDBBuffer[app.Key]!.CurrentDataRecv = dusvm.MyProcesses[app.Key].TotalDataRecv;\n                                    netProc.PushToDBBuffer[app.Key]!.CurrentDataSend = dusvm.MyProcesses[app.Key].TotalDataSend;\n                                }\n                            }\n\n                            netProc.MyProcessesBuffer.Clear();\n                        }\n                    }\n                }\n            }\n        }\n\n        private void EnsureProcessEntry(string processName)\n        {\n            ImageSource? icon = processIconService.GetProcessIcon(processName) as ImageSource;\n\n            if (!dusvm.MyProcesses.TryAdd(processName, new MyProcess_Big(processName, 0, 0, 0, 0, icon)))\n            {\n                if (dusvm.MyProcesses[processName].Icon == null)\n                {\n                    dusvm.MyProcesses[processName].Icon = icon;\n                }\n            }\n        }\n\n        private void UpdateData()\n        {\n            date2 = DateTime.Now;\n\n            UpdateMainWinSpeed();\n\n            UpdateMiniWidgetValues();\n\n            UpdateSummaryTab();\n        }\n\n        private void NetProc_PropertyChanged(object? sender, PropertyChangedEventArgs e)\n        {\n            Stopwatch sw = new Stopwatch();\n            sw.Start();\n            switch (e.PropertyName)\n            {\n                case \"DownloadSpeed\":\n                    UpdateData();\n                    break;\n                case \"IsNetworkOnline\":\n                    if (netProc.IsNetworkOnline == \"Disconnected\")\n                    {\n                        NetworkStatus = \"Disconnected\";\n                        if (dusvm.MyProcesses.Count() > 0)\n                        {\n                            foreach (var row in dusvm.MyProcesses.ToList())\n                            {\n                                dusvm.MyProcesses.Remove(row.Key);\n                            }\n                        }\n                        dusvm.Graph.DrawClear();\n                        dusvm.TodayDownloadData = 0;\n                        dusvm.TodayUploadData = 0;\n                    }\n                    else\n                    {\n                        NetworkStatus = \"Connected : \" + netProc.IsNetworkOnline;\n                        // Ensure current adapter exists in DB and refresh profiles\n                        using (ApplicationDB dB = new ApplicationDB(netProc.AdapterName))\n                        {\n                            dB.CreateTable();\n                            dB.InsertUniqueRow_AdapterTable(netProc.AdapterName);\n                        }\n                        duhvm.GetAllDBFiles();\n                    }\n                    break;\n                default:\n                    break;\n            }\n            sw.Stop();\n            // Debug.WriteLine($\"elapsed time (NetProc): {sw.ElapsedMilliseconds}\");\n        }\n\n        private void Dusvm_PropertyChanged(object? sender, PropertyChangedEventArgs e)\n        {\n            if (e.PropertyName == nameof(DataUsageSummaryVM.SinceDate))\n            {\n                RefreshSummaryBaseline();\n                UpdateSummaryTab();\n            }\n        }\n        private void SwitchTab(object? obj)\n        {\n            string? tab = obj as string;\n            switch (tab)\n            {\n                case \"summary\":\n                    if (TabBtnToggle != ((int)TabPage.Summary))\n                    {\n                        SelectedViewModel = dusvm;\n                        TabBtnToggle = ((int)TabPage.Summary);\n                        SettingsManager.Current.LaunchPage = TabBtnToggle;\n                        SettingsManager.Save();\n                    }\n                    break;\n                case \"history\":\n                    if (TabBtnToggle != ((int)TabPage.History))\n                    {\n                        SelectedViewModel = duhvm;\n                        TabBtnToggle = ((int)TabPage.History);\n                        SettingsManager.Current.LaunchPage = TabBtnToggle;\n                        SettingsManager.Save();\n                    }\n                    break;\n                case \"settings\":\n                    if (TabBtnToggle != ((int)TabPage.Settings))\n                    {\n                        SelectedViewModel = svm;\n                        TabBtnToggle = ((int)TabPage.Settings);\n                        SettingsManager.Current.LaunchPage = TabBtnToggle;\n                        SettingsManager.Save();\n                    }\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n\n        private void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n\n        public void Dispose()\n        {\n\n            dusvm.PropertyChanged -= Dusvm_PropertyChanged;\n\n            if (netProc != null)\n            {\n                netProc.PropertyChanged -= NetProc_PropertyChanged;\n                netProc.Dispose();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/ViewModels/MiniWidgetVM.cs",
    "content": "using OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing System.ComponentModel;\nusing System.Windows;\nusing System.Windows.Controls;\n\nnamespace OpenNetMeter.ViewModels\n{\n    public class MiniWidgetVM : INotifyPropertyChanged\n    {\n        private long currentSessionDownloadData;\n        public long CurrentSessionDownloadData\n        {\n            get { return currentSessionDownloadData; }\n            set\n            {\n                currentSessionDownloadData = value;\n                OnPropertyChanged(\"CurrentSessionDownloadData\");\n            }\n        }\n        private long currentSessionUploadData;\n        public long CurrentSessionUploadData\n        {\n            get { return currentSessionUploadData; }\n            set\n            {\n                currentSessionUploadData = value;\n                OnPropertyChanged(\"CurrentSessionUploadData\");\n            }\n        }\n\n        public long downloadSpeed;\n        public long DownloadSpeed\n        {\n            get { return downloadSpeed; }\n            set\n            {\n                downloadSpeed = value;\n                OnPropertyChanged(\"DownloadSpeed\");\n            }\n        }\n        public long uploadSpeed;\n        public long UploadSpeed\n        {\n            get { return uploadSpeed; }\n            set\n            {\n                uploadSpeed = value;\n                OnPropertyChanged(\"UploadSpeed\");\n            }\n        }\n\n        private double width;\n        public double Width\n        {\n            get { return width; }\n            set\n            {\n                width = value;\n                OnPropertyChanged(\"Width\");\n            }\n        }\n\n        private double height;\n        public double Height\n        {\n            get { return height; }\n            set\n            {\n                height = value;\n                OnPropertyChanged(\"Height\");\n            }\n        }\n\n        public string? backgroundColor;\n        public string? BackgroundColor\n        {\n            get { return backgroundColor; }\n            set\n            {\n                backgroundColor = value;\n                OnPropertyChanged(\"BackgroundColor\");\n            }\n        }\n\n        private bool isPinned;\n        public bool IsPinned\n        {\n            get { return isPinned; }\n            set\n            {\n                if (isPinned == value)\n                {\n                    return;\n                }\n\n                isPinned = value;\n                SettingsManager.Current.MiniWidgetPinned = value;\n                SettingsManager.Save();\n                OnPropertyChanged(\"IsPinned\");\n            }\n        }\n        public MiniWidgetVM()\n        {\n            CurrentSessionDownloadData = 0;\n            CurrentSessionUploadData = 0;\n            DownloadSpeed = 0;\n            UploadSpeed = 0;\n            BackgroundColor = \"#ff212121\";\n\n            int downloadIconSize = 20;\n            Size size1 = UIMeasure.Shape(new TextBlock { Text = \"Total:\", FontSize = 12, Padding = new Thickness(0) });\n            Size size2 = UIMeasure.Shape(new TextBlock { Text = \"1024.00Mb\", FontSize = 12, Padding = new Thickness(5,0,0,0) });\n            int widthMargins = 5 + 5; //these are from the miniwidget xaml margins\n            Width = (size1.Width + size2.Width + widthMargins + downloadIconSize) * 2;\n            int heightMargins = 2 + 2; //these are from the miniwidget xaml margins\n            Height = size1.Height * 2 + heightMargins * 2;\n\n            isPinned = SettingsManager.Current.MiniWidgetPinned;\n            OnPropertyChanged(\"IsPinned\");\n        }\n\n        //------property changers---------------//\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n\n        private void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n    }\n}\n\n\n\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/ViewModels/SettingsVM.cs",
    "content": "using OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing System;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.Reflection;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Windows.Input;\nusing OpenNetMeter.PlatformAbstractions;\n\n\nnamespace OpenNetMeter.ViewModels\n{\n    public class SettingsVM : INotifyPropertyChanged\n    {\n        private bool setStartWithWin;\n        public bool SetStartWithWin\n        {\n            get { return setStartWithWin; }\n\n            set\n            {\n                if (setStartWithWin != value)\n                {\n                    setStartWithWin = value;\n                    OnPropertyChanged(\"SetStartWithWin\");\n\n                    //set the app settings\n                    SettingsManager.Current.StartWithWin = value;\n                    SettingsManager.Save();\n\n                    UnlockOptionStartWin = false;\n                    startupRegistrationService.SetEnabled(value, MinimizeOnStart);\n                    UnlockOptionStartWin = true;\n\n                    if (value)\n                        UnlockMinimizeOnStart = false;\n                    else\n                        UnlockMinimizeOnStart = true;\n                }\n            }\n        }\n\n        private bool unlockOptionStartWin;\n        public bool UnlockOptionStartWin\n        {\n            get { return unlockOptionStartWin; }\n\n            set\n            {\n                if (unlockOptionStartWin != value)\n                {\n                    unlockOptionStartWin = value;\n                    OnPropertyChanged(\"UnlockOptionStartWin\");\n                }\n            }\n        }\n\n        private bool minimizeOnStart;\n        public bool MinimizeOnStart \n        {\n            get { return minimizeOnStart; }\n            set\n            {\n                if (minimizeOnStart != value)\n                {\n                    minimizeOnStart = value;\n                    SettingsManager.Current.MinimizeOnStart = value;\n                    SettingsManager.Save();\n                }\n            }\n        }\n\n        private bool unlockMinimizeOnStart;\n        public bool UnlockMinimizeOnStart \n        {\n            get { return unlockMinimizeOnStart; }\n\n            set\n            {\n                if (unlockMinimizeOnStart != value)\n                {\n                    unlockMinimizeOnStart = value;\n                    OnPropertyChanged(\"UnlockMinimizeOnStart\");\n                }\n            }\n        }\n\n        //0 == private, 1 == public, 2 == both\n        private int networkTrafficType;\n        public int NetworkTrafficType\n        {\n            get { return networkTrafficType; }\n\n            set\n            {\n                if (networkTrafficType != value)\n                {\n                    networkTrafficType = value;\n                    OnPropertyChanged(\"NetworkTrafficType\");\n\n                    //set the app settings\n                    SettingsManager.Current.NetworkType = value;\n                    SettingsManager.Save();\n                }\n            }\n        }\n\n        private int networkSpeedFormat;\n        public int NetworkSpeedFormat\n        {\n            get { return networkSpeedFormat; }\n            set\n            {\n                if(networkSpeedFormat != value)\n                {\n                    networkSpeedFormat = value;\n                    SettingsManager.Current.NetworkSpeedFormat = value;\n                    SettingsManager.Save();\n                    OnPropertyChanged(\"NetworkSpeedFormat\");\n                }\n            }\n        }\n\n        private int networkSpeedMagnitude;\n        public int NetworkSpeedMagnitude\n        {\n            get { return networkSpeedMagnitude; }\n            set\n            {\n                if (networkSpeedMagnitude != value)\n                {\n                    networkSpeedMagnitude = value;\n                    SettingsManager.Current.NetworkSpeedMagnitude = value;\n                    SettingsManager.Save();\n                    OnPropertyChanged(\"NetworkSpeedMagnitude\");\n                }\n            }\n        }\n\n        private bool darkMode;\n        public bool DarkMode\n        {\n            get { return darkMode; }\n\n            set\n            {\n                darkMode = value;\n                OnPropertyChanged(\"DarkMode\");\n\n                //trigger the miniwidget's BackgroundColor property.\n                SetMiniWidgetBackgroundColor(value, MiniWidgetTransparentSlider);\n\n                //set the app settings\n                SettingsManager.Current.DarkMode = value;\n                SettingsManager.Save();\n            }\n        }\n\n        private int miniWidgetTransparentSlider;\n        public int MiniWidgetTransparentSlider\n        {\n            get { return miniWidgetTransparentSlider; }\n            set\n            {\n                miniWidgetTransparentSlider = value;\n                \n                OnPropertyChanged(\"MiniWidgetTransparentSlider\");\n                //Debug.WriteLine($\"MiniWidgetTransparentSlider: {value}\");\n                \n                //trigger the miniwidget's BackgroundColor property.\n                SetMiniWidgetBackgroundColor(DarkMode, value);\n\n                SettingsManager.Current.MiniWidgetTransparentSlider = value;\n                SettingsManager.Save();\n            }\n        }\n\n        public bool deleteAllFiles;\n        public bool DeleteAllFiles\n        {\n            get { return deleteAllFiles; }\n            set\n            {\n                if (deleteAllFiles != value)\n                {\n                    deleteAllFiles = value;\n                    if(value)\n                        OnPropertyChanged(\"DeleteAllFiles\");\n                }\n            }\n        }\n        public ICommand ResetBtn { get; set; }\n        public ICommand UpdateCheckBtn { get; set; }\n        public ICommand DownloadUpdateBtn { get; set; }\n\n        private bool miniWidgetVisibility;\n        public bool MiniWidgetVisibility\n        {\n            get { return miniWidgetVisibility; }\n            set\n            {\n                if (miniWidgetVisibility != value)\n                {\n                    miniWidgetVisibility = value;\n                    OnPropertyChanged(\"MiniWidgetVisibility\");\n                    RequestSetMiniWidgetVisibility?.Invoke(value);\n                }\n            }\n        }\n\n        public event Action<bool>? RequestSetMiniWidgetVisibility;\n\n        public void SyncMiniWidgetVisibility(bool isVisible)\n        {\n            if (miniWidgetVisibility == isVisible)\n                return;\n\n            miniWidgetVisibility = isVisible;\n            OnPropertyChanged(\"MiniWidgetVisibility\");\n        }\n\n        private bool _isUpdateAvailable;\n        public bool IsUpdateAvailable\n        {\n            get { return _isUpdateAvailable; }\n            set\n            {\n                _isUpdateAvailable = value;\n                OnPropertyChanged(\"IsUpdateAvailable\");\n            }\n        }\n\n        private string _updateStatusMessage = string.Empty;\n        public string UpdateStatusMessage\n        {\n            get { return _updateStatusMessage; }\n            set\n            {\n                _updateStatusMessage = value;\n                OnPropertyChanged(\"UpdateStatusMessage\");\n            }\n        }\n\n        private bool _isCheckingForUpdates;\n        public bool IsCheckingForUpdates\n        {\n            get { return _isCheckingForUpdates; }\n            set\n            {\n                _isCheckingForUpdates = value;\n                OnPropertyChanged(\"IsCheckingForUpdates\");\n            }\n        }\n\n        private string DownloadUrl;\n        private readonly IStartupRegistrationService startupRegistrationService;\n\n        private ConfirmationDialogVM? cdvm;\n        private MiniWidgetVM? mwvm;\n\n        public SettingsVM(MiniWidgetVM mw_ref, ConfirmationDialogVM cdvm_ref)\n            : this(mw_ref, cdvm_ref, new WindowsStartupRegistrationService())\n        {\n        }\n\n        public SettingsVM(MiniWidgetVM mw_ref, ConfirmationDialogVM cdvm_ref, IStartupRegistrationService startupRegistrationService)\n        {\n            this.startupRegistrationService = startupRegistrationService;\n            mwvm = mw_ref;\n            cdvm = cdvm_ref;\n            cdvm.BtnCommand = new BaseCommand(ResetDataYesOrNo, true);\n            cdvm.DialogMessage = \"Warning!!! This will delete all saved profiles.\\nDo you still want to continue?\";\n\n            //start with windows setting\n            UnlockOptionStartWin = true;\n            SetStartWithWin = SettingsManager.Current.StartWithWin;\n            MinimizeOnStart = SettingsManager.Current.MinimizeOnStart;\n            DarkMode = SettingsManager.Current.DarkMode;\n            MiniWidgetTransparentSlider = SettingsManager.Current.MiniWidgetTransparentSlider;\n            MiniWidgetVisibility = SettingsManager.Current.MiniWidgetVisibility;\n\n            if (SetStartWithWin)\n                UnlockMinimizeOnStart = false;\n            else\n                UnlockMinimizeOnStart = true;\n\n            NetworkTrafficType = SettingsManager.Current.NetworkType;\n\n            NetworkSpeedFormat = SettingsManager.Current.NetworkSpeedFormat;\n            NetworkSpeedMagnitude = SettingsManager.Current.NetworkSpeedMagnitude;\n\n            ResetBtn = new BaseCommand(ResetData, true);\n            UpdateCheckBtn = new BaseCommand(UpdateCheck, true);\n            DownloadUpdateBtn = new BaseCommand(DownloadUpdate, true);\n            DownloadUrl = string.Empty;\n            IsUpdateAvailable = false;\n            UpdateStatusMessage = \"Click here to check for new updates\";\n            IsCheckingForUpdates = false;\n            DeleteAllFiles = false;\n        }\n\n        private void SetMiniWidgetBackgroundColor(bool darkMode, int transparency)\n        {\n            // 00XXXXXX -> 0%\n            // FFXXXXXX -> 100%\n            // 00 -> 0                  -> 0%,      fully transparent\n            // FF -> (2^8)-1 (256-1)    -> 100%,    fully opaque\n            // example, 77XXXXXX -> (64+32+16+4+2+1) = 119\n            //          (119/255) * 100% = 46.67% opaqueness\n\n            mwvm!.BackgroundColor = darkMode ? \"#\" + (((100 - transparency) * 255) / 100).ToString(\"x2\") + \"252525\" : \"#\" + (((100 - transparency) * 255) / 100).ToString(\"x2\") + \"f1f1f1\"; ;\n        }\n\n        private void ResetData(object? obj)\n        {\n            if(cdvm != null)\n                cdvm.IsVisible = UiVisibility.Visible;\n        }\n\n        private async void UpdateCheck(object? obj)\n        {\n            IsCheckingForUpdates = true;\n            UpdateStatusMessage = \"Checking for updates...\";\n            string tempMsgStatus = string.Empty; \n            IsUpdateAvailable = false;\n\n            const int minDisplayTimeMs = 2000; // 2 seconds, this is to show a progress bar for the update check, for better ux\n            var stopwatch = Stopwatch.StartNew();\n\n            try\n            {\n                (Version? latestVersion, string? downloadUrl) = await UpdateChecker.CheckForUpdates();\n                if (latestVersion != null && downloadUrl != null)\n                {\n                    Version? currentVersion = Assembly.GetExecutingAssembly()?.GetName()?.Version;\n                    Debug.WriteLine($\"download url: {downloadUrl}, current version: {currentVersion}, latest version: {latestVersion}\");\n                    if (currentVersion != null && latestVersion > currentVersion)\n                    {\n                        DownloadUrl = downloadUrl;\n                        tempMsgStatus = $\"A new version {latestVersion} is available!\";\n                        IsUpdateAvailable = true;\n                    }\n                    else\n                    {\n                        tempMsgStatus = \"You have the latest version.\";\n                    }\n                }\n                else\n                {\n                    tempMsgStatus = \"Error checking for updates.\";\n                }\n            }\n            catch (Exception ex)\n            {\n                tempMsgStatus = \"Error checking for updates.\";\n                EventLogger.Error(\"Error checking for updates\", ex);\n            }\n            finally\n            {\n                stopwatch.Stop();\n\n                int elapsedMs = (int)stopwatch.ElapsedMilliseconds;\n                int remainingTime = minDisplayTimeMs - elapsedMs;\n                if (remainingTime > 0)\n                {\n                    await Task.Delay(remainingTime);\n                }\n\n                IsCheckingForUpdates = false;\n                UpdateStatusMessage = tempMsgStatus;\n            }\n        }\n\n        private void DownloadUpdate(object? obj)\n        {\n            try\n            {\n                var psi = new ProcessStartInfo\n                {\n                    FileName = DownloadUrl,\n                    UseShellExecute = true\n                };\n                Process.Start(psi);\n            }\n            catch (Exception ex)\n            {\n                EventLogger.Error(\"Error launching update download URL\", ex);\n            }\n        }\n\n        private void ResetDataYesOrNo(object? obj)\n        {\n            if(obj != null)\n            {\n                if ((string)obj == \"Yes\")\n                    DeleteAllFiles = true;\n                if (cdvm != null)\n                    cdvm.IsVisible = UiVisibility.Hidden;\n            }\n        }\n\n        //------property changers---------------//\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n\n        private void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/AboutWindow.xaml",
    "content": "﻿<Window x:Class=\"OpenNetMeter.Views.AboutWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:vm=\"clr-namespace:OpenNetMeter.ViewModels\"\n        xmlns:res=\"clr-namespace:OpenNetMeter.Properties\"\n        xmlns:v=\"clr-namespace:OpenNetMeter.Views\"\n        mc:Ignorable=\"d\"\n        WindowStyle=\"None\"\n        ResizeMode=\"NoResize\"\n        UseLayoutRounding=\"True\"\n        Title=\"About OpenNetMeter\"\n        SizeToContent=\"WidthAndHeight\"\n        WindowStartupLocation=\"Manual\" Loaded=\"Window_Loaded\"\n        IsVisibleChanged=\"Window_IsVisibleChanged\"\n        ShowInTaskbar=\"False\">\n\n    <WindowChrome.WindowChrome>\n        <WindowChrome CaptionHeight=\"0\" GlassFrameThickness=\"1\" CornerRadius=\"0\" />\n    </WindowChrome.WindowChrome>\n\n    <Window.Resources>\n        <v:BitmapToImageConverter x:Key=\"BitmapToImage\"/>\n    </Window.Resources>\n    <Border Style=\"{StaticResource BorderColor}\">\n        <Grid>\n            <Grid.RowDefinitions>\n                <RowDefinition Height=\"0.5*\"/>\n                <RowDefinition />\n            </Grid.RowDefinitions>\n            <Grid Grid.Row=\"0\" IsHitTestVisible=\"True\" MouseLeftButtonDown=\"Grid_MouseDown\" Style=\"{StaticResource BackgroundColor1}\">\n                <StackPanel Orientation=\"Horizontal\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Center\" Margin=\"5,8,0,8\" >\n                    <Image Source=\"{Binding Source={x:Static res:Resources.x64}, Converter={StaticResource BitmapToImage}}\" Height=\"64\" Width=\"64\" Margin=\"10,0\"/>\n                    <TextBlock Text=\"OpenNetMeter\" FontFamily=\"Segoe UI\" FontWeight=\"Bold\" \n                    FontSize=\"30\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\" Style=\"{StaticResource FontColor3}\"/>\n                </StackPanel>\n            </Grid>\n            <Button Width=\"50\" Height=\"32\" VerticalAlignment=\"Top\" HorizontalAlignment=\"Right\" Click=\"Exit_Button_Click\" >\n                <Button.Style>\n                    <Style TargetType=\"{x:Type Button}\">\n                        <Setter Property=\"Template\">\n                            <Setter.Value>\n                                <ControlTemplate TargetType=\"Button\">\n                                    <Border>\n                                        <Border.Style>\n                                            <Style TargetType=\"{x:Type Border}\">\n                                                <Style.Triggers>\n                                                    <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                                        <Setter Property=\"Background\" Value=\"{StaticResource BrushDangerStrong}\"/>\n                                                    </Trigger>\n                                                </Style.Triggers>\n                                            </Style>\n                                        </Border.Style>\n                                        <Grid Background=\"Transparent\">\n                                            <ContentPresenter></ContentPresenter>\n                                        </Grid>\n                                    </Border>\n                                </ControlTemplate>\n                            </Setter.Value>\n                        </Setter>\n                    </Style>\n                </Button.Style>\n                <Button.Content >\n                    <Grid Margin=\"20,11\">\n                        <Line X1=\"0\" Y1=\"0\" Y2=\"1\" X2=\"1\" Style=\"{StaticResource LineColor}\" StrokeThickness=\"1\" Stretch=\"Fill\" />\n                        <Line Y1=\"1\" X2=\"1\" Style=\"{StaticResource LineColor}\" StrokeThickness=\"1\" Stretch=\"Fill\" />\n                    </Grid>\n                </Button.Content>\n            </Button>\n            <Grid Grid.Row=\"1\" Style=\"{StaticResource BackgroundColor2}\">\n\n                <Border Margin=\"8\" Style=\"{StaticResource BorderStyle1}\">\n                    <StackPanel Margin=\"8\">\n                        <TextBlock Text=\"{StaticResource AppVersion}\" Style=\"{StaticResource FontColor1}\"/>\n                        <TextBlock  Style=\"{StaticResource FontColor1}\">\n                            <LineBreak/>\n                            This product is created and maintained by Ashfaaq Riphque.\n                            <LineBreak/>\n                            <LineBreak/>\n                            If you find any problems with the product or any \n                            <LineBreak/>\n                             ideas for improvements, feel free create an issue at,\n                            <LineBreak/>\n                            <Hyperlink NavigateUri=\"https://github.com/Ashfaaq18/OpenNetMeter\" RequestNavigate=\"Hyperlink_RequestNavigate\" Style=\"{StaticResource FontColor4}\">\n                                https://github.com/Ashfaaq18/OpenNetMeter\n                            </Hyperlink>\n                        </TextBlock>\n                    </StackPanel>\n                </Border>\n                \n            </Grid>\n        </Grid>\n    </Border>\n</Window>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/AboutWindow.xaml.cs",
    "content": "﻿using System.Diagnostics;\nusing System.Reflection;\nusing System.Windows;\nusing System.Windows.Input;\nusing System.Windows.Navigation;\n\nnamespace OpenNetMeter.Views\n{\n    /// <summary>\n    /// Interaction logic for AboutWindow.xaml\n    /// </summary>\n    public partial class AboutWindow : Window\n    {\n        private Rect parentWindowRect;\n        public AboutWindow(Rect parentWindowRect_param)\n        {\n            this.Resources.Add(\"AppVersion\", \"Version: \" + Assembly.GetExecutingAssembly()?.GetName().Version);\n            InitializeComponent();\n            parentWindowRect = parentWindowRect_param;\n        }\n        private void Grid_MouseDown(object sender, MouseButtonEventArgs e)\n        {\n            this.DragMove();\n        }\n\n        public void CloseWin()\n        {\n            Close();\n        }\n        private void Exit_Button_Click(object sender, RoutedEventArgs e)\n        {\n            this.Visibility = Visibility.Collapsed;\n        }\n\n        private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)\n        {\n            ProcessStartInfo psi = new ProcessStartInfo(e.Uri.AbsoluteUri);\n            psi.UseShellExecute = true;\n            Process.Start(psi);\n            e.Handled = true;\n        }\n\n        public void SetParentWindowRect(Rect parentWindowRect_param)\n        {\n            parentWindowRect = parentWindowRect_param;\n        }\n\n        private void Window_Loaded(object sender, RoutedEventArgs e)\n        {\n            this.Left = parentWindowRect.Left + (parentWindowRect.Width / 2) - this.ActualWidth / 2;\n            this.Top = parentWindowRect.Top + (parentWindowRect.Height / 2) - this.ActualHeight / 2;\n        }\n\n        private void Window_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)\n        {\n            if (this.Visibility == Visibility.Visible)\n            {\n                this.Left = parentWindowRect.Left + (parentWindowRect.Width / 2) - this.ActualWidth / 2;\n                this.Top = parentWindowRect.Top + (parentWindowRect.Height / 2) - this.ActualHeight / 2;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ConfirmationDialog.xaml",
    "content": "﻿<Window x:Class=\"OpenNetMeter.Views.ConfirmationDialog\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:OpenNetMeter.Views\"\n        mc:Ignorable=\"d\"\n        WindowStyle=\"None\"\n        ResizeMode=\"NoResize\"\n        SizeToContent=\"WidthAndHeight\"\n        Title=\"Confirm\" \n        Visibility=\"{Binding IsVisible, Mode=TwoWay, Converter={StaticResource UiVisibilityToWpfVisibilityConverter}}\"\n        IsVisibleChanged=\"Window_IsVisibleChanged\"\n        WindowStartupLocation=\"Manual\" Loaded=\"Window_Loaded\"\n        ShowInTaskbar=\"False\">\n\n    <Window.Resources>\n        <local:UiVisibilityToWpfVisibilityConverter x:Key=\"UiVisibilityToWpfVisibilityConverter\"/>\n    </Window.Resources>\n\n    <WindowChrome.WindowChrome>\n        <WindowChrome CaptionHeight=\"0\" GlassFrameThickness=\"1\" CornerRadius=\"0\" />\n    </WindowChrome.WindowChrome>\n\n    <Border Style=\"{StaticResource BorderColor}\">\n        <Grid Style=\"{StaticResource BackgroundColor2}\">\n            <Grid.RowDefinitions>\n                <RowDefinition Height=\"Auto\" />\n                <RowDefinition Height=\"Auto\" />\n            </Grid.RowDefinitions>\n            <Grid Grid.Row=\"0\" IsHitTestVisible=\"True\" MouseLeftButtonDown=\"Grid_MouseDown\" Style=\"{StaticResource BackgroundColor1}\">\n                <Button Width=\"50\" Height=\"32\" VerticalAlignment=\"Top\" HorizontalAlignment=\"Right\" Click=\"Exit_Button_Click\" >\n                    <Button.Style>\n                        <Style TargetType=\"{x:Type Button}\">\n                            <Setter Property=\"Template\">\n                                <Setter.Value>\n                                    <ControlTemplate TargetType=\"Button\">\n                                        <Border>\n                                            <Border.Style>\n                                                <Style TargetType=\"{x:Type Border}\">\n                                                    <Style.Triggers>\n                                                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                                            <Setter Property=\"Background\" Value=\"{StaticResource BrushDangerStrong}\"/>\n                                                        </Trigger>\n                                                    </Style.Triggers>\n                                                </Style>\n                                            </Border.Style>\n                                            <Grid Background=\"Transparent\">\n                                                <ContentPresenter></ContentPresenter>\n                                            </Grid>\n                                        </Border>\n                                    </ControlTemplate>\n                                </Setter.Value>\n                            </Setter>\n                        </Style>\n                    </Button.Style>\n                    <Button.Content >\n                        <Grid Margin=\"20,11\">\n                            <Line X1=\"0\" Y1=\"0\" Y2=\"1\" X2=\"1\" Style=\"{StaticResource LineColor}\" StrokeThickness=\"1\" Stretch=\"Fill\" />\n                            <Line Y1=\"1\" X2=\"1\" Style=\"{StaticResource LineColor}\" StrokeThickness=\"1\" Stretch=\"Fill\" />\n                        </Grid>\n                    </Button.Content>\n                </Button>\n\n                <TextBlock Text=\"Confirm\" FontFamily=\"Segoe UI\"\n                    VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Margin=\"15,0\" Style=\"{StaticResource FontColor1}\"/>\n            </Grid>\n\n            <Grid Margin=\"20,10,20,20\" Grid.Row=\"1\">\n                <Grid.RowDefinitions>\n                    <RowDefinition Height=\"Auto\" />\n                    <RowDefinition Height=\"Auto\" />\n                </Grid.RowDefinitions>\n                <TextBlock Grid.Row=\"0\" Name=\"confirmation\" Style=\"{StaticResource FontColor1}\" Text=\"{Binding DialogMessage}\"></TextBlock>\n\n                <WrapPanel Grid.Row=\"1\" HorizontalAlignment=\"Center\" Margin=\"0,20,0,0\">\n                    <Button MinWidth=\"60\" MinHeight=\"24\" Margin=\"0,0,20,0\" Command=\"{Binding BtnCommand, Mode=OneWay}\" CommandParameter=\"Yes\" Style=\"{StaticResource ButtonColor}\">\n                        <TextBlock FontFamily=\"Segoe UI\" Text=\"Yes\" VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\"/>\n                    </Button>\n                    <Button MinWidth=\"60\" MinHeight=\"24\" Command=\"{Binding BtnCommand, Mode=OneWay}\" CommandParameter=\"No\" Style=\"{StaticResource ButtonColor}\">\n                        <TextBlock FontFamily=\"Segoe UI\" Text=\"No\" VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\"/>\n                    </Button>\n                </WrapPanel>\n            </Grid>\n\n        </Grid>\n    </Border>\n</Window>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ConfirmationDialog.xaml.cs",
    "content": "﻿using OpenNetMeter.ViewModels;\nusing System.Windows;\nusing System.Windows.Input;\n\nnamespace OpenNetMeter.Views\n{\n    /// <summary>\n    /// Interaction logic for ConfirmationDialog.xaml\n    /// </summary>\n    public partial class ConfirmationDialog : Window\n    {\n        private Rect parentWindowRect;\n        public ConfirmationDialog(Rect parentWindowRect_param)\n        {\n            InitializeComponent();\n            DataContext = new ConfirmationDialogVM();\n            parentWindowRect = parentWindowRect_param;\n        }\n        \n        private void Grid_MouseDown(object sender, MouseButtonEventArgs e)\n        {\n            this.DragMove();\n        }\n\n        public void SetParentWindowRect(Rect parentWindowRect_param)\n        {\n            parentWindowRect = parentWindowRect_param;\n        }\n\n        private void Exit_Button_Click(object sender, RoutedEventArgs e)\n        {\n            this.Visibility = Visibility.Hidden;\n        }\n\n        private void Window_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)\n        {\n            if(this.Visibility == Visibility.Visible)\n            {\n                this.Left = parentWindowRect.Left + (parentWindowRect.Width / 2) - this.ActualWidth / 2;\n                this.Top = parentWindowRect.Top + (parentWindowRect.Height / 2) - this.ActualHeight / 2;\n            }\n        }\n\n        private void Window_Loaded(object sender, RoutedEventArgs e)\n        {\n            this.Left = parentWindowRect.Left + (parentWindowRect.Width / 2) - this.ActualWidth / 2;\n            this.Top = parentWindowRect.Top + (parentWindowRect.Height / 2) - this.ActualHeight / 2;\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/Converters/BitmapToImageConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.IO;\nusing System.Windows.Data;\nusing System.Windows.Media.Imaging;\n\nnamespace OpenNetMeter.Views\n{\n    class BitmapToImageConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            MemoryStream ms = new MemoryStream();\n            ((System.Drawing.Bitmap)value).Save(ms, System.Drawing.Imaging.ImageFormat.Png);\n            BitmapImage image = new BitmapImage();\n            image.BeginInit();\n            ms.Seek(0, SeekOrigin.Begin);\n            image.StreamSource = ms;\n            image.EndInit();\n\n            return image;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/Converters/NetSpeedFormatConverter.cs",
    "content": "﻿using OpenNetMeter.Properties;\nusing OpenNetMeter.Utilities;\nusing System;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace OpenNetMeter.Views\n{\n    public class NetSpeedFormatConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            bool useBytes = SettingsManager.Current.NetworkSpeedFormat != 0;\n            SpeedMagnitude magnitude = DataSizeSuffix.NormalizeMagnitude(SettingsManager.Current.NetworkSpeedMagnitude);\n\n            return DataSizeSuffix.InStr((long)value, 1, useBytes, magnitude);\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n     \n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/Converters/RadioBoolToIntConverter.cs",
    "content": "﻿using System;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace OpenNetMeter.Views\n{\n    public class RadioBoolToIntConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            int integer = (int)value;\n            if (parameter != null)\n            {\n                if (integer == int.Parse(parameter.ToString()!))\n                    return true;\n                else\n                    return false;\n            }\n            else\n                return false;\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            return parameter;\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/Converters/UiVisibilityToWpfVisibilityConverter.cs",
    "content": "using System;\nusing System.Globalization;\nusing System.Windows;\nusing System.Windows.Data;\nusing OpenNetMeter.PlatformAbstractions;\n\nnamespace OpenNetMeter.Views\n{\n    public class UiVisibilityToWpfVisibilityConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value is not UiVisibility uiVisibility)\n                return Visibility.Hidden;\n\n            return uiVisibility switch\n            {\n                UiVisibility.Visible => Visibility.Visible,\n                UiVisibility.Collapsed => Visibility.Collapsed,\n                _ => Visibility.Hidden,\n            };\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            if (value is not Visibility visibility)\n                return UiVisibility.Hidden;\n\n            return visibility switch\n            {\n                Visibility.Visible => UiVisibility.Visible,\n                Visibility.Collapsed => UiVisibility.Collapsed,\n                _ => UiVisibility.Hidden,\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/Converters/UnitConverterBytes.cs",
    "content": "﻿using OpenNetMeter.Utilities;\nusing System;\nusing System.Globalization;\nusing System.Windows.Data;\n\nnamespace OpenNetMeter.Views\n{\n    public class UnitConverterBytes : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            return DataSizeSuffix.InStr((long)value, 1, true);\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n     \n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/CustomSystemTray.cs",
    "content": "﻿using System.Drawing;\nusing System.Windows.Forms;\nusing OpenNetMeter.Properties;\n\nnamespace OpenNetMeter.Views\n{\n    public class MyColorTable : ProfessionalColorTable\n    {\n        public override Color MenuItemBorder\n        {\n            get\n            {\n                if (SettingsManager.Current.DarkMode)\n                    return Color.FromArgb(32, 32, 32);\n                else\n                    return Color.FromArgb(240, 240, 240);\n            }\n        }\n        public override Color ButtonSelectedHighlight\n        {\n            get\n            {\n                if (SettingsManager.Current.DarkMode)\n                    return Color.FromArgb(64, 64, 64);\n                else\n                    return Color.FromArgb(220, 220, 220);\n            }\n        }\n\n        public override Color ToolStripDropDownBackground\n        {\n            get\n            {\n                if (SettingsManager.Current.DarkMode)\n                    return Color.FromArgb(32, 32, 32);\n                else\n                    return Color.FromArgb(240, 240, 240);\n            }\n        }\n        public override Color ImageMarginGradientBegin\n        {\n            get\n            {\n                if (SettingsManager.Current.DarkMode)\n                    return Color.FromArgb(32, 32, 32);\n                else\n                    return Color.FromArgb(240, 240, 240);\n            }\n        }\n        public override Color ImageMarginGradientMiddle\n        {\n            get\n            {\n                if (SettingsManager.Current.DarkMode)\n                    return Color.FromArgb(32, 32, 32);\n                else\n                    return Color.FromArgb(240, 240, 240);\n            }\n        }\n        public override Color ImageMarginGradientEnd\n        {\n            get\n            {\n                if (SettingsManager.Current.DarkMode)\n                    return Color.FromArgb(32, 32, 32);\n                else\n                    return Color.FromArgb(240, 240, 240);\n            }\n        }\n\n    }\n    public class CustomSystemTray : ToolStripProfessionalRenderer\n    {\n        public CustomSystemTray() : base(new MyColorTable()) { }\n\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"OpenNetMeter.Views.MainWindow\"\n        x:Name=\"MyWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:v=\"clr-namespace:OpenNetMeter.Views\" \n        xmlns:vm=\"clr-namespace:OpenNetMeter.ViewModels\"\n        xmlns:res=\"clr-namespace:OpenNetMeter.Properties\"\n        mc:Ignorable=\"d\"\n        ResizeMode=\"CanResizeWithGrip\"\n        WindowStyle=\"None\"\n        UseLayoutRounding=\"True\"\n        MinHeight=\"600\" Height=\"600\" MinWidth=\"800\" Width=\"800\"\n        SizeChanged=\"MyWindow_SizeChanged\"\n        Window.LocationChanged =\"MyWindow_LocationChanged\">\n    \n    <WindowChrome.WindowChrome>\n        <WindowChrome CaptionHeight=\"0\" ResizeBorderThickness=\"5\" GlassFrameThickness=\"1\" CornerRadius=\"0\"/>\n    </WindowChrome.WindowChrome>\n    \n    <Window.Resources>\n        <v:BitmapToImageConverter x:Key=\"BitmapToImage\"/>\n        <v:NetSpeedFormatConverter x:Key=\"netSpeedFormatConverter\"/>\n        <DataTemplate DataType=\"{x:Type vm:DataUsageSummaryVM}\">\n\n            <v:DataUsageSummaryV/>\n\n        </DataTemplate>\n\n        <DataTemplate DataType=\"{x:Type vm:DataUsageHistoryVM}\">\n\n            <v:DataUsageHistoryV/>\n\n        </DataTemplate>\n\n        <DataTemplate DataType=\"{x:Type vm:SettingsVM}\">\n\n            <v:SettingsV/>\n\n        </DataTemplate>\n\n    </Window.Resources>\n    <Border Style=\"{StaticResource BorderColor}\">\n        <Grid Style=\"{StaticResource BackgroundColor2}\">\n            <Grid.RowDefinitions>\n                <RowDefinition Height=\"50\"/>\n                <RowDefinition Height=\"*\"/>\n                <RowDefinition Height=\"30\"/>\n            </Grid.RowDefinitions>\n\n            <Grid IsHitTestVisible=\"True\" MouseLeftButtonDown=\"Grid_MouseDown\" Style=\"{StaticResource BackgroundColor1}\">\n                <Grid.ColumnDefinitions>\n                    <ColumnDefinition Width=\"60\"/>\n                    <ColumnDefinition Width=\"430\"/>\n                </Grid.ColumnDefinitions>\n                <Grid.RowDefinitions>\n                    <RowDefinition Height=\"50\"/>\n                </Grid.RowDefinitions>\n\n                <StackPanel Orientation=\"Horizontal\" HorizontalAlignment=\"Left\" VerticalAlignment=\"Center\">\n                    <Image Source=\"{Binding Source={x:Static res:Resources.x48}, Converter={StaticResource BitmapToImage}}\" Height=\"35\" Width=\"35\" Margin=\"10,0\"/>\n                </StackPanel>\n\n                <Grid  Grid.Column=\"1\">\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"80\"/>\n                        <ColumnDefinition Width=\"80\"/>\n                        <ColumnDefinition Width=\"80\"/>\n                        <ColumnDefinition Width=\"*\"/>\n                    </Grid.ColumnDefinitions>\n                    <Button Height=\"35\" BorderThickness=\"0,0,0,5\" Margin=\"0,0,5,0\" VerticalAlignment=\"Bottom\" Style=\"{StaticResource MainWindowSummaryTab}\" Command=\"{Binding SwitchTabCommand}\" CommandParameter=\"summary\">\n                        <StackPanel Orientation=\"Horizontal\">\n                            <TextBlock FontFamily=\"Segoe UI\" Text=\"Summary\" VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\"/>\n                        </StackPanel>\n                    </Button>\n\n                    <Button Grid.Column=\"1\" BorderThickness=\"0,0,0,5\" Margin=\"0,0,5,0\" Height=\"35\" VerticalAlignment=\"Bottom\" Style=\"{StaticResource MainWindowHistoryTab}\" Command=\"{Binding SwitchTabCommand}\" CommandParameter=\"history\">\n                        <StackPanel Orientation=\"Horizontal\">\n                            <TextBlock FontFamily=\"Segoe UI\" Text=\"History\" VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\"/>\n                        </StackPanel>\n                    </Button>\n\n                    <Button Grid.Column=\"2\" Margin=\"0,0,5,0\" Height=\"35\" VerticalAlignment=\"Bottom\" Style=\"{StaticResource MainWindowSettingsTab}\" Command=\"{Binding SwitchTabCommand}\" CommandParameter=\"settings\">\n                        <StackPanel Orientation=\"Horizontal\">\n                            <TextBlock FontFamily=\"Segoe UI\" Text=\"Settings\" VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\"/>\n                        </StackPanel>\n                    </Button>\n\n                </Grid>\n            </Grid>\n\n            <StackPanel Orientation=\"Horizontal\" HorizontalAlignment=\"Right\" VerticalAlignment=\"Top\" Height=\"32\" Width=\"150\">\n\n                <Button BorderThickness=\"0\" Width=\"50\" Height=\"20\" HorizontalAlignment=\"Right\" VerticalAlignment=\"Top\" Click=\"About_Button_Click\" Style=\"{StaticResource ButtonColor}\">\n                    <TextBlock FontFamily=\"Segoe UI\" Text=\"About\" VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\"/>\n                </Button>\n\n                <Button Width=\"50\"  \n                    HorizontalAlignment=\"Right\" Click=\"Minimize_Button_Click\" BorderThickness=\"0\" Style=\"{StaticResource ButtonColor}\">\n                    <Button.Content >\n                        <Grid Margin=\"18,0\">\n                            <Line X1=\"0\" Y1=\"0.5\" Y2=\"0.5\" X2=\"0.5\" Style=\"{StaticResource LineColor}\" StrokeThickness=\"1\" Stretch=\"Fill\" />\n                        </Grid>\n                    </Button.Content>\n                </Button>\n                <Button Width=\"50\"  HorizontalAlignment=\"Right\" Click=\"Exit_Button_Click\" >\n                    <Button.Style>\n                        <Style TargetType=\"{x:Type Button}\">\n                            <Setter Property=\"Template\">\n                                <Setter.Value>\n                                    <ControlTemplate TargetType=\"Button\">\n                                        <Border>\n                                            <Border.Style>\n                                                <Style TargetType=\"{x:Type Border}\">\n                                                    <Style.Triggers>\n                                                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                                            <Setter Property=\"Background\" Value=\"{StaticResource BrushDangerStrong}\"/>\n                                                        </Trigger>\n                                                    </Style.Triggers>\n                                                </Style>\n                                            </Border.Style>\n                                            <Grid Background=\"Transparent\">\n                                                <ContentPresenter></ContentPresenter>\n                                            </Grid>\n                                        </Border>\n                                    </ControlTemplate>\n                                </Setter.Value>\n                            </Setter>\n                        </Style>\n                    </Button.Style>\n                    <Button.Content >\n                        <Grid Margin=\"20,11\">\n                            <Line X1=\"0\" Y1=\"0\" Y2=\"1\" X2=\"1\" Style=\"{StaticResource LineColor}\" StrokeThickness=\"1\" Stretch=\"Fill\" />\n                            <Line Y1=\"1\" X2=\"1\" Style=\"{StaticResource LineColor}\" StrokeThickness=\"1\" Stretch=\"Fill\" />\n                        </Grid>\n                    </Button.Content>\n                </Button>\n            </StackPanel>\n\n            <ContentControl x:Name=\"Pages\" Grid.Row=\"1\" Content=\"{Binding SelectedViewModel}\"/>\n\n            <Grid Grid.Row=\"2\" DockPanel.Dock=\"Bottom\"  Style=\"{StaticResource BackgroundColor1}\">\n                <Grid.ColumnDefinitions>\n                    <ColumnDefinition />\n                    <ColumnDefinition Width=\"104\"/>\n                    <ColumnDefinition Width=\"84\"/>\n                    <ColumnDefinition Width=\"84\"/>\n                    <ColumnDefinition Width=\"84\"/>\n                </Grid.ColumnDefinitions>\n\n                <TextBlock Grid.Column=\"0\" Text=\"{Binding NetworkStatus}\" FontSize=\"12\" FontFamily=\"Segoe UI\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Margin=\"10,0,0,0\" Style=\"{StaticResource FontColor1}\"></TextBlock>\n\n                <TextBlock Grid.Column=\"1\" Text=\"Download Speed : \" FontSize=\"12\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Right\" Style=\"{StaticResource FontColor1}\" />\n                <WrapPanel Grid.Column=\"2\" VerticalAlignment=\"Center\" >\n                    <TextBlock FontSize=\"12\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Padding=\"0\" Text=\"{Binding DownloadSpeed, Converter={StaticResource netSpeedFormatConverter}}\" Style=\"{StaticResource FontColor1}\"/>\n                    <TextBlock FontSize=\"12\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Padding=\"0\" Text=\"ps\" Style=\"{StaticResource FontColor1}\"/>\n                </WrapPanel>\n\n                <TextBlock Grid.Column=\"3\" Text=\"Upload Speed : \" FontSize=\"12\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Right\" Style=\"{StaticResource FontColor1}\"/>\n                <WrapPanel Grid.Column=\"4\" VerticalAlignment=\"Center\">\n                    <TextBlock FontSize=\"12\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Padding=\"0\" Text=\"{Binding UploadSpeed, Converter={StaticResource netSpeedFormatConverter}}\" Style=\"{StaticResource FontColor1}\"/>\n                    <TextBlock FontSize=\"12\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Padding=\"0\" Text=\"ps\" Style=\"{StaticResource FontColor1}\"/>\n                </WrapPanel>\n            </Grid>\n\n        </Grid>\n    </Border>\n    \n</Window>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindow.xaml.cs",
    "content": "﻿using System.Windows;\nusing OpenNetMeter.ViewModels;\nusing System.Windows.Input;\nusing Forms = System.Windows.Forms;\nusing System;\nusing System.Threading.Tasks;\nusing System.Threading;\nusing System.Drawing;\nusing OpenNetMeter.Models;\nusing System.Windows.Threading;\nusing System.Diagnostics;\nusing System.Windows.Controls;\nusing System.ComponentModel;\nusing OpenNetMeter.Properties;\n\nnamespace OpenNetMeter.Views\n{\n    /// <summary>\n    /// Interaction logic for MainWindow.xaml\n    /// </summary>\n    public partial class MainWindow : Window\n    {\n        private Mutex? mutex;\n        public bool IsSingleInstance()\n        {\n            bool createdNew;\n            mutex = new Mutex(true, \"{6C4919CA-062E-47E3-85CC-2393D00CBA4A}\", out createdNew);\n\n            if (!createdNew)\n            {\n                //exit app\n                MessageBox.Show(\"An instance is already running,\\nCheck if it's minimized to the system tray\", \"OpenNetMeter\", MessageBoxButton.OK);\n                \n                Application.Current.Shutdown();\n                return false;\n            }\n            else\n                return true;\n        }\n\n        //--------- windows ------------//\n        private ConfirmationDialog? confDialog;\n        private AboutWindow? aboutWin;\n        private MiniWidgetV? miniWidget;\n        private MainWindowVM? mainWin;\n\n        //--------- timers ------------//\n        private DispatcherTimer resizeTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 200), IsEnabled = false };\n        private DispatcherTimer relocationTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 200), IsEnabled = false };\n\n        //--------- tray icon -----------//\n        private Forms.NotifyIcon? trayIcon;\n        private bool balloonShow;\n\n        public MainWindow()\n        {\n            if (IsSingleInstance())\n            {\n                InitializeComponent();\n\n                confDialog = new ConfirmationDialog(new System.Windows.Rect(this.Left, this.Top, this.ActualWidth, this.ActualHeight));     \n                aboutWin = new AboutWindow(new System.Windows.Rect(this.Left, this.Top, this.ActualWidth, this.ActualHeight));\n                miniWidget = new MiniWidgetV(this);\n                mainWin = new MainWindowVM((MiniWidgetVM)miniWidget.DataContext, (ConfirmationDialogVM)confDialog.DataContext);\n                DataContext = mainWin;\n                mainWin.svm.RequestSetMiniWidgetVisibility += SetMiniWidgetVisibility;\n                \n                //initialize window position and size\n                AllWinPosAndSizeInit();\n\n                //initialize system tray\n                trayIcon = new Forms.NotifyIcon();\n                Forms.ContextMenuStrip cm = new Forms.ContextMenuStrip();\n                balloonShow = false;\n                trayIcon.Icon = Properties.Resources.AppIcon;\n                trayIcon.Visible = true;\n                trayIcon.DoubleClick += Ni_DoubleClick;\n                trayIcon.MouseClick += Ni_MouseClick;\n                cm.Items.Add(\"Reset all window positions\", null, ResetWinPos_Click);\n                cm.Items.Add(\"Show Mini Widget\", null, MiniWidget_Show_Click);\n                cm.Items.Add(new Forms.ToolStripSeparator());\n                cm.Items.Add(\"Open\", null, Cm_Open_Click);\n                cm.Items.Add(\"Exit\", null, Cm_Exit_Click);\n                trayIcon.ContextMenuStrip = cm;\n\n                //------- events ----------//\n                this.Closing += MainWindow_Closing;\n                this.Loaded += MainWindow_Loaded;\n            }\n        }\n\n        private void MainWindow_Loaded(object sender, RoutedEventArgs e)\n        {\n            if(confDialog != null)\n                confDialog.Owner = this;\n            if(aboutWin != null)\n                aboutWin.Owner = this;\n        }\n\n        private void ResetWinPos_Click(object? sender, EventArgs e)\n        {\n            this.Left = SystemParameters.PrimaryScreenWidth/2 - this.Width / 2;\n            this.Top = SystemParameters.PrimaryScreenHeight/2 - this.Height / 2;\n\n            SaveWinPos((int)this.Left, (int)this.Top);\n\n            if(miniWidget != null)\n            {\n                miniWidget.Left = this.Left + this.Width / 2 - miniWidget.Width / 2;\n                miniWidget.Top = this.Top + this.Height / 2 - miniWidget.Height / 2;\n\n                miniWidget.SaveWinPos((int)miniWidget.Left, (int)miniWidget.Top);\n            }       \n        }\n\n        private void MiniWidget_Show_Click(object? sender, EventArgs e)\n        {\n            ShowMiniWidget();\n        }\n\n        public void ShowMiniWidget()\n        {\n            if (miniWidget == null)\n                return;\n\n            miniWidget.ShowMiniWidget();\n            mainWin?.svm.SyncMiniWidgetVisibility(true);\n        }\n\n        public void HideMiniWidget()\n        {\n            if (miniWidget == null)\n                return;\n\n            miniWidget.HideMiniWidget();\n            mainWin?.svm.SyncMiniWidgetVisibility(false);\n        }\n\n        private void SetMiniWidgetVisibility(bool isVisible)\n        {\n            if (isVisible)\n                ShowMiniWidget();\n            else\n                HideMiniWidget();\n        }\n\n        // this is for when the user clicks the window exit button through the alt+tab program switcher\n        private void MainWindow_Closing(object? sender, CancelEventArgs e)\n        {\n            e.Cancel = true;\n            this.Visibility = Visibility.Collapsed;\n        }\n        private void Ni_MouseClick(object? sender, Forms.MouseEventArgs e)\n        {\n            switch (e.Button)\n            {\n                case Forms.MouseButtons.Right:\n                    if(trayIcon != null && trayIcon.ContextMenuStrip != null)\n                    {\n                        if (SettingsManager.Current.DarkMode)\n                            trayIcon.ContextMenuStrip.ForeColor = Color.White;\n                        else\n                            trayIcon.ContextMenuStrip.ForeColor = Color.Black;\n                        trayIcon.ContextMenuStrip.Renderer = new CustomSystemTray();\n                    }\n                    break;\n            }\n        }\n\n        private void AllWinPosAndSizeInit()\n        {\n            if (SettingsManager.Current.LaunchFirstTime)\n            {\n                SettingsManager.Current.WinSize = new System.Drawing.Size((int)this.MinWidth, (int)this.MinHeight);\n                this.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;\n                SettingsManager.Current.WinPos = new System.Drawing.Point((int)this.Left, (int)this.Top);\n\n                if(miniWidget != null)\n                {\n                    miniWidget.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;\n                    SettingsManager.Current.MiniWidgetPos = new System.Drawing.Point((int)miniWidget.Left, (int)miniWidget.Top);\n                }\n\n                SettingsManager.Current.LaunchFirstTime = false;\n                SettingsManager.Save();\n            }\n\n            this.Left = SettingsManager.Current.WinPos.X;\n            this.Top = SettingsManager.Current.WinPos.Y;\n            this.Width = SettingsManager.Current.WinSize.Width;\n            this.Height = SettingsManager.Current.WinSize.Height;\n\n            if(miniWidget!= null)\n            {\n                miniWidget.Left = SettingsManager.Current.MiniWidgetPos.X;\n                miniWidget.Top = SettingsManager.Current.MiniWidgetPos.Y;\n            }\n\n            //check if window is out of bounds. This is for, when the user last opened the app in the 2nd monitor and then reopens it with a 1 monitor setup.\n            bool isInScreen = false;\n            for (int i = 0; i < System.Windows.Forms.Screen.AllScreens.Length; i++)\n            {\n                //extra margin to repoisition the app when its outside the screen and only its borders are intersecting the edge.\n                int margin = 32;\n                Rectangle rectA = System.Windows.Forms.Screen.AllScreens[i].WorkingArea;\n                if (rectA.Left < (this.Left + this.Width - margin) && (rectA.Left + rectA.Width) > this.Left + margin &&\n                    rectA.Top < (this.Top + this.Height - margin) && (rectA.Top + rectA.Height) > this.Top + margin)\n                {\n                    isInScreen = true;\n                }\n            }\n\n            //if main window is out of bounds, center it\n            if (!isInScreen)\n            {\n                this.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;\n                SettingsManager.Current.WinPos = new System.Drawing.Point((int)this.Left, (int)this.Top);\n                SettingsManager.Save();\n            }\n\n            resizeTimer.Tick += ResizeTimer_Tick;\n            relocationTimer.Tick += RelocationTimer_Tick;\n        }\n\n        private void Cm_Open_Click(object? sender, EventArgs e)\n        {\n            this.Visibility = Visibility.Visible;\n            this.Activate();\n        }\n\n        private void Cm_Exit_Click(object? sender, EventArgs e)\n        {\n            confDialog?.Close();\n            miniWidget?.Close();\n            aboutWin?.Close();\n            mainWin?.Dispose();\n            this.Closing -= MainWindow_Closing;\n            this.Close();\n            if(trayIcon != null)\n                trayIcon.Visible = false;\n        }\n\n        private void Ni_DoubleClick(object? sender, EventArgs e)\n        {\n            this.Visibility = Visibility.Visible;\n            this.Activate();\n        }\n\n        private void Grid_MouseDown(object sender, MouseButtonEventArgs e)\n        {\n            if (WindowState == WindowState.Maximized)\n            {\n                double fullScreenWidth = this.RenderSize.Width;\n                double fullScreenHeight = this.RenderSize.Height;\n                WindowState = WindowState.Normal;\n                this.Left = e.GetPosition(this).X - (this.RenderSize.Width / fullScreenWidth) * e.GetPosition(this).X;\n                this.Top = e.GetPosition(this).Y - (this.RenderSize.Height / fullScreenHeight) * e.GetPosition(this).Y;\n            }\n\n            DragMove();\n        }\n\n        private void Minimize_Button_Click(object sender, RoutedEventArgs e)\n        {\n            WindowState = WindowState.Minimized;\n        }\n        \n        public void Exit_Button_Click(object? sender, RoutedEventArgs? e)\n        {\n            if (!balloonShow && trayIcon != null)\n            {\n                trayIcon.ShowBalloonTip(1000, \"\", \"Minimized to system tray\", Forms.ToolTipIcon.None);\n                balloonShow = true;\n            }\n            if(aboutWin != null)\n                aboutWin.Visibility = Visibility.Collapsed;\n            if(confDialog != null)\n                confDialog.Visibility = Visibility.Collapsed;\n            this.Visibility = Visibility.Collapsed;\n        }\n\n\n\n        private void About_Button_Click(object sender, RoutedEventArgs e)\n        {\n            if(aboutWin != null)\n                aboutWin.Visibility = Visibility.Visible;\n        }\n\n        //save window size and position at the end of the respective events\n        private void ResizeTimer_Tick(object? sender, EventArgs e)\n        {\n            resizeTimer.IsEnabled = false;\n\n            //Do end of resize processing\n            SettingsManager.Current.WinSize =  new System.Drawing.Size((int)this.Width, (int)this.Height);\n            SettingsManager.Save();\n\n            //pass parent window dimensions to confirmation dialog\n            confDialog?.SetParentWindowRect(new System.Windows.Rect(this.Left, this.Top, this.ActualWidth, this.ActualHeight));\n            aboutWin?.SetParentWindowRect(new System.Windows.Rect(this.Left, this.Top, this.ActualWidth, this.ActualHeight));\n        }\n\n        private void MyWindow_SizeChanged(object? sender, SizeChangedEventArgs e)\n        {\n            resizeTimer.IsEnabled = true;\n            resizeTimer.Stop();\n            resizeTimer.Start();\n        }\n        \n        private void RelocationTimer_Tick(object? sender, EventArgs e)\n        {\n            relocationTimer.IsEnabled = false;\n\n            //Do end of relocation processing\n            SaveWinPos((int)this.Left, (int)this.Top);\n        }\n\n        private void SaveWinPos(int x, int y)\n        {\n            SettingsManager.Current.WinPos = new System.Drawing.Point(x, y);\n            SettingsManager.Save();\n\n            //pass parent window dimensions to confirmation and about dialog\n            confDialog?.SetParentWindowRect(new System.Windows.Rect(this.Left, this.Top, this.ActualWidth, this.ActualHeight));\n            aboutWin?.SetParentWindowRect(new System.Windows.Rect(this.Left, this.Top, this.ActualWidth, this.ActualHeight));\n        }\n\n        private void MyWindow_LocationChanged(object sender, EventArgs e)\n        {\n            relocationTimer.IsEnabled = true;\n            relocationTimer.Stop();\n            relocationTimer.Start();    \n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/DataUsageHistoryV.xaml",
    "content": "﻿<UserControl x:Class=\"OpenNetMeter.Views.DataUsageHistoryV\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:v=\"clr-namespace:OpenNetMeter.Views\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"450\" d:DesignWidth=\"800\">\n    <UserControl.Resources>\n        <v:UnitConverterBytes x:Key=\"converterBytes\"/>\n    </UserControl.Resources>\n    <Grid Style=\"{StaticResource BackgroundColor2}\">\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"50\"/>\n            <RowDefinition/>\n            <RowDefinition Height=\"32\"/>\n        </Grid.RowDefinitions>\n        <StackPanel Orientation=\"Horizontal\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\">\n            <Label VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\">Usage of</Label>\n            <StackPanel Grid.Row=\"0\" Margin=\"4,10,10,10\" Width=\"150\">\n                <ComboBox x:Name=\"combo_box\" ItemsSource=\"{Binding Profiles, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\" \n                          SelectedItem=\"{Binding SelectedProfile , Mode=TwoWay}\" \n                          Style=\"{StaticResource ComboBoxFlatStyle}\"/>\n            </StackPanel>\n            <Label VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\">From:</Label>\n            <DatePicker Style=\"{StaticResource ModernDatePicker}\" VerticalAlignment=\"Center\" DisplayDateStart=\"{Binding DateMin, Mode=OneWay}\" DisplayDateEnd=\"{Binding DateMax, Mode=OneWay}\" SelectedDate=\"{Binding DateStart}\" ></DatePicker>\n            <Label VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\">To:</Label>\n            <DatePicker Style=\"{StaticResource ModernDatePicker}\" VerticalAlignment=\"Center\" DisplayDateStart=\"{Binding DateMin, Mode=OneWay}\" DisplayDateEnd=\"{Binding DateMax, Mode=OneWay}\" SelectedDate=\"{Binding DateEnd}\"></DatePicker>\n            <Button Height=\"24\" Width=\"64\" Margin=\"16,0\" Command=\"{Binding FilterBtn}\" Style=\"{StaticResource ButtonColor}\">\n                <TextBlock FontFamily=\"Segoe UI\" Text=\"Filter\" VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\"/>\n                <Button.Resources>\n                    <Style TargetType=\"{x:Type Border}\">\n                        <Setter Property=\"CornerRadius\" Value=\"4\"/>\n                        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderEmphasis}\"/>\n                        <Setter Property=\"BorderThickness\" Value=\"1\"/>\n                    </Style>\n                </Button.Resources>\n            </Button> \n           \n        </StackPanel>\n        <DataGrid Grid.Row=\"1\" x:Name=\"AllAppsData\" FrozenColumnCount=\"1\" IsReadOnly=\"True\" AutoGenerateColumns=\"False\" ItemsSource=\"{Binding MyProcesses}\" BorderThickness=\"1\" Style=\"{DynamicResource ModernDGHeaderStyle}\">\n            <DataGrid.Columns>\n                <DataGridTemplateColumn x:Name=\"DG_Name\" Header=\"Name\" HeaderStyle=\"{StaticResource ModernDGCHeaderStyle1}\" CellStyle=\"{StaticResource ModernDGCellStyle1}\">\n                    <DataGridTemplateColumn.CellTemplate>\n                        <DataTemplate>\n                            <StackPanel Orientation=\"Horizontal\" VerticalAlignment=\"Center\">\n                                <Image Source=\"{Binding Icon}\" Width=\"16\" Height=\"16\" Margin=\"0,0,8,0\"/>\n                                <TextBlock Text=\"{Binding Name}\" VerticalAlignment=\"Center\"/>\n                            </StackPanel>\n                        </DataTemplate>\n                    </DataGridTemplateColumn.CellTemplate>\n                </DataGridTemplateColumn>\n                <DataGridTextColumn x:Name=\"DG_DataRecv\" Header=\"Data Received\" Binding=\"{Binding CurrentDataRecv, Converter={StaticResource converterBytes}}\" HeaderStyle=\"{StaticResource ModernDGCHeaderStyle2}\" CellStyle=\"{StaticResource ModernDGCellStyle2}\"/>\n                <DataGridTextColumn x:Name=\"DG_DataSent\" Header=\"Data Sent\" Binding=\"{Binding CurrentDataSend, Converter={StaticResource converterBytes}}\" HeaderStyle=\"{StaticResource ModernDGCHeaderStyle2}\" CellStyle=\"{StaticResource ModernDGCellStyle2}\"/>\n            </DataGrid.Columns>\n        </DataGrid>\n        <!-- Footer aligned to DataGrid columns -->\n        <Grid Grid.Row=\"2\"\n      x:Name=\"TotalsFooter\"\n      Width=\"{Binding ElementName=AllAppsData, Path=ActualWidth}\"\n      Style=\"{StaticResource ModernFooterGridStyle}\">\n            <Grid.ColumnDefinitions>\n                <ColumnDefinition Width=\"{Binding ElementName=AllAppsData, Path=Columns[0].ActualWidth}\"/>\n                <ColumnDefinition Width=\"{Binding ElementName=AllAppsData, Path=Columns[1].ActualWidth}\"/>\n                <ColumnDefinition Width=\"{Binding ElementName=AllAppsData, Path=Columns[2].ActualWidth}\"/>\n            </Grid.ColumnDefinitions>\n\n            <Border Grid.Column=\"0\" Style=\"{StaticResource ModernFooterTextBorder}\">\n                <TextBlock Text=\"Total\"\n                   Style=\"{StaticResource ModernFooterTextStyle}\"/>\n            </Border>\n\n            <Border Grid.Column=\"1\" Style=\"{StaticResource ModernFooterTextBorder}\">\n                <TextBlock Text=\"{Binding TotalDownloadData, Converter={StaticResource converterBytes}}\"\n                   Style=\"{StaticResource ModernFooterTextStyle}\"/>\n            </Border>\n\n            <Border Grid.Column=\"2\" Style=\"{StaticResource ModernFooterTextBorder}\">\n                <TextBlock Text=\"{Binding TotalUploadData, Converter={StaticResource converterBytes}}\"\n                   Style=\"{StaticResource ModernFooterTextStyle}\"/>\n            </Border>\n        </Grid>\n\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/DataUsageHistoryV.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace OpenNetMeter.Views\n{\n    /// <summary>\n    /// Interaction logic for DataUsageHistoryV.xaml\n    /// </summary>\n    public partial class DataUsageHistoryV : UserControl\n    {\n        public DataUsageHistoryV()\n        {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/DataUsageSummaryV.xaml",
    "content": "﻿<UserControl x:Class=\"OpenNetMeter.Views.DataUsageSummaryV\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:vm=\"clr-namespace:OpenNetMeter.ViewModels\"\n             xmlns:v=\"clr-namespace:OpenNetMeter.Views\"\n             mc:Ignorable=\"d\" \n             UseLayoutRounding=\"True\"\n             d:DesignHeight=\"600\" d:DesignWidth=\"800\">\n\n    <UserControl.Resources>\n        <v:UnitConverterBytes x:Key=\"converterBytes\"/>\n    </UserControl.Resources>\n\n    <Grid Margin=\"12\">\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"*\"/>\n            <RowDefinition Height=\"100\"/>\n        </Grid.RowDefinitions>\n\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"180\"/>\n            <ColumnDefinition Width=\"12\"/>\n            <ColumnDefinition Width=\"*\"/>\n        </Grid.ColumnDefinitions>\n\n        <!-- Left Column: Data Usage Cards + Activity Header (L-shape top) -->\n        <Grid Grid.Row=\"0\" Grid.Column=\"0\">\n            <Grid.RowDefinitions>\n                <RowDefinition Height=\"Auto\"/>\n                <RowDefinition Height=\"10\"/>\n                <RowDefinition Height=\"Auto\"/>\n                <RowDefinition Height=\"10\"/>\n                <RowDefinition Height=\"*\"/>\n            </Grid.RowDefinitions>\n\n            <!-- Current Session Card -->\n            <Border Grid.Row=\"0\" Style=\"{StaticResource ModernCard}\" Padding=\"10\">\n                <StackPanel>\n                    <StackPanel Orientation=\"Horizontal\" Margin=\"0,0,0,8\">\n                        <Border Style=\"{StaticResource DataRowAccentDownload}\">\n                            <Path Data=\"M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C6.47,22 2,17.5 2,12A10,10 0 0,1 12,2M12.5,7V12.25L17,14.92L16.25,16.15L11,13V7H12.5Z\" \n                                  Fill=\"{StaticResource BrushWhite}\" Width=\"12\" Height=\"12\" Stretch=\"Uniform\"/>\n                        </Border>\n                        <TextBlock Text=\"Current Session\" FontSize=\"11\" FontWeight=\"SemiBold\" Style=\"{StaticResource FontColor1}\" VerticalAlignment=\"Center\" Margin=\"6,0,0,0\"/>\n                    </StackPanel>\n\n                    <Border Style=\"{StaticResource DataRowAccentDownload}\" Margin=\"0,0,0,4\">\n                        <Grid>\n                            <StackPanel Orientation=\"Horizontal\">\n                                <Path Data=\"M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z\" Fill=\"{StaticResource BrushWhite}\" Width=\"12\" Height=\"12\" Stretch=\"Uniform\" VerticalAlignment=\"Center\"/>\n                                <TextBlock Text=\"DL\" Foreground=\"{StaticResource BrushWhite}\" FontSize=\"10\" VerticalAlignment=\"Center\" Margin=\"4,0,0,0\"/>\n                            </StackPanel>\n                            <TextBlock Text=\"{Binding CurrentSessionDownloadData, Converter={StaticResource converterBytes}}\" Foreground=\"{StaticResource BrushWhite}\" FontSize=\"11\" FontWeight=\"SemiBold\" HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\"/>\n                        </Grid>\n                    </Border>\n\n                    <Border Style=\"{StaticResource DataRowAccentUpload}\">\n                        <Grid>\n                            <StackPanel Orientation=\"Horizontal\">\n                                <Path Data=\"M9,16V10H5L12,3L19,10H15V16H9M5,20V18H19V20H5Z\" Fill=\"{StaticResource BrushWhite}\" Width=\"12\" Height=\"12\" Stretch=\"Uniform\" VerticalAlignment=\"Center\"/>\n                                <TextBlock Text=\"UL\" Foreground=\"{StaticResource BrushWhite}\" FontSize=\"10\" VerticalAlignment=\"Center\" Margin=\"4,0,0,0\"/>\n                            </StackPanel>\n                            <TextBlock Text=\"{Binding CurrentSessionUploadData, Converter={StaticResource converterBytes}}\" Foreground=\"{StaticResource BrushWhite}\" FontSize=\"11\" FontWeight=\"SemiBold\" HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\"/>\n                        </Grid>\n                    </Border>\n                </StackPanel>\n            </Border>\n\n            <!-- Data Usage From Card -->\n            <Border Grid.Row=\"2\" Style=\"{StaticResource ModernCard}\" Padding=\"10\">\n                <StackPanel>\n                    <Grid Margin=\"0,0,0,8\">\n                        <StackPanel Orientation=\"Horizontal\">\n                            <Border Style=\"{StaticResource DataRowAccentUpload}\">\n                                <Path Data=\"M12,3C7.58,3 4,4.79 4,7C4,9.21 7.58,11 12,11C16.42,11 20,9.21 20,7C20,4.79 16.42,3 12,3M4,9V12C4,14.21 7.58,16 12,16C16.42,16 20,14.21 20,12V9C20,11.21 16.42,13 12,13C7.58,13 4,11.21 4,9M4,14V17C4,19.21 7.58,21 12,21C16.42,21 20,19.21 20,17V14C20,16.21 16.42,18 12,18C7.58,18 4,16.21 4,14Z\" \n                                      Fill=\"{StaticResource BrushWhite}\" Width=\"12\" Height=\"12\" Stretch=\"Uniform\"/>\n                            </Border>\n                            <TextBlock Text=\"Usage From\" FontSize=\"11\" FontWeight=\"SemiBold\" Style=\"{StaticResource FontColor1}\" VerticalAlignment=\"Center\" Margin=\"6,0,0,0\"/>\n                        </StackPanel>\n                    </Grid>\n\n                    <DatePicker HorizontalAlignment=\"Stretch\" Margin=\"0,0,0,8\"\n                                Style=\"{StaticResource ModernDatePicker}\"\n                                DisplayDateStart=\"{Binding DateMin, Mode=OneWay}\" \n                                DisplayDateEnd=\"{Binding DateMax, Mode=OneWay}\" \n                                SelectedDate=\"{Binding SinceDate}\"/>\n\n                    <Border Style=\"{StaticResource DataRowAccentDownload}\" Margin=\"0,0,0,4\">\n                        <Grid>\n                            <StackPanel Orientation=\"Horizontal\">\n                                <Path Data=\"M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z\" Fill=\"{StaticResource BrushWhite}\" Width=\"12\" Height=\"12\" Stretch=\"Uniform\" VerticalAlignment=\"Center\"/>\n                                <TextBlock Text=\"DL\" Foreground=\"{StaticResource BrushWhite}\" FontSize=\"10\" VerticalAlignment=\"Center\" Margin=\"4,0,0,0\"/>\n                            </StackPanel>\n                            <TextBlock Text=\"{Binding TodayDownloadData, Converter={StaticResource converterBytes}}\" Foreground=\"{StaticResource BrushWhite}\" FontSize=\"11\" FontWeight=\"SemiBold\" HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\"/>\n                        </Grid>\n                    </Border>\n\n                    <Border Style=\"{StaticResource DataRowAccentUpload}\">\n                        <Grid>\n                            <StackPanel Orientation=\"Horizontal\">\n                                <Path Data=\"M9,16V10H5L12,3L19,10H15V16H9M5,20V18H19V20H5Z\" Fill=\"{StaticResource BrushWhite}\" Width=\"12\" Height=\"12\" Stretch=\"Uniform\" VerticalAlignment=\"Center\"/>\n                                <TextBlock Text=\"UL\" Foreground=\"{StaticResource BrushWhite}\" FontSize=\"10\" VerticalAlignment=\"Center\" Margin=\"4,0,0,0\"/>\n                            </StackPanel>\n                            <TextBlock Text=\"{Binding TodayUploadData, Converter={StaticResource converterBytes}}\" Foreground=\"{StaticResource BrushWhite}\" FontSize=\"11\" FontWeight=\"SemiBold\" HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\"/>\n                        </Grid>\n                    </Border>\n                </StackPanel>\n            </Border>\n\n            <!-- Activity Header Card (L-shape vertical part) - no bottom margin, connects to graph -->\n            <Border Grid.Row=\"4\" Style=\"{StaticResource ModernCard}\" Padding=\"10\" CornerRadius=\"12,12,0,0\" VerticalAlignment=\"Bottom\">\n                <Border.Effect>\n                    <DropShadowEffect BlurRadius=\"10\" ShadowDepth=\"2\" Opacity=\"0.2\" Direction=\"270\"/>\n                </Border.Effect>\n                <StackPanel VerticalAlignment=\"Center\">\n                    <StackPanel Orientation=\"Horizontal\">\n                        <Border Style=\"{StaticResource DataRowAccentDownload}\">\n                            <Path Data=\"M22,21H2V3H4V19H6V10H10V19H12V6H16V19H18V14H22V21Z\" Fill=\"{StaticResource BrushWhite}\" Width=\"12\" Height=\"12\" Stretch=\"Uniform\"/>\n                        </Border>\n                        <TextBlock Text=\"Activity\" FontSize=\"11\" FontWeight=\"SemiBold\" Style=\"{StaticResource FontColor1}\" VerticalAlignment=\"Center\" Margin=\"6,0,0,0\"/>\n                    </StackPanel>\n                    <StackPanel Orientation=\"Horizontal\" Margin=\"0,8,0,0\">\n                        <Ellipse Width=\"8\" Height=\"8\" Fill=\"{StaticResource BrushDownloadAccent}\" VerticalAlignment=\"Center\"/>\n                        <TextBlock Text=\"Download\" Style=\"{StaticResource FontColor2}\" FontSize=\"10\" Margin=\"4,0,12,0\" VerticalAlignment=\"Center\"/>\n                        <Ellipse Width=\"8\" Height=\"8\" Fill=\"{StaticResource BrushUploadAccent}\" VerticalAlignment=\"Center\"/>\n                        <TextBlock Text=\"Upload\" Style=\"{StaticResource FontColor2}\" FontSize=\"10\" Margin=\"4,0,0,0\" VerticalAlignment=\"Center\"/>\n                    </StackPanel>\n                </StackPanel>\n            </Border>\n        </Grid>\n\n        <!-- Right Column: Active Processes Table -->\n        <Border Grid.Row=\"0\" Grid.Column=\"2\" Style=\"{StaticResource ModernCard}\" Margin=\"0,0,0,10\">\n            <Grid>\n                <Grid.RowDefinitions>\n                    <RowDefinition Height=\"Auto\"/>\n                    <RowDefinition Height=\"*\"/>\n                </Grid.RowDefinitions>\n\n                <!-- Table Header -->\n                <Border Grid.Row=\"0\" Padding=\"12,4\" CornerRadius=\"12,12,0,0\" Style=\"{StaticResource CardHeaderStyle}\" BorderBrush=\"Gray\" BorderThickness=\"0,0,0,0.4\">\n                    <Grid >\n                        <StackPanel Orientation=\"Horizontal\">\n                            <Path Data=\"M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M7.5,13A2.5,2.5 0 0,0 5,15.5A2.5,2.5 0 0,0 7.5,18A2.5,2.5 0 0,0 10,15.5A2.5,2.5 0 0,0 7.5,13M16.5,13A2.5,2.5 0 0,0 14,15.5A2.5,2.5 0 0,0 16.5,18A2.5,2.5 0 0,0 19,15.5A2.5,2.5 0 0,0 16.5,13Z\" \n                                  Style=\"{StaticResource Fill1}\" Width=\"18\" Height=\"18\" Stretch=\"Uniform\" VerticalAlignment=\"Center\"/>\n                            <TextBlock Text=\"Active Processes\" Style=\"{StaticResource FontColor1}\" FontSize=\"12\" FontWeight=\"SemiBold\" VerticalAlignment=\"Center\" Margin=\"8,0,0,0\"/>\n                        </StackPanel>\n                        <TextBlock HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\" Style=\"{StaticResource FontColor1}\" FontSize=\"10\" Opacity=\"0.85\">\n                            <Run Text=\"{Binding MyProcesses.Count, Mode=OneWay, FallbackValue=0}\"/>\n                            <Run Text=\"processes\"/>\n                        </TextBlock>\n                    </Grid>\n                </Border>\n\n                <!-- DataGrid -->\n                <DataGrid Grid.Row=\"1\" \n                          x:Name=\"AllAppsData1\"\n                          ItemsSource=\"{Binding MyProcesses}\"\n                          Style=\"{DynamicResource ModernDGHeaderStyle}\">\n\n                    <DataGrid.Columns>\n                        <DataGridTemplateColumn Header=\"Name\" Width=\"*\" MinWidth=\"120\" HeaderStyle=\"{StaticResource ModernDGCHeaderStyle1}\" CellStyle=\"{StaticResource ModernDGCellStyle1}\">\n                            <DataGridTemplateColumn.CellTemplate>\n                                <DataTemplate>\n                                    <StackPanel Orientation=\"Horizontal\" VerticalAlignment=\"Center\" Margin=\"8,4\">\n                                        <Image Source=\"{Binding Value.Icon}\" Width=\"16\" Height=\"16\" RenderOptions.BitmapScalingMode=\"HighQuality\"/>\n                                        <TextBlock Text=\"{Binding Value.Name}\" VerticalAlignment=\"Center\" Margin=\"8,0,0,0\" FontSize=\"11\" TextTrimming=\"CharacterEllipsis\"/>\n                                    </StackPanel>\n                                </DataTemplate>\n                            </DataGridTemplateColumn.CellTemplate>\n                        </DataGridTemplateColumn>\n\n                        <DataGridTextColumn Header=\"↓ Current\" \n                                            Binding=\"{Binding Value.CurrentDataRecv, Converter={StaticResource converterBytes}}\" \n                                            Width=\"80\"\n                                            HeaderStyle=\"{StaticResource ModernDGCHeaderStyle2}\" \n                                            CellStyle=\"{StaticResource ModernDGCellStyle2}\">\n                            <DataGridTextColumn.ElementStyle>\n                                <Style TargetType=\"TextBlock\">\n                                    <Setter Property=\"FontSize\" Value=\"11\"/>\n                                    <Setter Property=\"Foreground\" Value=\"{StaticResource BrushDownloadAccent}\"/>\n                                    <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n                                    <Setter Property=\"Margin\" Value=\"8,4\"/>\n                                </Style>\n                            </DataGridTextColumn.ElementStyle>\n                        </DataGridTextColumn>\n\n                        <DataGridTextColumn Header=\"↑ Current\" \n                                            Binding=\"{Binding Value.CurrentDataSend, Converter={StaticResource converterBytes}}\" \n                                            Width=\"80\"\n                                            HeaderStyle=\"{StaticResource ModernDGCHeaderStyle2}\" \n                                            CellStyle=\"{StaticResource ModernDGCellStyle2}\">\n                            <DataGridTextColumn.ElementStyle>\n                                <Style TargetType=\"TextBlock\">\n                                    <Setter Property=\"FontSize\" Value=\"11\"/>\n                                    <Setter Property=\"Foreground\" Value=\"{StaticResource BrushUploadAccent}\"/>\n                                    <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n                                    <Setter Property=\"Margin\" Value=\"8,4\"/>\n                                </Style>\n                            </DataGridTextColumn.ElementStyle>\n                        </DataGridTextColumn>\n\n                        <DataGridTextColumn Header=\"↓ Total\" \n                                            Binding=\"{Binding Value.TotalDataRecv, Converter={StaticResource converterBytes}}\" \n                                            Width=\"80\"\n                                            HeaderStyle=\"{StaticResource ModernDGCHeaderStyle2}\" \n                                            CellStyle=\"{StaticResource ModernDGCellStyle2}\">\n                            <DataGridTextColumn.ElementStyle>\n                                <Style TargetType=\"TextBlock\">\n                                    <Setter Property=\"FontSize\" Value=\"11\"/>\n                                    <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n                                    <Setter Property=\"Margin\" Value=\"8,4\"/>\n                                </Style>\n                            </DataGridTextColumn.ElementStyle>\n                        </DataGridTextColumn>\n\n                        <DataGridTextColumn Header=\"↑ Total\" \n                                            Binding=\"{Binding Value.TotalDataSend, Converter={StaticResource converterBytes}}\" \n                                            Width=\"80\"\n                                            HeaderStyle=\"{StaticResource ModernDGCHeaderStyle2}\" \n                                            CellStyle=\"{StaticResource ModernDGCellStyle2}\">\n                            <DataGridTextColumn.ElementStyle>\n                                <Style TargetType=\"TextBlock\">\n                                    <Setter Property=\"FontSize\" Value=\"11\"/>\n                                    <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n                                    <Setter Property=\"Margin\" Value=\"8,4\"/>\n                                </Style>\n                            </DataGridTextColumn.ElementStyle>\n                        </DataGridTextColumn>\n                    </DataGrid.Columns>\n                </DataGrid>\n            </Grid>\n        </Border>\n\n        <!-- Bottom Row: Graph (L-shape horizontal part) -->\n        <Border Grid.Row=\"1\" Grid.Column=\"0\" Grid.ColumnSpan=\"3\" Style=\"{StaticResource ModernCard}\" Padding=\"10\" CornerRadius=\"0,12,12,12\">\n            <Grid x:Name=\"GraphGrid\" SizeChanged=\"Graph_SizeChanged\">\n                <ItemsControl ItemsSource=\"{Binding Graph.XLines}\">\n                    <ItemsControl.ItemsPanel>\n                        <ItemsPanelTemplate>\n                            <Canvas/>\n                        </ItemsPanelTemplate>\n                    </ItemsControl.ItemsPanel>\n                    <ItemsControl.ItemTemplate>\n                        <DataTemplate>\n                            <Line X1=\"{Binding From.X}\" Y1=\"{Binding From.Y}\" X2=\"{Binding To.X}\" Y2=\"{Binding To.Y}\" \n                                  StrokeThickness=\"1\" Stroke=\"{StaticResource BrushSurfaceDivider}\" StrokeDashArray=\"4 4\"/>\n                        </DataTemplate>\n                    </ItemsControl.ItemTemplate>\n                </ItemsControl>\n\n                <ItemsControl ItemsSource=\"{Binding Graph.YLines}\">\n                    <ItemsControl.ItemsPanel>\n                        <ItemsPanelTemplate>\n                            <Canvas/>\n                        </ItemsPanelTemplate>\n                    </ItemsControl.ItemsPanel>\n                    <ItemsControl.ItemTemplate>\n                        <DataTemplate>\n                            <Line X1=\"{Binding From.X}\" Y1=\"{Binding From.Y}\" X2=\"{Binding To.X}\" Y2=\"{Binding To.Y}\" \n                                  StrokeThickness=\"1\" Stroke=\"{StaticResource BrushSurfaceDivider}\"/>\n                        </DataTemplate>\n                    </ItemsControl.ItemTemplate>\n                </ItemsControl>\n\n                <ItemsControl ItemsSource=\"{Binding Graph.Borders}\">\n                    <ItemsControl.ItemsPanel>\n                        <ItemsPanelTemplate>\n                            <Canvas/>\n                        </ItemsPanelTemplate>\n                    </ItemsControl.ItemsPanel>\n                    <ItemsControl.ItemTemplate>\n                        <DataTemplate>\n                            <Line X1=\"{Binding From.X}\" Y1=\"{Binding From.Y}\" X2=\"{Binding To.X}\" Y2=\"{Binding To.Y}\" \n                                  StrokeThickness=\"1\" Stroke=\"{StaticResource BrushBorderLight}\"/>\n                        </DataTemplate>\n                    </ItemsControl.ItemTemplate>\n                </ItemsControl>\n\n                <ItemsControl ItemsSource=\"{Binding Graph.UploadLines}\">\n                    <ItemsControl.ItemsPanel>\n                        <ItemsPanelTemplate>\n                            <Canvas/>\n                        </ItemsPanelTemplate>\n                    </ItemsControl.ItemsPanel>\n                    <ItemsControl.ItemTemplate>\n                        <DataTemplate>\n                            <Line X1=\"{Binding From.X}\" Y1=\"{Binding From.Y}\" X2=\"{Binding To.X}\" Y2=\"{Binding To.Y}\"\n                                  Stroke=\"{StaticResource BrushUploadAccent}\" StrokeThickness=\"2\" StrokeEndLineCap=\"Round\" StrokeStartLineCap=\"Round\"/>\n                        </DataTemplate>\n                    </ItemsControl.ItemTemplate>\n                </ItemsControl>\n\n                <ItemsControl ItemsSource=\"{Binding Graph.DownloadLines}\">\n                    <ItemsControl.ItemsPanel>\n                        <ItemsPanelTemplate>\n                            <Canvas/>\n                        </ItemsPanelTemplate>\n                    </ItemsControl.ItemsPanel>\n                    <ItemsControl.ItemTemplate>\n                        <DataTemplate>\n                            <Line X1=\"{Binding From.X}\" Y1=\"{Binding From.Y}\" X2=\"{Binding To.X}\" Y2=\"{Binding To.Y}\"\n                                  Stroke=\"{StaticResource BrushDownloadAccent}\" StrokeThickness=\"2\" StrokeEndLineCap=\"Round\" StrokeStartLineCap=\"Round\"/>\n                        </DataTemplate>\n                    </ItemsControl.ItemTemplate>\n                </ItemsControl>\n\n                <ItemsControl ItemsSource=\"{Binding Graph.Xlabels}\" Style=\"{StaticResource GraphLabels}\">\n                    <ItemsControl.ItemsPanel>\n                        <ItemsPanelTemplate>\n                            <Canvas/>\n                        </ItemsPanelTemplate>\n                    </ItemsControl.ItemsPanel>\n                </ItemsControl>\n\n                <ItemsControl ItemsSource=\"{Binding Graph.Ylabels}\" Style=\"{StaticResource GraphLabels}\">\n                    <ItemsControl.ItemsPanel>\n                        <ItemsPanelTemplate>\n                            <Canvas/>\n                        </ItemsPanelTemplate>\n                    </ItemsControl.ItemsPanel>\n                </ItemsControl>\n            </Grid>\n        </Border>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/DataUsageSummaryV.xaml.cs",
    "content": "﻿using System.Diagnostics;\nusing System.Windows.Media;\nusing System.Threading.Tasks;\nusing System.Windows.Controls;\nusing System.Windows.Shapes;\nusing OpenNetMeter.ViewModels;\nusing System.Windows;\nusing System.Collections.Generic;\nusing System;\nusing System.Windows.Threading;\nusing System.Threading;\nusing OpenNetMeter.Models;\n\nnamespace OpenNetMeter.Views\n{\n    /// <summary>\n    /// Interaction logic for DataUsageSummaryV.xaml\n    /// </summary>\n    public partial class DataUsageSummaryV : UserControl\n    {\n        private DispatcherTimer resizeTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 200), IsEnabled = false };\n        private DataUsageSummaryVM? dusvm;\n\n        //private Rectangle GridBorder;\n        private Size maxYtextSize;\n\n        public DataUsageSummaryV()\n        {\n            InitializeComponent();\n            Loaded += delegate\n            {\n                dusvm = (DataUsageSummaryVM)this.DataContext;\n\n                maxYtextSize = ShapeMeasure(new TextBlock { Text = \"0512Mb\", FontSize = 11, Padding = new Thickness(0) });\n                maxYtextSize.Width += 2.0;\n                dusvm.Graph.Xstart = maxYtextSize.Width;\n                double GraphHeight = GraphSize.Height;\n                double GraphWidth = GraphSize.Width;\n                dusvm.Graph.GraphWidth = GraphWidth;\n                dusvm.Graph.GraphHeight = GraphHeight;\n\n                resizeTimer.Tick += ResizeTimer_Tick;\n                Graph_SizeChanged(null,null);\n            };\n        }\n\n\n        private void ResizeTimer_Tick(object? sender, EventArgs e)\n        {\n            resizeTimer.IsEnabled = false;\n\n            //Do end of resize processing\n            if(dusvm != null)\n                dusvm.Graph.resumeDraw = true;\n        }\n\n\n        private Size GraphSize => new Size(\n            Math.Max(0, GraphGrid.ActualWidth - 16 - maxYtextSize.Width),\n            Math.Max(0, GraphGrid.ActualHeight - maxYtextSize.Height));\n\n        public Size ShapeMeasure(TextBlock tb)\n        {\n            // Measured Size is bounded to be less than maxSize\n            Size maxSize = new Size(\n                 double.PositiveInfinity,\n                 double.PositiveInfinity);\n            tb.Measure(maxSize);\n            return tb.DesiredSize;\n        }\n\n        //scale the MyGraph coordinates\n        private void Graph_SizeChanged(object? sender, System.Windows.SizeChangedEventArgs? e)\n        {\n            if (dusvm != null)\n            {\n                resizeTimer.IsEnabled = true;\n                resizeTimer.Stop();\n                resizeTimer.Start();\n\n                //Stop drawing MyGraph\n                dusvm.Graph.resumeDraw = false;\n\n                double GraphHeight = GraphSize.Height;\n                double GraphWidth = GraphSize.Width;\n                dusvm.Graph.GraphWidth = GraphWidth;\n                dusvm.Graph.GraphHeight = GraphHeight;\n\n                for (int i = 0; i < dusvm.Graph.DownloadLines.Count; i++) //scale the download line\n                {\n                    dusvm.Graph.DownloadLines[i].From = new Point(dusvm.Graph.Xstart + dusvm.Graph.DownloadPoints[i].From.X * (GraphWidth / dusvm.Graph.XaxisResolution), dusvm.Graph.ConvToGraphCoords(dusvm.Graph.DownloadPoints[i].From.Y, GraphHeight));\n                    dusvm.Graph.DownloadLines[i].To = new Point(dusvm.Graph.Xstart + dusvm.Graph.DownloadPoints[i].To.X * (GraphWidth / dusvm.Graph.XaxisResolution), dusvm.Graph.ConvToGraphCoords(dusvm.Graph.DownloadPoints[i].To.Y, GraphHeight));\n\n                }\n                for (int i = 0; i < dusvm.Graph.UploadLines.Count; i++) //scale the upload line\n                {\n                    dusvm.Graph.UploadLines[i].From = new Point(dusvm.Graph.Xstart + dusvm.Graph.UploadPoints[i].From.X * (GraphWidth / dusvm.Graph.XaxisResolution), dusvm.Graph.ConvToGraphCoords(dusvm.Graph.UploadPoints[i].From.Y, GraphHeight));\n                    dusvm.Graph.UploadLines[i].To = new Point(dusvm.Graph.Xstart + dusvm.Graph.UploadPoints[i].To.X * (GraphWidth / dusvm.Graph.XaxisResolution), dusvm.Graph.ConvToGraphCoords(dusvm.Graph.UploadPoints[i].To.Y, GraphHeight));\n                }\n\n\n                //scale the X lines\n                for (int i = 0; i < dusvm.Graph.GridXCount; i++)\n                {\n                    Canvas.SetTop(dusvm.Graph.Ylabels[i], ((GraphHeight / (dusvm.Graph.GridXCount - 1)) * (dusvm.Graph.GridXCount - 1 - i)) - maxYtextSize.Height / 2.0);\n                    Size textSize = ShapeMeasure(dusvm.Graph.Ylabels[i]);\n                    Canvas.SetLeft(dusvm.Graph.Ylabels[i], maxYtextSize.Width - textSize.Width - 4.0);\n\n                    if (i > 0 && i < dusvm.Graph.GridXCount - 1)\n                    {\n                        dusvm.Graph.XLines[i - 1].From = new Point(maxYtextSize.Width, (GraphHeight / (dusvm.Graph.GridXCount - 1)) * i);\n                        dusvm.Graph.XLines[i - 1].To = new Point(GraphWidth + maxYtextSize.Width, (GraphHeight / (dusvm.Graph.GridXCount - 1)) * i);\n                    }\n                }\n\n                //scale the Y lines\n                for (int i = 0; i < dusvm.Graph.GridYCount; i++)\n                {\n                    if (i < dusvm.Graph.GridYCount - 1)\n                    {\n                        Canvas.SetTop(dusvm.Graph.Xlabels[i], GraphHeight);\n                        Canvas.SetLeft(dusvm.Graph.Xlabels[i], (GraphWidth / (dusvm.Graph.GridYCount - 1)) * i + maxYtextSize.Width - dusvm.Graph.Xlabels[i].ActualWidth / 2.0);\n                    }\n                    else\n                    {\n                        Canvas.SetTop(dusvm.Graph.Xlabels[i], GraphHeight);\n                        Canvas.SetLeft(dusvm.Graph.Xlabels[i], GraphWidth + maxYtextSize.Width / 4);\n                    }\n\n                    if (i > 0 && i < dusvm.Graph.GridYCount - 1)\n                    {\n                        dusvm.Graph.YLines[i - 1].From = new Point((GraphWidth / (dusvm.Graph.GridYCount - 1)) * i + maxYtextSize.Width, 0);\n                        dusvm.Graph.YLines[i - 1].To = new Point((GraphWidth / (dusvm.Graph.GridYCount - 1)) * i + maxYtextSize.Width, GraphHeight);\n                    }\n                }\n\n                //scale the border\n\n                //Y lines\n                dusvm.Graph.Borders[0].From = new Point(maxYtextSize.Width, 0);\n                dusvm.Graph.Borders[0].To = new Point(maxYtextSize.Width, GraphHeight);\n\n                dusvm.Graph.Borders[1].From = new Point((GraphWidth / (dusvm.Graph.GridYCount - 1)) * (dusvm.Graph.GridYCount - 1) + maxYtextSize.Width, 0);\n                dusvm.Graph.Borders[1].To = new Point((GraphWidth / (dusvm.Graph.GridYCount - 1)) * (dusvm.Graph.GridYCount - 1) + maxYtextSize.Width, GraphHeight);\n\n                //X lines\n                dusvm.Graph.Borders[2].From = new Point(maxYtextSize.Width, 0);\n                dusvm.Graph.Borders[2].To = new Point(GraphWidth + maxYtextSize.Width, 0);\n\n                dusvm.Graph.Borders[3].From = new Point(maxYtextSize.Width, GraphHeight);\n                dusvm.Graph.Borders[3].To = new Point(GraphWidth + maxYtextSize.Width, GraphHeight);\n\n                dusvm.Graph.firstDrawAfterResume = true;\n\n            }\n        }\n       \n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/SettingsV.xaml",
    "content": "﻿<UserControl x:Class=\"OpenNetMeter.Views.SettingsV\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:v=\"clr-namespace:OpenNetMeter.Views\"\n             xmlns:vm=\"clr-namespace:OpenNetMeter.ViewModels\"\n             xmlns:properties=\"clr-namespace:OpenNetMeter.Properties\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"1200\" d:DesignWidth=\"800\">\n\n    <UserControl.Resources>\n        <v:RadioBoolToIntConverter x:Key=\"radioBoolToIntConverter\" />\n        <properties:AppSettings x:Key=\"Settings\"/>\n\n        <!-- Modern Toggle Switch Template -->\n        <Style x:Key=\"ModernToggleSwitch\" TargetType=\"CheckBox\">\n            <Setter Property=\"Cursor\" Value=\"Hand\"/>\n            <Setter Property=\"Template\">\n                <Setter.Value>\n                    <ControlTemplate TargetType=\"CheckBox\">\n                        <Grid>\n                            <Border x:Name=\"SwitchBorder\" \n                            Width=\"44\" Height=\"24\" \n                            CornerRadius=\"12\"\n                            Background=\"{StaticResource BrushSurfaceDivider}\">\n                                <Grid>\n                                    <Ellipse x:Name=\"SwitchThumb\" \n                                     Width=\"20\" Height=\"20\" \n                                     Fill=\"{StaticResource BrushWhite}\"\n                                     HorizontalAlignment=\"Left\"\n                                     Margin=\"2,0,0,0\">\n                                        <Ellipse.Effect>\n                                            <DropShadowEffect ShadowDepth=\"1\" \n                                                    BlurRadius=\"2\" \n                                                    Opacity=\"0.2\" \n                                                    Direction=\"270\"/>\n                                        </Ellipse.Effect>\n                                    </Ellipse>\n                                </Grid>\n                            </Border>\n                        </Grid>\n                        <ControlTemplate.Triggers>\n                            <Trigger Property=\"IsChecked\" Value=\"True\">\n                                <Setter TargetName=\"SwitchBorder\" Property=\"Background\" Value=\"{StaticResource BrushAccentTeal}\"/>\n                                <Setter TargetName=\"SwitchThumb\" Property=\"HorizontalAlignment\" Value=\"Right\"/>\n                                <Setter TargetName=\"SwitchThumb\" Property=\"Margin\" Value=\"0,0,2,0\"/>\n                            </Trigger>\n                            <Trigger Property=\"IsEnabled\" Value=\"False\">\n                                <Setter TargetName=\"SwitchBorder\" Property=\"Opacity\" Value=\"0.5\"/>\n                                <Setter Property=\"Cursor\" Value=\"Arrow\"/>\n                            </Trigger>\n                        </ControlTemplate.Triggers>\n                    </ControlTemplate>\n                </Setter.Value>\n            </Setter>\n        </Style>\n        <!-- Modern Button Style -->\n        <Style x:Key=\"ModernButton\" TargetType=\"Button\">\n            <Setter Property=\"Cursor\" Value=\"Hand\"/>\n            <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLight}\"/>\n            <Setter Property=\"Foreground\" Value=\"{StaticResource BrushMutedRadioText}\"/>\n            <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderLight}\"/>\n            <Setter Property=\"BorderThickness\" Value=\"1\"/>\n            <Setter Property=\"Padding\" Value=\"16,8\"/>\n            <Setter Property=\"Template\">\n                <Setter.Value>\n                    <ControlTemplate TargetType=\"Button\">\n                        <Border Background=\"{TemplateBinding Background}\"\n                                BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                BorderThickness=\"{TemplateBinding BorderThickness}\"\n                                CornerRadius=\"6\">\n                            <ContentPresenter HorizontalAlignment=\"Center\" \n                                            VerticalAlignment=\"Center\"\n                                            Margin=\"{TemplateBinding Padding}\"/>\n                        </Border>\n                        <ControlTemplate.Triggers>\n                            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceHover}\"/>\n                            </Trigger>\n                            <Trigger Property=\"IsPressed\" Value=\"True\">\n                                <Setter Property=\"Background\" Value=\"{StaticResource BrushNeutralHover}\"/>\n                            </Trigger>\n                        </ControlTemplate.Triggers>\n                    </ControlTemplate>\n                </Setter.Value>\n            </Setter>\n        </Style>\n\n        <!-- Destructive Button Style -->\n        <Style x:Key=\"DestructiveButton\" TargetType=\"Button\" BasedOn=\"{StaticResource ModernButton}\">\n            <Setter Property=\"Cursor\" Value=\"Hand\"/>\n            <Setter Property=\"Background\" Value=\"{StaticResource BrushDanger}\"/>\n            <Setter Property=\"Foreground\" Value=\"{StaticResource BrushWhite}\"/>\n            <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushDanger}\"/>\n            <Setter Property=\"Template\">\n                <Setter.Value>\n                    <ControlTemplate TargetType=\"Button\">\n                        <Border Background=\"{TemplateBinding Background}\"\n                    BorderBrush=\"{TemplateBinding BorderBrush}\"\n                    BorderThickness=\"{TemplateBinding BorderThickness}\"\n                    CornerRadius=\"6\">\n                            <ContentPresenter HorizontalAlignment=\"Center\" \n                                VerticalAlignment=\"Center\"\n                                Margin=\"{TemplateBinding Padding}\"/>\n                        </Border>\n                        <ControlTemplate.Triggers>\n                            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                <Setter Property=\"Background\" Value=\"{StaticResource BrushDangerHover}\"/>\n                                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushDangerHover}\"/>\n                            </Trigger>\n                            <Trigger Property=\"IsPressed\" Value=\"True\">\n                                <Setter Property=\"Background\" Value=\"{StaticResource BrushDangerPressed}\"/>\n                                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushDangerPressed}\"/>\n                            </Trigger>\n                        </ControlTemplate.Triggers>\n                    </ControlTemplate>\n                </Setter.Value>\n            </Setter>\n        </Style>\n    </UserControl.Resources>\n\n    <!-- Main Background -->\n    <Grid Style=\"{StaticResource BackgroundColor2}\">\n        <ScrollViewer VerticalScrollBarVisibility=\"Auto\" HorizontalScrollBarVisibility=\"Disabled\">\n            <StackPanel Margin=\"24\" MaxWidth=\"800\">\n                \n                <!-- General Settings Card -->\n                <Border Style=\"{StaticResource ModernCard}\" Margin=\"0,0,0,16\">\n                    <Grid>\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"Auto\"/>\n                            <RowDefinition Height=\"*\"/>\n                        </Grid.RowDefinitions>\n                        \n                        <!-- Header -->\n                        <Border Grid.Row=\"0\" Style=\"{StaticResource CardHeaderStyle}\" \n                                CornerRadius=\"8,8,0,0\">\n                            <TextBlock Text=\"General Settings\" Style=\"{StaticResource TitleText}\" \n                                     Margin=\"24,0,0,0\"/>\n                        </Border>\n                        \n                        <!-- Content -->\n                        <StackPanel Grid.Row=\"1\" Margin=\"24,20\">\n                            <!-- Start on Windows Startup -->\n                            <Grid Margin=\"0,0,0,20\">\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"Auto\"/>\n                                </Grid.ColumnDefinitions>\n                                <StackPanel Grid.Column=\"0\">\n                                    <TextBlock Text=\"Start program on Windows startup\" \n                                             Style=\"{StaticResource PrimaryText}\"/>\n                                    <TextBlock Text=\"Launch automatically when Windows starts\" \n                                             Style=\"{StaticResource SecondaryText}\" Margin=\"0,2,0,0\"/>\n                                </StackPanel>\n                                <CheckBox Grid.Column=\"1\" Style=\"{StaticResource ModernToggleSwitch}\"\n                                        IsChecked=\"{Binding SetStartWithWin}\" \n                                        IsEnabled=\"{Binding UnlockOptionStartWin}\"\n                                        VerticalAlignment=\"Center\"/>\n                            </Grid>\n\n                            <!-- Minimize to Tray -->\n                            <Grid Margin=\"0,0,0,20\">\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"Auto\"/>\n                                </Grid.ColumnDefinitions>\n                                <StackPanel Grid.Column=\"0\">\n                                    <TextBlock Text=\"Minimize to system tray on start\" \n                                             Style=\"{StaticResource PrimaryText}\"/>\n                                    <TextBlock Text=\"Start minimized in the system tray\" \n                                             Style=\"{StaticResource SecondaryText}\" Margin=\"0,2,0,0\"/>\n                                </StackPanel>\n                                <CheckBox Grid.Column=\"1\" Style=\"{StaticResource ModernToggleSwitch}\"\n                                        IsChecked=\"{Binding MinimizeOnStart}\" \n                                        IsEnabled=\"{Binding UnlockMinimizeOnStart}\"\n                                        VerticalAlignment=\"Center\"/>\n                            </Grid>\n\n                            <!-- Dark Mode -->\n                            <Grid>\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"Auto\"/>\n                                </Grid.ColumnDefinitions>\n                                <StackPanel Grid.Column=\"0\">\n                                    <TextBlock Text=\"Dark mode\" Style=\"{StaticResource PrimaryText}\"/>\n                                    <TextBlock Text=\"Use dark theme for the interface\" \n                                             Style=\"{StaticResource SecondaryText}\" Margin=\"0,2,0,0\"/>\n                                </StackPanel>\n                                <CheckBox Grid.Column=\"1\" Style=\"{StaticResource ModernToggleSwitch}\"\n                                        IsChecked=\"{Binding DarkMode}\"\n                                        VerticalAlignment=\"Center\"/>\n                            </Grid>\n                        </StackPanel>\n                    </Grid>\n                </Border>\n\n                <!-- Mini Widget Card -->\n                <Border Style=\"{StaticResource ModernCard}\" Margin=\"0,0,0,16\">\n                    <Grid>\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"Auto\"/>\n                            <RowDefinition Height=\"*\"/>\n                        </Grid.RowDefinitions>\n                        \n                        <Border Grid.Row=\"0\" Style=\"{StaticResource CardHeaderStyle}\" \n                                CornerRadius=\"8,8,0,0\">\n                            <TextBlock Text=\"Mini Widget\" Style=\"{StaticResource TitleText}\" \n                                     Margin=\"24,0,0,0\"/>\n                        </Border>\n\n                        <StackPanel Grid.Row=\"1\" Margin=\"24,20\">\n                            <Grid>\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"Auto\"/>\n                                </Grid.ColumnDefinitions>\n                                <StackPanel Grid.Column=\"0\" Margin=\"0,0,16,0\">\n                                    <TextBlock Text=\"Show mini widget\" Style=\"{StaticResource PrimaryText}\"/>\n                                    <TextBlock Text=\"Toggle the mini widget visibility\" Style=\"{StaticResource SecondaryText}\" Margin=\"0,2,0,0\"/>\n                                </StackPanel>\n                                <CheckBox Grid.Column=\"1\"\n                                          Style=\"{StaticResource ModernToggleSwitch}\"\n                                          IsChecked=\"{Binding MiniWidgetVisibility}\"\n                                          VerticalAlignment=\"Center\"/>\n                            </Grid>\n\n                            <Border Height=\"1\" Background=\"{StaticResource BrushSurfaceDivider}\" Margin=\"0,20,0,20\"/>\n                            \n                            <Grid>\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition Width=\"Auto\"/>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"Auto\"/>\n                                </Grid.ColumnDefinitions>\n                                <TextBlock Grid.Column=\"0\" Text=\"Transparency\" Style=\"{StaticResource PrimaryText}\" \n                                     VerticalAlignment=\"Center\"/>\n                                <TextBlock Grid.Column=\"2\" VerticalAlignment=\"Center\" Style=\"{StaticResource SecondaryText}\">\n                                <Run Text=\"{Binding MiniWidgetTransparentSlider}\"/>\n                                <Run Text=\"%\"/>\n                                </TextBlock>\n                            </Grid>\n\n                            <Slider Margin=\"0,12,0,8\" \n                                    Minimum=\"0\" Maximum=\"100\" \n                                    Value=\"{Binding MiniWidgetTransparentSlider}\"\n                                    IsEnabled=\"{Binding MiniWidgetVisibility}\"\n                                    Style=\"{StaticResource ModernSlider}\"/>\n        \n                            <Grid>\n                                <TextBlock HorizontalAlignment=\"Left\" Text=\"0%\" Style=\"{StaticResource SecondaryText}\"/>\n                                <TextBlock HorizontalAlignment=\"Right\" Text=\"100%\" Style=\"{StaticResource SecondaryText}\"/>\n                            </Grid>\n\n                        </StackPanel>\n                    </Grid>\n                </Border>\n\n                <!-- Network Monitoring Card -->\n                <Border Style=\"{StaticResource ModernCard}\" Margin=\"0,0,0,16\">\n                    <Grid>\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"Auto\"/>\n                            <RowDefinition Height=\"*\"/>\n                        </Grid.RowDefinitions>\n                        \n                        <Border Grid.Row=\"0\" Style=\"{StaticResource CardHeaderStyle}\" \n                                CornerRadius=\"8,8,0,0\">\n                            <TextBlock Text=\"Network Monitoring\" Style=\"{StaticResource TitleText}\" \n                                     Margin=\"24,0,0,0\"/>\n                        </Border>\n                        \n                        <StackPanel Grid.Row=\"1\" Margin=\"24,20\">\n                            <!-- Monitor Networks Section -->\n                            <StackPanel Margin=\"0,0,0,20\">\n                                <TextBlock Text=\"Monitor these networks\" Style=\"{StaticResource PrimaryText}\" \n                                         Margin=\"0,0,0,8\"/>\n                                <TextBlock Text=\"(set to public for internet only)\" Style=\"{StaticResource SecondaryText}\" \n                                         Margin=\"0,0,0,12\"/>\n                                <StackPanel>\n                                    <RadioButton IsChecked=\"{Binding Path=NetworkTrafficType, Converter={StaticResource radioBoolToIntConverter}, ConverterParameter=0}\" \n                                               Content=\"Private\" Margin=\"0,0,0,8\" Style=\"{StaticResource ModernRadioButton}\"/>\n                                    <RadioButton IsChecked=\"{Binding Path=NetworkTrafficType, Converter={StaticResource radioBoolToIntConverter}, ConverterParameter=1}\" \n                                               Content=\"Public\" Margin=\"0,0,0,8\" Style=\"{StaticResource ModernRadioButton}\"/>\n                                    <RadioButton IsChecked=\"{Binding Path=NetworkTrafficType, Converter={StaticResource radioBoolToIntConverter}, ConverterParameter=2}\" \n                                               Content=\"Both\" Style=\"{StaticResource ModernRadioButton}\" />\n                                </StackPanel>\n                            </StackPanel>\n\n                            <!-- Separator -->\n                            <Border Height=\"1\" Background=\"{StaticResource BrushSurfaceDivider}\" Margin=\"0,4,0,20\"/>\n\n                            <!-- Network Speed Format -->\n                            <StackPanel>\n                                <TextBlock Text=\"Network speed format\" Style=\"{StaticResource PrimaryText}\" \n                                         Margin=\"0,0,0,12\"/>\n                                <StackPanel Orientation=\"Horizontal\">\n                                    <ComboBox Width=\"180\" Margin=\"0,0,16,0\"\n                                            SelectedValuePath=\"Tag\" \n                                            SelectedValue=\"{Binding NetworkSpeedMagnitude}\"\n                                            Style=\"{StaticResource ComboBoxFlatStyle}\">\n                                        <ComboBoxItem Tag=\"0\" Content=\"Auto\"/>\n                                        <ComboBoxItem Tag=\"1\" Content=\"Kilo\"/>\n                                        <ComboBoxItem Tag=\"2\" Content=\"Mega\"/>\n                                        <ComboBoxItem Tag=\"3\" Content=\"Giga\"/>\n                                    </ComboBox>\n                                    <ComboBox Width=\"180\"\n                                            SelectedValuePath=\"Tag\" \n                                            SelectedValue=\"{Binding NetworkSpeedFormat}\"\n                                            Style=\"{StaticResource ComboBoxFlatStyle}\">\n                                        <ComboBoxItem Tag=\"0\" Content=\"bps (bits/sec)\"/>\n                                        <ComboBoxItem Tag=\"1\" Content=\"Bps (bytes/sec)\"/>\n                                    </ComboBox>\n                                </StackPanel>\n                            </StackPanel>\n                        </StackPanel>\n                    </Grid>\n                </Border>\n\n                <!-- Data Management Card -->\n                <Border Style=\"{StaticResource ModernCard}\" Margin=\"0,0,0,16\">\n                    <Grid>\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"Auto\"/>\n                            <RowDefinition Height=\"*\"/>\n                        </Grid.RowDefinitions>\n                        \n                        <Border Grid.Row=\"0\" Style=\"{StaticResource CardHeaderStyle}\" \n                                CornerRadius=\"8,8,0,0\">\n                            <TextBlock Text=\"Data Management\" Style=\"{StaticResource TitleText}\" \n                                     Margin=\"24,0,0,0\"/>\n                        </Border>\n                        \n                        <StackPanel Grid.Row=\"1\" Margin=\"24,20\">\n                            <TextBlock Text=\"Delete all saved profiles\" Style=\"{StaticResource PrimaryText}\" \n                                     Margin=\"0,0,0,4\"/>\n                            <TextBlock Text=\"Remove all network usage history and profiles\" \n                                     Style=\"{StaticResource SecondaryText}\" Margin=\"0,0,0,16\"/>\n                            <Button Command=\"{Binding ResetBtn}\" Style=\"{StaticResource DestructiveButton}\"\n                                    HorizontalAlignment=\"Stretch\">\n                                <TextBlock Text=\"DELETE ALL\" FontWeight=\"SemiBold\"/>\n                            </Button>\n                        </StackPanel>\n                    </Grid>\n                </Border>\n\n                <!-- Updates Card -->\n                <Border Style=\"{StaticResource ModernCard}\">\n                    <Grid>\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"Auto\"/>\n                            <RowDefinition Height=\"*\"/>\n                        </Grid.RowDefinitions>\n                        \n                        <Border Grid.Row=\"0\" Style=\"{StaticResource CardHeaderStyle}\" \n                                CornerRadius=\"8,8,0,0\">\n                            <TextBlock Text=\"Updates\" Style=\"{StaticResource TitleText}\" \n                                     Margin=\"24,0,0,0\"/>\n                        </Border>\n\n                        <StackPanel Grid.Row=\"1\" Margin=\"24,20\">\n                            <TextBlock Text=\"Ensure you have the latest features and improvements\" \n                                     Style=\"{StaticResource SecondaryText}\" Margin=\"0,0,0,16\"/>\n                            \n                            <!-- Update Status Message -->\n                            <TextBlock Text=\"{Binding UpdateStatusMessage}\" \n                                     Style=\"{StaticResource SecondaryText}\"\n                                     HorizontalAlignment=\"Center\"\n                                     Margin=\"0,0,0,12\"/>\n                            \n                            <!-- Progress Bar -->\n                            <ProgressBar Height=\"4\" Margin=\"0,0,0,12\"\n                                       IsIndeterminate=\"True\">\n                                <ProgressBar.Style>\n                                    <Style TargetType=\"ProgressBar\">\n                                        <Setter Property=\"Visibility\" Value=\"Collapsed\"/>\n                                        <Style.Triggers>\n                                            <DataTrigger Binding=\"{Binding IsCheckingForUpdates}\" Value=\"True\">\n                                                <Setter Property=\"Visibility\" Value=\"Visible\"/>\n                                            </DataTrigger>\n                                        </Style.Triggers>\n                                    </Style>\n                                </ProgressBar.Style>\n                            </ProgressBar>\n                            \n                            <!-- Buttons -->\n                            <Grid>\n                                <Button Command=\"{Binding UpdateCheckBtn}\" \n                                        HorizontalAlignment=\"Stretch\">\n                                    <Button.Style>\n                                        <Style TargetType=\"Button\" BasedOn=\"{StaticResource ModernButton}\">\n                                            <Style.Triggers>\n                                                <DataTrigger Binding=\"{Binding IsUpdateAvailable}\" Value=\"True\">\n                                                    <Setter Property=\"Visibility\" Value=\"Collapsed\"/>\n                                                </DataTrigger>\n                                            </Style.Triggers>\n                                        </Style>\n                                    </Button.Style>\n                                    <TextBlock Text=\"Check for updates\"/>\n                                </Button>\n                                \n                                <Button Command=\"{Binding DownloadUpdateBtn}\"\n                                        HorizontalAlignment=\"Stretch\">\n                                    <Button.Style>\n                                        <Style TargetType=\"Button\" BasedOn=\"{StaticResource ModernButton}\">\n                                            <Setter Property=\"Visibility\" Value=\"Collapsed\"/>\n                                            <Style.Triggers>\n                                                <DataTrigger Binding=\"{Binding IsUpdateAvailable}\" Value=\"True\">\n                                                    <Setter Property=\"Visibility\" Value=\"Visible\"/>\n                                                </DataTrigger>\n                                            </Style.Triggers>\n                                        </Style>\n                                    </Button.Style>\n                                    <TextBlock Text=\"Download\"/>\n                                </Button>\n                            </Grid>\n                        </StackPanel>\n                    </Grid>\n                </Border>\n            </StackPanel>\n        </ScrollViewer>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MainWindowTabs/SettingsV.xaml.cs",
    "content": "﻿using System.Windows.Controls;\n\nnamespace OpenNetMeter.Views\n{\n    /// <summary>\n    /// Interaction logic for SettingsV.xaml\n    /// </summary>\n    public partial class SettingsV : UserControl\n    {\n        public SettingsV()\n        {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MiniWidgetV.xaml",
    "content": "﻿<Window x:Class=\"OpenNetMeter.Views.MiniWidgetV\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:vm=\"clr-namespace:OpenNetMeter.ViewModels\"\n        xmlns:v=\"clr-namespace:OpenNetMeter.Views\"\n        xmlns:res=\"clr-namespace:OpenNetMeter.Properties\"\n        mc:Ignorable=\"d\"\n        WindowStyle=\"None\"\n        AllowsTransparency=\"True\"\n        ResizeMode=\"NoResize\"\n        Topmost=\"True\"\n        MouseLeftButtonDown=\"Window_MouseLeftButtonDown\"\n        LocationChanged=\"Window_LocationChanged\"\n        Width=\"{Binding Width, Mode=TwoWay}\"\n        Height=\"{Binding Height, Mode=TwoWay}\"\n        Background=\"{StaticResource BrushWindowOverlay}\"\n        ShowInTaskbar=\"False\">\n    <WindowChrome.WindowChrome>\n        <WindowChrome CaptionHeight=\"0\" GlassFrameThickness=\"1\" CornerRadius=\"0\" />\n    </WindowChrome.WindowChrome>\n    <Window.Resources>\n        <v:UnitConverterBytes x:Key=\"converterBytes\"/>\n        <v:NetSpeedFormatConverter x:Key=\"netSpeedFormatConverter\"/>\n    </Window.Resources>\n\n    <Border Style=\"{StaticResource BorderColor}\">\n        <Grid Background=\"{Binding BackgroundColor}\">\n            <Grid.ColumnDefinitions>\n                <ColumnDefinition Width=\"*\" />\n                <ColumnDefinition Width=\"*\"/>\n                <ColumnDefinition Width=\"Auto\"/>\n            </Grid.ColumnDefinitions>\n            <Grid.RowDefinitions>\n                <RowDefinition Height=\"Auto\"/>\n            </Grid.RowDefinitions>\n            <Grid.ContextMenu>\n                <ContextMenu Style=\"{StaticResource miniWidgetContextMenuColor}\">\n                    <MenuItem Header=\"Open\" Click=\"MenuItem_Open_Click\"/>\n                    <MenuItem Header=\"Hide\" Click=\"MenuItem_Hide_Click\"/>\n                </ContextMenu>\n            </Grid.ContextMenu>\n\n            <!-- Download Section -->\n            <StackPanel Grid.Row=\"0\" Grid.Column=\"0\" Orientation=\"Horizontal\" \n                       HorizontalAlignment=\"Left\" VerticalAlignment=\"Center\" \n                       Margin=\"2,2\">\n                <!-- Download Icon Background -->\n                <Border Width=\"20\" Height=\"24\" \n                       Background=\"{StaticResource BrushDownloadBadgeOverlay}\"\n                       CornerRadius=\"4\"\n                       Margin=\"0,0,6,0\"\n                       VerticalAlignment=\"Center\">\n                    <TextBlock Text=\"↓\" \n                              FontSize=\"16\" \n                              FontWeight=\"Bold\"\n                              Style=\"{StaticResource DownloadForeground}\"\n                              HorizontalAlignment=\"Center\" \n                              VerticalAlignment=\"Center\"/>\n                </Border>\n\n                <!-- Download Speed and Total -->\n                <StackPanel Orientation=\"Vertical\" VerticalAlignment=\"Center\">\n                    <!-- Download Speed -->\n                    <TextBlock FontSize=\"11\" \n                        Style=\"{StaticResource FontColor1}\" \n                        Text=\"{Binding DownloadSpeed, Converter={StaticResource netSpeedFormatConverter}}\"\n                        VerticalAlignment=\"Center\"/>\n\n                    <!-- Download Total -->\n                    <StackPanel Orientation=\"Horizontal\" Margin=\"0,1,0,0\">\n                        <TextBlock Text=\"Total:\"\n                                   FontSize=\"11\"\n                                  Style=\"{StaticResource DownloadForeground}\"\n                                  Margin=\"0,0,3,0\"\n                                  VerticalAlignment=\"Center\"/>\n                        <TextBlock FontSize=\"11\" \n                                  Text=\"{Binding CurrentSessionDownloadData, Converter={StaticResource converterBytes}}\"\n                                  Style=\"{StaticResource FontColor1}\" \n                                  VerticalAlignment=\"Center\"/>\n                    </StackPanel>\n                </StackPanel>\n            </StackPanel>\n\n            <!-- Upload Section -->\n            <StackPanel Grid.Row=\"1\" Grid.Column=\"1\" Orientation=\"Horizontal\" \n                       HorizontalAlignment=\"Left\" VerticalAlignment=\"Center\" \n                       Margin=\"2,2\">\n                <!-- Upload Icon Background -->\n                <Border Width=\"20\" Height=\"24\" \n                       Background=\"{StaticResource BrushUploadBadgeOverlay}\"\n                       CornerRadius=\"4\"\n                       Margin=\"0,0,6,0\"\n                       VerticalAlignment=\"Center\">\n                    <TextBlock Text=\"↑\" \n                              FontSize=\"16\" \n                              FontWeight=\"Bold\"\n                              Style=\"{StaticResource UploadForeground}\"\n                              HorizontalAlignment=\"Center\" \n                              VerticalAlignment=\"Center\"/>\n                </Border>\n\n                <!-- Upload Speed and Total -->\n                <StackPanel Orientation=\"Vertical\" VerticalAlignment=\"Center\">\n                    <!-- Upload Speed -->\n                    <TextBlock FontSize=\"11\" \n                      Style=\"{StaticResource FontColor1}\" \n                      Text=\"{Binding UploadSpeed, Converter={StaticResource netSpeedFormatConverter}}\"\n                      VerticalAlignment=\"Center\"/>\n                    \n                    <!-- Upload Total -->\n                    <StackPanel Orientation=\"Horizontal\" Margin=\"0,1,0,0\">\n                        <TextBlock Text=\"Total:\" \n                                   FontSize=\"11\"\n                                  Style=\"{StaticResource UploadForeground}\"\n                                  Margin=\"0,0,3,0\"\n                                  VerticalAlignment=\"Center\"/>\n                        <TextBlock FontSize=\"11\"\n                                  Text=\"{Binding CurrentSessionUploadData, Converter={StaticResource converterBytes}}\"\n                                  Style=\"{StaticResource FontColor1}\" \n                                  VerticalAlignment=\"Center\"/>\n                    </StackPanel>\n                </StackPanel>\n            </StackPanel>\n            \n            <!-- Pin Button -->\n            <ToggleButton Grid.Row=\"0\"\n                  Grid.Column=\"2\"\n                  Width=\"18\"\n                  Height=\"18\"\n                  IsChecked=\"{Binding IsPinned, Mode=TwoWay}\"\n                          Margin=\"2\"\n                          VerticalAlignment=\"Top\"\n                  >\n                <ToggleButton.Style>\n                    <Style TargetType=\"ToggleButton\" BasedOn=\"{StaticResource FontColor1}\">\n                        <Setter Property=\"Background\" Value=\"Transparent\"/>\n                        <Setter Property=\"BorderBrush\" Value=\"{Binding RelativeSource={RelativeSource Self}, Path=Foreground}\"/>\n                        <Setter Property=\"BorderThickness\" Value=\"1\"/>\n                        <Setter Property=\"Cursor\" Value=\"Hand\"/>\n                        <Setter Property=\"ToolTipService.ToolTip\" Value=\"Pin\"/>\n                        <Setter Property=\"Template\">\n                            <Setter.Value>\n                                <ControlTemplate TargetType=\"ToggleButton\">\n                                    <Border x:Name=\"PinBorder\"\n                                    Background=\"{TemplateBinding Background}\"\n                                    BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                    BorderThickness=\"{TemplateBinding BorderThickness}\"\n                                    CornerRadius=\"2\"\n                                    Padding=\"1\">\n                                        <Image x:Name=\"PinStateImage\"\n                                       Width=\"12\"\n                                       Height=\"12\">\n                                            <Image.Style>\n                                                <Style TargetType=\"Image\">\n                                                    <Setter Property=\"Source\" Value=\"/OpenNetMeter;component/Resources/pin/pin.png\"/>\n                                                    <Style.Triggers>\n                                                        <DataTrigger Binding=\"{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ToggleButton}}\" Value=\"True\">\n                                                            <Setter Property=\"Source\" Value=\"/OpenNetMeter;component/Resources/pin/unpin.png\"/>\n                                                        </DataTrigger>\n                                                        <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                                                            <Setter Property=\"Source\" Value=\"/OpenNetMeter;component/Resources/pin/pin-dark.png\"/>\n                                                        </DataTrigger>\n                                                        <MultiDataTrigger>\n                                                            <MultiDataTrigger.Conditions>\n                                                                <Condition Binding=\"{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ToggleButton}}\" Value=\"True\"/>\n                                                                <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\"/>\n                                                            </MultiDataTrigger.Conditions>\n                                                            <Setter Property=\"Source\" Value=\"/OpenNetMeter;component/Resources/pin/unpin-dark.png\"/>\n                                                        </MultiDataTrigger>\n                                                    </Style.Triggers>\n                                                </Style>\n                                            </Image.Style>\n                                        </Image>\n                                    </Border>\n                                </ControlTemplate>\n                            </Setter.Value>\n                        </Setter>\n                        <Style.Triggers>\n                            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                <Setter Property=\"Background\" Value=\"{StaticResource BrushPinHover}\"/>\n                            </Trigger>\n                            <Trigger Property=\"IsChecked\" Value=\"True\">\n                                <Setter Property=\"Background\" Value=\"{StaticResource BrushPinActive}\"/>\n                                <Setter Property=\"ToolTipService.ToolTip\" Value=\"Unpin\"/>\n                            </Trigger>\n                        </Style.Triggers>\n                    </Style>\n                </ToggleButton.Style>\n            </ToggleButton>\n\n\n        </Grid>\n    </Border>\n</Window>\n\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/MiniWidgetV.xaml.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Windows;\nusing System.Windows.Interop;\nusing System.Windows.Threading;\nusing OpenNetMeter.Models;\nusing OpenNetMeter.Properties;\nusing OpenNetMeter.ViewModels;\n\nnamespace OpenNetMeter.Views\n{\n    /// <summary>\n    /// Interaction logic for MiniWidgetV.xaml\n    /// </summary>\n    public partial class MiniWidgetV : Window\n    {\n        private DispatcherTimer fixZorderTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 200), IsEnabled = false };\n        private DispatcherTimer relocationTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 200), IsEnabled = false };\n\n        private Window mainWindow;\n        public MiniWidgetV(Window mainWindow_ref)\n        {\n            InitializeComponent();\n            DataContext = new MiniWidgetVM();\n\n            fixZorderTimer.Tick += FixZorderTimer_Tick;\n            relocationTimer.Tick += RelocationTimer_Tick;\n\n            mainWindow = mainWindow_ref;\n\n            this.Visibility = Visibility.Visible;\n            fixZorderTimer.IsEnabled = true;\n\n            Loaded += MiniWidgetV_Loaded;\n        }\n\n        private void MiniWidgetV_Loaded(object sender, RoutedEventArgs e)\n        {\n            if (!SettingsManager.Current.MiniWidgetVisibility)\n            {\n                this.Visibility = Visibility.Hidden;\n                fixZorderTimer.IsEnabled = false;\n            }\n        }\n\n        private void FixZorderTimer_Tick(object? sender, EventArgs e)\n        {\n            const int HWND_TOPMOST = -1;\n            //const int HWND_NOTOPMOST = -2;\n            const string SHELLTRAY = \"Shell_traywnd\";\n\n            WindowInteropHelper thisWin = new WindowInteropHelper(this);\n            IntPtr shellTray = NativeMethods.GetWindowByClassName(IntPtr.Zero ,SHELLTRAY);\n\n            //Reassign owner when explorer.exe restarts\n            if (thisWin.Owner != shellTray)\n            {\n                Debug.WriteLine(\"set owner again\");\n                thisWin.Owner = shellTray;\n            }\n\n            //check if window is behind the taskbar. If yes, bring it in front without activating it.\n            for (IntPtr h = thisWin.Handle; h != IntPtr.Zero; h = NativeMethods.GetWindow(h, (uint)NativeMethods.GW.HWNDPREV))\n            {\n                if (h == shellTray)\n                {\n                    Debug.WriteLine(\"this window is behind Shell_TrayWnd\");\n                    NativeMethods.SetWindowPos(thisWin.Handle, (IntPtr)HWND_TOPMOST, 0, 0, 0, 0, \n                        (int)NativeMethods.SWP.ASYNCWINDOWPOS |\n                        (int)NativeMethods.SWP.NOACTIVATE |\n                        (int)NativeMethods.SWP.NOMOVE |\n                        (int)NativeMethods.SWP.NOSIZE);\n                    //NativeMethods.SetWindowPos(thisWin.Handle, (IntPtr)HWND_NOTOPMOST, 0, 0, 0, 0, SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);\n                    break;\n                }\n            }\n        }\n\n        private void Window_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)\n        {\n            if (DataContext is MiniWidgetVM vm && vm.IsPinned)\n            {\n                return;\n            }\n\n            this.DragMove();\n        }\n\n        private void MenuItem_Hide_Click(object sender, RoutedEventArgs e)\n        {\n            if (mainWindow is MainWindow mw) // hide from miniwidget\n                mw.HideMiniWidget();\n            else // hide from settings page\n                HideMiniWidget();\n        }\n        private void MenuItem_Open_Click(object sender, RoutedEventArgs e)\n        {\n            if (mainWindow != null)\n            {\n                mainWindow.Visibility = Visibility.Visible;\n                mainWindow.Activate();\n            }\n        }\n\n        /// <summary>\n        /// Save window position when user moves window\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private void RelocationTimer_Tick(object? sender, EventArgs e)\n        {\n            relocationTimer.IsEnabled = false;\n            //Do end of relocation processing\n            SaveWinPos((int)this.Left, (int)this.Top);\n        }\n\n        public void SaveWinPos(int x, int y)\n        {\n            SettingsManager.Current.MiniWidgetPos = new System.Drawing.Point(x, y);\n            SettingsManager.Save();\n        }\n\n        private void Window_LocationChanged(object sender, System.EventArgs e)\n        {\n            relocationTimer.IsEnabled = true;\n            relocationTimer.Stop();\n            relocationTimer.Start();\n        }\n\n        public void ShowMiniWidget()\n        {\n            this.Visibility = Visibility.Visible;\n            this.Activate();\n            fixZorderTimer.IsEnabled = true;\n\n            SettingsManager.Current.MiniWidgetVisibility = true;\n            SettingsManager.Save();\n        }\n\n        public void HideMiniWidget()\n        {\n            this.Visibility = Visibility.Hidden;\n            fixZorderTimer.IsEnabled = false;\n\n            SettingsManager.Current.MiniWidgetVisibility = false;\n            SettingsManager.Save();\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/CustomDatePicker.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:settings=\"clr-namespace:OpenNetMeter.Properties\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Theme.Colors.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n\n    <!-- Modern DatePicker Button Style -->\n    <Style x:Key=\"ModernDropDownButtonStyle\" TargetType=\"Button\">\n        <!-- light mode defaults -->\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLight}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderStrong}\"/>\n        <Setter Property=\"BorderThickness\" Value=\"1\"/>\n        <Setter Property=\"Padding\" Value=\"8,4\"/>\n        <Setter Property=\"Cursor\" Value=\"Hand\"/>\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type Button}\">\n                    <Border x:Name=\"ButtonBorder\"\n                        CornerRadius=\"6\"\n                        Background=\"{TemplateBinding Background}\"\n                        BorderBrush=\"{TemplateBinding BorderBrush}\"\n                        BorderThickness=\"{TemplateBinding BorderThickness}\"\n                        Padding=\"{TemplateBinding Padding}\">\n                        <Grid>\n                            <!-- Date Text -->\n                            <TextBlock\n                            Text=\"{Binding SelectedDate,\n                                           RelativeSource={RelativeSource AncestorType=DatePicker},\n                                           StringFormat='MMM dd, yyyy'}\"\n                            FontSize=\"12\"\n                            VerticalAlignment=\"Center\">\n                                <TextBlock.Style>\n                                    <Style TargetType=\"TextBlock\">\n                                        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                                        <Style.Triggers>\n                                            <!-- Dark mode text color -->\n                                            <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\"\n                                                     Value=\"True\">\n                                                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushWhiteSmoke}\"/>\n                                            </DataTrigger>\n                                        </Style.Triggers>\n                                    </Style>\n                                </TextBlock.Style>\n                            </TextBlock>\n                        </Grid>\n                    </Border>\n\n                    <!-- Hover / Press / Dark-mode background -->\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                            <Setter TargetName=\"ButtonBorder\" Property=\"Opacity\" Value=\"0.9\"/>\n                        </Trigger>\n                        <Trigger Property=\"IsPressed\" Value=\"True\">\n                            <Setter TargetName=\"ButtonBorder\" Property=\"Opacity\" Value=\"0.8\"/>\n                        </Trigger>\n\n                        <!-- Dark mode background + border -->\n                        <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\"\n                                 Value=\"True\">\n                            <Setter Property=\"Background\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                            <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderLighter}\"/>\n                        </DataTrigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <!-- Modern Calendar Day Button Style -->\n    <Style TargetType=\"CalendarDayButton\" x:Key=\"ModernCalendarDayButtonStyle\">\n        <Setter Property=\"MinWidth\" Value=\"18\"/>\n        <Setter Property=\"MinHeight\" Value=\"18\"/>\n        <Setter Property=\"Padding\" Value=\"0\"/>\n        <Setter Property=\"FontSize\" Value=\"12\"/>\n        <Setter Property=\"HorizontalContentAlignment\" Value=\"Center\"/>\n        <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"CalendarDayButton\">\n                    <Grid>\n                        <VisualStateManager.VisualStateGroups>\n                            <VisualStateGroup Name=\"CommonStates\">\n                                <VisualState Name=\"Normal\"/>\n                                <VisualState Name=\"MouseOver\">\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"HoverBackground\"\n                                                       Storyboard.TargetProperty=\"(Border.Background).(SolidColorBrush.Color)\"\n                                                       To=\"{StaticResource ColorAccentOrange}\"\n                                                       Duration=\"0:0:0.1\"/>\n                                    </Storyboard>\n                                </VisualState>\n                                <VisualState Name=\"Pressed\">\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"HoverBackground\"\n                                                       Storyboard.TargetProperty=\"(Border.Background).(SolidColorBrush.Color)\"\n                                                       To=\"{StaticResource ColorAccentTealMuted}\"\n                                                       Duration=\"0\"/>\n                                    </Storyboard>\n                                </VisualState>\n                                <VisualState Name=\"Disabled\">\n                                    <Storyboard>\n                                        <DoubleAnimation Storyboard.TargetName=\"NormalText\"\n                                                        Storyboard.TargetProperty=\"Opacity\"\n                                                        To=\"0.3\"\n                                                        Duration=\"0\"/>\n                                    </Storyboard>\n                                </VisualState>\n                            </VisualStateGroup>\n                            <VisualStateGroup Name=\"SelectionStates\">\n                                <VisualState Name=\"Unselected\"/>\n                                <VisualState Name=\"Selected\">\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"SelectedBackground\"\n                                                       Storyboard.TargetProperty=\"(Border.Background).(SolidColorBrush.Color)\"\n                                                       To=\"{StaticResource ColorAccentPeachSelected}\"\n                                                       Duration=\"0\"/>\n                                        <ColorAnimation Storyboard.TargetName=\"NormalText\"\n                                                       Storyboard.TargetProperty=\"(TextBlock.Foreground).(SolidColorBrush.Color)\"\n                                                       To=\"{StaticResource ColorPrimaryText}\"\n                                                       Duration=\"0\"/>\n                                    </Storyboard>\n                                </VisualState>\n                            </VisualStateGroup>\n                            <VisualStateGroup Name=\"ActiveStates\">\n                                <VisualState Name=\"Active\"/>\n                                <VisualState Name=\"Inactive\">\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"NormalText\"\n                                                       Storyboard.TargetProperty=\"(TextBlock.Foreground).(SolidColorBrush.Color)\"\n                                                       To=\"{StaticResource ColorBorderLight}\"\n                                                       Duration=\"0\"/>\n                                        <DoubleAnimation Storyboard.TargetName=\"NormalText\"\n                                                        Storyboard.TargetProperty=\"Opacity\"\n                                                        To=\"0.5\"\n                                                        Duration=\"0\"/>\n                                    </Storyboard>\n                                </VisualState>\n                            </VisualStateGroup>\n                            <VisualStateGroup Name=\"DayStates\">\n                                <VisualState Name=\"RegularDay\"/>\n                                <VisualState Name=\"Today\">\n                                    <Storyboard>\n                                        <ColorAnimation Storyboard.TargetName=\"TodayBackground\"\n                                                       Storyboard.TargetProperty=\"(Border.Background).(SolidColorBrush.Color)\"\n                                                       To=\"{StaticResource ColorLightGray}\"\n                                                       Duration=\"0\"/>\n                                        <ColorAnimation Storyboard.TargetName=\"NormalText\"\n                                                       Storyboard.TargetProperty=\"(TextBlock.Foreground).(SolidColorBrush.Color)\"\n                                                       To=\"{StaticResource ColorHeaderDeep}\"\n                                                       Duration=\"0\"/>\n                                    </Storyboard>\n                                </VisualState>\n                            </VisualStateGroup>\n                        </VisualStateManager.VisualStateGroups>\n\n                        <!-- Today Background -->\n                        <Border x:Name=\"TodayBackground\"\n                               Background=\"Transparent\"\n                               CornerRadius=\"4\"/>\n\n                        <!-- Selected Background -->\n                        <Border x:Name=\"SelectedBackground\"\n                               Background=\"Transparent\"\n                               CornerRadius=\"4\">\n                            <Border.Style>\n                                <Style TargetType=\"Border\">\n                                    <Setter Property=\"Background\" Value=\"Transparent\"/>\n                                </Style>\n                            </Border.Style>\n                        </Border>\n\n                        <!-- Hover Background -->\n                        <Border x:Name=\"HoverBackground\"\n                               Background=\"Transparent\"\n                               CornerRadius=\"4\"/>\n\n                        <!-- Day Number -->\n                        <TextBlock x:Name=\"NormalText\"\n                                  Text=\"{TemplateBinding Content}\"\n                                  FontSize=\"11\"\n                                  HorizontalAlignment=\"Center\"\n                                  VerticalAlignment=\"Center\">\n                            <TextBlock.Style>\n                                <Style TargetType=\"TextBlock\">\n                                    <Setter Property=\"Foreground\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                                    <Style.Triggers>\n                                        <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                                            <Setter Property=\"Foreground\" Value=\"{StaticResource BrushWhiteSmoke}\"/>\n                                        </DataTrigger>\n                                    </Style.Triggers>\n                                </Style>\n                            </TextBlock.Style>\n                        </TextBlock>\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <!-- Previous Button Template -->\n    <ControlTemplate x:Key=\"ModernPreviousButtonTemplate\" TargetType=\"{x:Type Button}\">\n        <Border x:Name=\"NavBorder\"\n            Background=\"Transparent\"\n            CornerRadius=\"4\"\n            Width=\"28\"\n            Height=\"28\"\n            Padding=\"6\"\n            Cursor=\"Hand\">\n            <Path x:Name=\"NavArrow\"\n              Stretch=\"Uniform\"\n              Data=\"M14,8 L6,14 M6,14 L14,20\">\n                <Path.Style>\n                    <Style TargetType=\"Path\">\n                        <Setter Property=\"Stroke\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                        <Setter Property=\"StrokeThickness\" Value=\"2.4\"/>\n                        <Setter Property=\"StrokeLineJoin\" Value=\"Round\"/>\n                        <Setter Property=\"StrokeStartLineCap\" Value=\"Round\"/>\n                        <Setter Property=\"StrokeEndLineCap\" Value=\"Round\"/>\n                        <Style.Triggers>\n                            <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\"\n                                     Value=\"True\">\n                                <Setter Property=\"Stroke\" Value=\"{StaticResource BrushWhiteSmoke}\"/>\n                            </DataTrigger>\n                        </Style.Triggers>\n                    </Style>\n                </Path.Style>\n            </Path>\n        </Border>\n        <ControlTemplate.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                <Setter TargetName=\"NavBorder\" Property=\"Background\" Value=\"{StaticResource BrushAccentOrange}\"/>\n                <Setter TargetName=\"NavBorder\" Property=\"Opacity\" Value=\"0.3\"/>\n            </Trigger>\n            <Trigger Property=\"IsPressed\" Value=\"True\">\n                <Setter TargetName=\"NavBorder\" Property=\"Background\" Value=\"{StaticResource BrushAccentOrange}\"/>\n                <Setter TargetName=\"NavBorder\" Property=\"Opacity\" Value=\"0.5\"/>\n            </Trigger>\n        </ControlTemplate.Triggers>\n    </ControlTemplate>\n\n    <!-- Next Button Template -->\n    <ControlTemplate x:Key=\"ModernNextButtonTemplate\" TargetType=\"{x:Type Button}\">\n        <Border x:Name=\"NavBorder\"\n            Background=\"Transparent\"\n            CornerRadius=\"4\"\n            Width=\"28\"\n            Height=\"28\"\n            Padding=\"6\"\n            Cursor=\"Hand\">\n            <Path x:Name=\"NavArrow\"\n              Stretch=\"Uniform\"\n              Data=\"M6,8 L14,14 M14,14 L6,20\">\n                <Path.Style>\n                    <Style TargetType=\"Path\">\n                        <Setter Property=\"Stroke\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                        <Setter Property=\"StrokeThickness\" Value=\"2.4\"/>\n                        <Setter Property=\"StrokeLineJoin\" Value=\"Round\"/>\n                        <Setter Property=\"StrokeStartLineCap\" Value=\"Round\"/>\n                        <Setter Property=\"StrokeEndLineCap\" Value=\"Round\"/>\n                        <Style.Triggers>\n                            <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\"\n                                     Value=\"True\">\n                                <Setter Property=\"Stroke\" Value=\"{StaticResource BrushWhiteSmoke}\"/>\n                            </DataTrigger>\n                        </Style.Triggers>\n                    </Style>\n                </Path.Style>\n            </Path>\n        </Border>\n        <ControlTemplate.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                <Setter TargetName=\"NavBorder\" Property=\"Background\" Value=\"{StaticResource BrushAccentOrange}\"/>\n                <Setter TargetName=\"NavBorder\" Property=\"Opacity\" Value=\"0.3\"/>\n            </Trigger>\n            <Trigger Property=\"IsPressed\" Value=\"True\">\n                <Setter TargetName=\"NavBorder\" Property=\"Background\" Value=\"{StaticResource BrushAccentOrange}\"/>\n                <Setter TargetName=\"NavBorder\" Property=\"Opacity\" Value=\"0.5\"/>\n            </Trigger>\n        </ControlTemplate.Triggers>\n    </ControlTemplate>\n\n    <!-- Header Button Template -->\n    <ControlTemplate x:Key=\"ModernHeaderButtonTemplate\" TargetType=\"{x:Type Button}\">\n        <Border Background=\"Transparent\" Cursor=\"Hand\">\n            <TextBlock x:Name=\"HeaderText\"\n                      Text=\"{TemplateBinding Content}\"\n                      FontSize=\"13\"\n                      FontWeight=\"SemiBold\"\n                      HorizontalAlignment=\"Center\"\n                      VerticalAlignment=\"Center\"\n                      Margin=\"8,0\">\n                <TextBlock.Style>\n                    <Style TargetType=\"TextBlock\">\n                        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                        <Style.Triggers>\n                            <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushWhiteSmoke}\"/>\n                            </DataTrigger>\n                        </Style.Triggers>\n                    </Style>\n                </TextBlock.Style>\n            </TextBlock>\n            <Border.Style>\n                <Style TargetType=\"Border\">\n                    <Style.Triggers>\n                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                            <Setter Property=\"Background\" Value=\"{StaticResource BrushTransparentWhite}\"/>\n                        </Trigger>\n                    </Style.Triggers>\n                </Style>\n            </Border.Style>\n        </Border>\n    </ControlTemplate>\n\n    <!-- Calendar Item Style -->\n    <Style x:Key=\"ModernCalendarItemStyle\" TargetType=\"{x:Type CalendarItem}\">\n        <Setter Property=\"Margin\" Value=\"0\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type CalendarItem}\">\n                    <ControlTemplate.Resources>\n                        <DataTemplate x:Key=\"{x:Static CalendarItem.DayTitleTemplateResourceKey}\">\n                            <TextBlock Text=\"{Binding}\"\n                                      FontSize=\"11\"\n                                      FontWeight=\"SemiBold\"\n                                      HorizontalAlignment=\"Center\"\n                                      VerticalAlignment=\"Center\"\n                                      Margin=\"0,8,0,4\">\n                                <TextBlock.Style>\n                                    <Style TargetType=\"TextBlock\">\n                                        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushMutedText}\"/>\n                                        <Style.Triggers>\n                                            <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                                                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushMutedTextAlt}\"/>\n                                            </DataTrigger>\n                                        </Style.Triggers>\n                                    </Style>\n                                </TextBlock.Style>\n                            </TextBlock>\n                        </DataTemplate>\n                    </ControlTemplate.Resources>\n\n                    <Border Padding=\"0\">\n                        <Border.Style>\n                            <Style TargetType=\"Border\">\n                                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLight}\"/>\n                                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                                <Setter Property=\"BorderThickness\" Value=\"1\"/>\n                                <Setter Property=\"CornerRadius\" Value=\"8\"/>\n                                <Style.Triggers>\n                                    <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                                        <Setter Property=\"Background\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                                        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderLighter}\"/>\n                                    </DataTrigger>\n                                </Style.Triggers>\n                            </Style>\n                        </Border.Style>\n                        <Grid>\n                            <Grid.RowDefinitions>\n                                <RowDefinition Height=\"Auto\"/>\n                                <RowDefinition Height=\"*\"/>\n                            </Grid.RowDefinitions>\n\n                            <!-- Gradient Header -->\n                            <Border Grid.Row=\"0\"\n                                   CornerRadius=\"8,8,0,0\"\n                                   Height=\"28\">\n                                <Border.Style>\n                                    <Style TargetType=\"Border\">\n                                        <Setter Property=\"Background\">\n                                            <Setter.Value>\n                                                <LinearGradientBrush StartPoint=\"0,0\" EndPoint=\"0,1\">\n                                                    <GradientStop Color=\"{StaticResource ColorAccentGreenAlt2}\" Offset=\"0\"/>\n                                                    <GradientStop Color=\"{StaticResource ColorAccentGreenAlt1}\" Offset=\"1\"/>\n                                                </LinearGradientBrush>\n                                            </Setter.Value>\n                                        </Setter>\n                                        <Style.Triggers>\n                                            <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                                                <Setter Property=\"Background\">\n                                                    <Setter.Value>\n                                                        <LinearGradientBrush StartPoint=\"0,0\" EndPoint=\"0,1\">\n                                                            <GradientStop Color=\"{StaticResource ColorAccentGreenAlt3}\" Offset=\"0\"/>\n                                                            <GradientStop Color=\"{StaticResource ColorAccentGreenAlt4}\" Offset=\"1\"/>\n                                                        </LinearGradientBrush>\n                                                    </Setter.Value>\n                                                </Setter>\n                                            </DataTrigger>\n                                        </Style.Triggers>\n                                    </Style>\n                                </Border.Style>\n\n                                <Grid>\n                                    <Grid.ColumnDefinitions>\n                                        <ColumnDefinition Width=\"Auto\"/>\n                                        <ColumnDefinition Width=\"*\"/>\n                                        <ColumnDefinition Width=\"Auto\"/>\n                                    </Grid.ColumnDefinitions>\n\n                                    <!-- Previous Button -->\n                                    <Button x:Name=\"PART_PreviousButton\"\n                                           Grid.Column=\"0\"\n                                           Margin=\"8,0,0,0\"\n                                           Template=\"{StaticResource ModernPreviousButtonTemplate}\"\n                                           Focusable=\"False\"\n                                           HorizontalAlignment=\"Left\"\n                                           VerticalAlignment=\"Center\"/>\n\n                                    <!-- Header Button -->\n                                    <Button x:Name=\"PART_HeaderButton\"\n                                           Grid.Column=\"1\"\n                                           Template=\"{StaticResource ModernHeaderButtonTemplate}\"\n                                           Focusable=\"False\"\n                                           HorizontalAlignment=\"Center\"\n                                           VerticalAlignment=\"Center\"/>\n\n                                    <!-- Next Button -->\n                                    <Button x:Name=\"PART_NextButton\"\n                                           Grid.Column=\"2\"\n                                           Margin=\"0,0,8,0\"\n                                           Template=\"{StaticResource ModernNextButtonTemplate}\"\n                                           Focusable=\"False\"\n                                           HorizontalAlignment=\"Right\"\n                                           VerticalAlignment=\"Center\"/>\n                                </Grid>\n                            </Border>\n\n                            <!-- Calendar Grid -->\n                            <Grid x:Name=\"PART_MonthView\"\n                                 Grid.Row=\"1\"\n                                 Margin=\"2\"\n                                 Visibility=\"Visible\">\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"*\"/>\n                                </Grid.ColumnDefinitions>\n                                <Grid.RowDefinitions>\n                                    <RowDefinition Height=\"Auto\"/>\n                                    <RowDefinition Height=\"*\"/>\n                                    <RowDefinition Height=\"*\"/>\n                                    <RowDefinition Height=\"*\"/>\n                                    <RowDefinition Height=\"*\"/>\n                                    <RowDefinition Height=\"*\"/>\n                                    <RowDefinition Height=\"*\"/>\n                                </Grid.RowDefinitions>\n                            </Grid>\n\n                            <Grid x:Name=\"PART_YearView\"\n                                 Grid.Row=\"1\"\n                                 Margin=\"12\"\n                                 Visibility=\"Hidden\">\n                                <Grid.ColumnDefinitions>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"*\"/>\n                                    <ColumnDefinition Width=\"*\"/>\n                                </Grid.ColumnDefinitions>\n                                <Grid.RowDefinitions>\n                                    <RowDefinition Height=\"*\"/>\n                                    <RowDefinition Height=\"*\"/>\n                                    <RowDefinition Height=\"*\"/>\n                                </Grid.RowDefinitions>\n                            </Grid>\n                        </Grid>\n                    </Border>\n\n                    <ControlTemplate.Triggers>\n                        <DataTrigger Binding=\"{Binding DisplayMode, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Calendar}}}\" Value=\"Year\">\n                            <Setter Property=\"Visibility\" TargetName=\"PART_MonthView\" Value=\"Hidden\"/>\n                            <Setter Property=\"Visibility\" TargetName=\"PART_YearView\" Value=\"Visible\"/>\n                        </DataTrigger>\n                        <DataTrigger Binding=\"{Binding DisplayMode, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Calendar}}}\" Value=\"Decade\">\n                            <Setter Property=\"Visibility\" TargetName=\"PART_MonthView\" Value=\"Hidden\"/>\n                            <Setter Property=\"Visibility\" TargetName=\"PART_YearView\" Value=\"Visible\"/>\n                        </DataTrigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <!-- Modern Calendar Style -->\n    <Style TargetType=\"{x:Type Calendar}\" x:Key=\"ModernCalendar\">\n        <Setter Property=\"CalendarDayButtonStyle\" Value=\"{StaticResource ModernCalendarDayButtonStyle}\"/>\n        <Setter Property=\"CalendarItemStyle\" Value=\"{StaticResource ModernCalendarItemStyle}\"/>\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLight}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderStrong}\"/>\n        <Setter Property=\"BorderThickness\" Value=\"0\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type Calendar}\">\n                    <Border Background=\"Transparent\"\n                           CornerRadius=\"8\"\n                           Padding=\"0\">\n                        <Border.Effect>\n                            <DropShadowEffect BlurRadius=\"20\"\n                                            ShadowDepth=\"4\"\n                                            Opacity=\"0.15\"\n                                            Color=\"{StaticResource ColorShadowDefault}\"/>\n                        </Border.Effect>\n                        <CalendarItem x:Name=\"PART_CalendarItem\"\n                                     BorderBrush=\"{TemplateBinding BorderBrush}\"\n                                     BorderThickness=\"{TemplateBinding BorderThickness}\"\n                                     Background=\"{TemplateBinding Background}\"\n                                     Style=\"{TemplateBinding CalendarItemStyle}\"/>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderLighter}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <!-- DatePicker TextBox Style -->\n    <Style TargetType=\"DatePickerTextBox\" x:Key=\"ModernDatePickerTextBox\">\n        <Setter Property=\"Background\" Value=\"Transparent\"/>\n        <Setter Property=\"BorderThickness\" Value=\"0\"/>\n        <Setter Property=\"Padding\" Value=\"8,6\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type DatePickerTextBox}\">\n                    <Grid>\n                        <ScrollViewer x:Name=\"PART_ContentHost\"\n                                     Margin=\"0\"\n                                     VerticalContentAlignment=\"Center\"/>\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushWhiteSmoke}\"/>\n            </DataTrigger>\n            <DataTrigger Binding=\"{Binding Source={x:Static settings:SettingsManager.Current}, Path=DarkMode}\" Value=\"False\">\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushBorderStrong}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <!-- Modern DatePicker Style -->\n    <Style TargetType=\"{x:Type DatePicker}\" x:Key=\"ModernDatePicker\">\n        <Setter Property=\"CalendarStyle\" Value=\"{StaticResource ModernCalendar}\"/>\n        <Setter Property=\"IsTodayHighlighted\" Value=\"True\"/>\n        <Setter Property=\"SelectedDateFormat\" Value=\"Short\"/>\n        <Setter Property=\"Background\" Value=\"Transparent\"/>\n        <Setter Property=\"BorderBrush\" Value=\"Transparent\"/>\n        <Setter Property=\"BorderThickness\" Value=\"0\"/>\n        <Setter Property=\"Padding\" Value=\"0\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type DatePicker}\">\n                    <Grid>\n                        <!-- Main button (shows date text, opens popup) -->\n                        <Button x:Name=\"PART_Button\"\n                            Grid.Column=\"0\"\n                            Style=\"{StaticResource ModernDropDownButtonStyle}\"\n                            Focusable=\"False\"\n                            HorizontalAlignment=\"Stretch\"\n                            VerticalAlignment=\"Stretch\"/>\n\n                        <!-- Popup with actual Calendar inside -->\n                        <Popup x:Name=\"PART_Popup\"\n                           AllowsTransparency=\"True\"\n                           Placement=\"Bottom\"\n                           PlacementTarget=\"{Binding ElementName=PART_Button}\"\n                           StaysOpen=\"False\">\n                            <Border Margin=\"0\"\n                                Background=\"Transparent\">\n                                <Calendar x:Name=\"PART_Calendar\"\n                                      Style=\"{TemplateBinding CalendarStyle}\"\n                                      IsTodayHighlighted=\"{TemplateBinding IsTodayHighlighted}\"\n                                      SelectedDate=\"{Binding SelectedDate, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}\"\n                                      DisplayDate=\"{Binding DisplayDate, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}\"\n                                      BorderThickness=\"0\"/>\n                            </Border>\n                        </Popup>\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.Colors.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <!-- Base neutrals -->\n    <Color x:Key=\"ColorWhite\">White</Color>\n    <Color x:Key=\"ColorBlack\">Black</Color>\n    <Color x:Key=\"ColorGray\">Gray</Color>\n    <Color x:Key=\"ColorLightGray\">LightGray</Color>\n    <Color x:Key=\"ColorDarkGray\">DarkGray</Color>\n    <Color x:Key=\"ColorWhiteSmoke\">WhiteSmoke</Color>\n    <Color x:Key=\"ColorTransparentWhite\">#FFFFFF20</Color>\n    <Color x:Key=\"ColorNeutralHover\">#F3F4F6</Color>\n\n    <!-- Text -->\n    <Color x:Key=\"ColorPrimaryText\">#1F2937</Color>\n    <Color x:Key=\"ColorPrimaryTextDark\">White</Color>\n    <Color x:Key=\"ColorSecondaryText\">#6B7280</Color>\n    <Color x:Key=\"ColorTertiaryText\">#4F5050</Color>\n    <Color x:Key=\"ColorTertiaryTextDark\">#a9abab</Color>\n    <Color x:Key=\"ColorLinkText\">#4287f5</Color>\n    <Color x:Key=\"ColorLinkTextDark\">#97b9f0</Color>\n    <Color x:Key=\"ColorMutedRadioText\">#374151</Color>\n    <Color x:Key=\"ColorMutedText\">#666666</Color>\n    <Color x:Key=\"ColorMutedTextAlt\">#AAAAAA</Color>\n    <Color x:Key=\"ColorHeaderText\">#1a1a1a</Color>\n    <Color x:Key=\"ColorHeaderTextDark\">#f0f0f0</Color>\n\n    <!-- Surfaces -->\n    <Color x:Key=\"ColorSurfaceLight\">White</Color>\n    <Color x:Key=\"ColorSurfaceLightAlt\">#f1f1f1</Color>\n    <Color x:Key=\"ColorSurfaceLightAlt2\">#f8f8f8</Color>\n    <Color x:Key=\"ColorSurfaceLightContext\">#f0f0f0</Color>\n    <Color x:Key=\"ColorSurfaceLightHover\">#F9FAFB</Color>\n    <Color x:Key=\"ColorSurfaceDivider\">#E5E7EB</Color>\n    <Color x:Key=\"ColorSurfaceDark\">#151515</Color>\n    <Color x:Key=\"ColorSurfaceDarkAlt\">#252525</Color>\n    <Color x:Key=\"ColorSurfaceDark2\">#1f1f1f</Color>\n    <Color x:Key=\"ColorSurfaceDark3\">#2a2a2a</Color>\n    <Color x:Key=\"ColorSurfaceDark4\">#3a3a3a</Color>\n    <Color x:Key=\"ColorSurfaceDarkTab\">#202020</Color>\n    <Color x:Key=\"ColorSurfaceSummaryDark\">#303030</Color>\n\n    <!-- Accents -->\n    <Color x:Key=\"ColorAccentTeal\">#00A6A0</Color>\n    <Color x:Key=\"ColorAccentTealHover\">#F0FDFA</Color>\n    <Color x:Key=\"ColorAccentTealPressed\">#E6FFFA</Color>\n    <Color x:Key=\"ColorAccentTealMuted\">#E5E7EB</Color>\n\n    <Color x:Key=\"ColorAccentPeach\">#ffcab5</Color>\n    <Color x:Key=\"ColorAccentPeachSelected\">#f7bda6</Color>\n    <Color x:Key=\"ColorAccentPeachDark\">#c97c5d</Color>\n    <Color x:Key=\"ColorAccentPeachSelectedDark\">#c99079</Color>\n\n    <Color x:Key=\"ColorAccentGreenSoft\">#c0e3da</Color>\n    <Color x:Key=\"ColorAccentGreenSoftHover\">#afe6d9</Color>\n    <Color x:Key=\"ColorAccentGreenBorderLight\">#9fd4c7</Color>\n    <Color x:Key=\"ColorAccentGreenBorderHover\">#7fb6a8</Color>\n    <Color x:Key=\"ColorAccentGreenDark\">#367061</Color>\n    <Color x:Key=\"ColorAccentGreenBorderDark\">#2a5248</Color>\n    <Color x:Key=\"ColorAccentGreenHoverDark\">#458270</Color>\n\n    <Color x:Key=\"ColorAccentGreenAlt1\">#a9d9cc</Color>\n    <Color x:Key=\"ColorAccentGreenAlt2\">#b6e3d7</Color>\n    <Color x:Key=\"ColorAccentGreenAlt3\">#6da395</Color>\n    <Color x:Key=\"ColorAccentGreenAlt4\">#3b695c</Color>\n    <Color x:Key=\"ColorAccentOrange\">#FFA37E</Color>\n\n    <!-- Semantic -->\n    <Color x:Key=\"ColorDanger\">#EF4444</Color>\n    <Color x:Key=\"ColorDangerHover\">#EF8888</Color>\n    <Color x:Key=\"ColorDangerPressed\">#EFAAAA</Color>\n    <Color x:Key=\"ColorDangerStrong\">#E81123</Color>\n\n    <!-- Borders / outlines -->\n    <Color x:Key=\"ColorBorderNeutral\">#4F5050</Color>\n    <Color x:Key=\"ColorBorderNeutralDark\">#616161</Color>\n    <Color x:Key=\"ColorBorderLight\">#D1D5DB</Color>\n    <Color x:Key=\"ColorBorderStrong\">#333333</Color>\n    <Color x:Key=\"ColorBorderSoft\">#555555</Color>\n    <Color x:Key=\"ColorBorderLighter\">#999999</Color>\n    <Color x:Key=\"ColorBorderAlt\">#9CA3AF</Color>\n    <Color x:Key=\"ColorBorderEmphasis\">#FF888888</Color>\n\n    <!-- Shadows -->\n    <Color x:Key=\"ColorShadowDefault\">#000000</Color>\n    <Color x:Key=\"ColorShadowLight\">#dddddd</Color>\n    <Color x:Key=\"ColorShadowDark\">#0d0d0d</Color>\n\n    <!-- Misc -->\n    <Color x:Key=\"ColorHeaderDeep\">#111827</Color>\n    <Color x:Key=\"ColorDownloadAccent\">LightSeaGreen</Color>\n    <Color x:Key=\"ColorUploadAccent\">LightSalmon</Color>\n    <Color x:Key=\"ColorUploadDarkAccent\">#D98868</Color>\n    <Color x:Key=\"ColorDownloadBadgeOverlay\">#6A22C55E</Color>\n    <Color x:Key=\"ColorUploadBadgeOverlay\">#6AF97316</Color>\n    <Color x:Key=\"ColorWindowOverlay\">#01000000</Color>\n    <Color x:Key=\"ColorPinHover\">#30000000</Color>\n    <Color x:Key=\"ColorPinActive\">#40000000</Color>\n    <Color x:Key=\"ColorChartPrimaryStart\">#5DD3CD</Color>\n    <Color x:Key=\"ColorChartPrimaryEnd\">#20B2AA</Color>\n    <Color x:Key=\"ColorChartAccentStart\">#FFB899</Color>\n    <Color x:Key=\"ColorChartAccentEnd\">#FFA07A</Color>\n\n    <!-- Brushes -->\n    <SolidColorBrush x:Key=\"BrushWhite\" Color=\"{StaticResource ColorWhite}\"/>\n    <SolidColorBrush x:Key=\"BrushBlack\" Color=\"{StaticResource ColorBlack}\"/>\n    <SolidColorBrush x:Key=\"BrushGray\" Color=\"{StaticResource ColorGray}\"/>\n    <SolidColorBrush x:Key=\"BrushLightGray\" Color=\"{StaticResource ColorLightGray}\"/>\n    <SolidColorBrush x:Key=\"BrushDarkGray\" Color=\"{StaticResource ColorDarkGray}\"/>\n    <SolidColorBrush x:Key=\"BrushWhiteSmoke\" Color=\"{StaticResource ColorWhiteSmoke}\"/>\n    <SolidColorBrush x:Key=\"BrushTransparentWhite\" Color=\"{StaticResource ColorTransparentWhite}\"/>\n    <SolidColorBrush x:Key=\"BrushNeutralHover\" Color=\"{StaticResource ColorNeutralHover}\"/>\n\n    <SolidColorBrush x:Key=\"BrushPrimaryText\" Color=\"{StaticResource ColorPrimaryText}\"/>\n    <SolidColorBrush x:Key=\"BrushPrimaryTextDark\" Color=\"{StaticResource ColorPrimaryTextDark}\"/>\n    <SolidColorBrush x:Key=\"BrushSecondaryText\" Color=\"{StaticResource ColorSecondaryText}\"/>\n    <SolidColorBrush x:Key=\"BrushTertiaryText\" Color=\"{StaticResource ColorTertiaryText}\"/>\n    <SolidColorBrush x:Key=\"BrushTertiaryTextDark\" Color=\"{StaticResource ColorTertiaryTextDark}\"/>\n    <SolidColorBrush x:Key=\"BrushLinkText\" Color=\"{StaticResource ColorLinkText}\"/>\n    <SolidColorBrush x:Key=\"BrushLinkTextDark\" Color=\"{StaticResource ColorLinkTextDark}\"/>\n    <SolidColorBrush x:Key=\"BrushMutedRadioText\" Color=\"{StaticResource ColorMutedRadioText}\"/>\n    <SolidColorBrush x:Key=\"BrushMutedText\" Color=\"{StaticResource ColorMutedText}\"/>\n    <SolidColorBrush x:Key=\"BrushMutedTextAlt\" Color=\"{StaticResource ColorMutedTextAlt}\"/>\n    <SolidColorBrush x:Key=\"BrushHeaderText\" Color=\"{StaticResource ColorHeaderText}\"/>\n    <SolidColorBrush x:Key=\"BrushHeaderTextDark\" Color=\"{StaticResource ColorHeaderTextDark}\"/>\n\n    <SolidColorBrush x:Key=\"BrushSurfaceLight\" Color=\"{StaticResource ColorSurfaceLight}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceLightAlt\" Color=\"{StaticResource ColorSurfaceLightAlt}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceLightAlt2\" Color=\"{StaticResource ColorSurfaceLightAlt2}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceLightContext\" Color=\"{StaticResource ColorSurfaceLightContext}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceHover\" Color=\"{StaticResource ColorSurfaceLightHover}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceDivider\" Color=\"{StaticResource ColorSurfaceDivider}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceDark\" Color=\"{StaticResource ColorSurfaceDark}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceDarkAlt\" Color=\"{StaticResource ColorSurfaceDarkAlt}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceDark2\" Color=\"{StaticResource ColorSurfaceDark2}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceDark3\" Color=\"{StaticResource ColorSurfaceDark3}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceDark4\" Color=\"{StaticResource ColorSurfaceDark4}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceDarkTab\" Color=\"{StaticResource ColorSurfaceDarkTab}\"/>\n    <SolidColorBrush x:Key=\"BrushSurfaceSummaryDark\" Color=\"{StaticResource ColorSurfaceSummaryDark}\"/>\n\n    <SolidColorBrush x:Key=\"BrushAccentTeal\" Color=\"{StaticResource ColorAccentTeal}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentTealHover\" Color=\"{StaticResource ColorAccentTealHover}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentTealPressed\" Color=\"{StaticResource ColorAccentTealPressed}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentTealMuted\" Color=\"{StaticResource ColorAccentTealMuted}\"/>\n\n    <SolidColorBrush x:Key=\"BrushAccentPeach\" Color=\"{StaticResource ColorAccentPeach}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentPeachSelected\" Color=\"{StaticResource ColorAccentPeachSelected}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentPeachDark\" Color=\"{StaticResource ColorAccentPeachDark}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentPeachSelectedDark\" Color=\"{StaticResource ColorAccentPeachSelectedDark}\"/>\n\n    <SolidColorBrush x:Key=\"BrushAccentGreenSoft\" Color=\"{StaticResource ColorAccentGreenSoft}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentGreenSoftHover\" Color=\"{StaticResource ColorAccentGreenSoftHover}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentGreenBorderLight\" Color=\"{StaticResource ColorAccentGreenBorderLight}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentGreenBorderHover\" Color=\"{StaticResource ColorAccentGreenBorderHover}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentGreenDark\" Color=\"{StaticResource ColorAccentGreenDark}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentGreenBorderDark\" Color=\"{StaticResource ColorAccentGreenBorderDark}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentGreenHoverDark\" Color=\"{StaticResource ColorAccentGreenHoverDark}\"/>\n\n    <SolidColorBrush x:Key=\"BrushAccentGreenAlt1\" Color=\"{StaticResource ColorAccentGreenAlt1}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentGreenAlt2\" Color=\"{StaticResource ColorAccentGreenAlt2}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentGreenAlt3\" Color=\"{StaticResource ColorAccentGreenAlt3}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentGreenAlt4\" Color=\"{StaticResource ColorAccentGreenAlt4}\"/>\n    <SolidColorBrush x:Key=\"BrushAccentOrange\" Color=\"{StaticResource ColorAccentOrange}\"/>\n\n    <SolidColorBrush x:Key=\"BrushDanger\" Color=\"{StaticResource ColorDanger}\"/>\n    <SolidColorBrush x:Key=\"BrushDangerHover\" Color=\"{StaticResource ColorDangerHover}\"/>\n    <SolidColorBrush x:Key=\"BrushDangerPressed\" Color=\"{StaticResource ColorDangerPressed}\"/>\n    <SolidColorBrush x:Key=\"BrushDangerStrong\" Color=\"{StaticResource ColorDangerStrong}\"/>\n\n    <SolidColorBrush x:Key=\"BrushBorderNeutral\" Color=\"{StaticResource ColorBorderNeutral}\"/>\n    <SolidColorBrush x:Key=\"BrushBorderNeutralDark\" Color=\"{StaticResource ColorBorderNeutralDark}\"/>\n    <SolidColorBrush x:Key=\"BrushBorderLight\" Color=\"{StaticResource ColorBorderLight}\"/>\n    <SolidColorBrush x:Key=\"BrushBorderStrong\" Color=\"{StaticResource ColorBorderStrong}\"/>\n    <SolidColorBrush x:Key=\"BrushBorderSoft\" Color=\"{StaticResource ColorBorderSoft}\"/>\n    <SolidColorBrush x:Key=\"BrushBorderLighter\" Color=\"{StaticResource ColorBorderLighter}\"/>\n    <SolidColorBrush x:Key=\"BrushBorderAlt\" Color=\"{StaticResource ColorBorderAlt}\"/>\n    <SolidColorBrush x:Key=\"BrushBorderEmphasis\" Color=\"{StaticResource ColorBorderEmphasis}\"/>\n\n    <SolidColorBrush x:Key=\"BrushShadowDefault\" Color=\"{StaticResource ColorShadowDefault}\"/>\n    <SolidColorBrush x:Key=\"BrushShadowLight\" Color=\"{StaticResource ColorShadowLight}\"/>\n    <SolidColorBrush x:Key=\"BrushShadowDark\" Color=\"{StaticResource ColorShadowDark}\"/>\n\n    <SolidColorBrush x:Key=\"BrushHeaderDeep\" Color=\"{StaticResource ColorHeaderDeep}\"/>\n    <SolidColorBrush x:Key=\"BrushDownloadAccent\" Color=\"{StaticResource ColorDownloadAccent}\"/>\n    <SolidColorBrush x:Key=\"BrushUploadAccent\" Color=\"{StaticResource ColorUploadAccent}\"/>\n    <SolidColorBrush x:Key=\"BrushUploadDarkAccent\" Color=\"{StaticResource ColorUploadDarkAccent}\"/>\n    <SolidColorBrush x:Key=\"BrushDownloadBadgeOverlay\" Color=\"{StaticResource ColorDownloadBadgeOverlay}\"/>\n    <SolidColorBrush x:Key=\"BrushUploadBadgeOverlay\" Color=\"{StaticResource ColorUploadBadgeOverlay}\"/>\n    <SolidColorBrush x:Key=\"BrushWindowOverlay\" Color=\"{StaticResource ColorWindowOverlay}\"/>\n    <SolidColorBrush x:Key=\"BrushPinHover\" Color=\"{StaticResource ColorPinHover}\"/>\n    <SolidColorBrush x:Key=\"BrushPinActive\" Color=\"{StaticResource ColorPinActive}\"/>\n\n    <!-- Gradients -->\n    <LinearGradientBrush x:Key=\"BrushChartPrimaryGradient\" StartPoint=\"0,0\" EndPoint=\"1,1\">\n        <GradientStop Color=\"{StaticResource ColorChartPrimaryStart}\" Offset=\"0\"/>\n        <GradientStop Color=\"{StaticResource ColorChartPrimaryEnd}\" Offset=\"1\"/>\n    </LinearGradientBrush>\n    <LinearGradientBrush x:Key=\"BrushChartAccentGradient\" StartPoint=\"0,0\" EndPoint=\"1,1\">\n        <GradientStop Color=\"{StaticResource ColorChartAccentStart}\" Offset=\"0\"/>\n        <GradientStop Color=\"{StaticResource ColorChartAccentEnd}\" Offset=\"1\"/>\n    </LinearGradientBrush>\n</ResourceDictionary>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.ComboBox.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:res=\"clr-namespace:OpenNetMeter.Properties\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Theme.Colors.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n\n    <SolidColorBrush x:Key=\"BorderBrush\" Color=\"{StaticResource ColorBorderEmphasis}\"/>\n    <SolidColorBrush x:Key=\"BackgroundBrush\" Color=\"{StaticResource ColorSurfaceLight}\"/>\n    <SolidColorBrush x:Key=\"TextBrush\" Color=\"{StaticResource ColorBlack}\"/>\n    <SolidColorBrush x:Key=\"HoverBrush\" Color=\"{StaticResource ColorNeutralHover}\"/>\n    <SolidColorBrush x:Key=\"SelectedBrush\" Color=\"{StaticResource ColorAccentTealMuted}\"/>\n    <ControlTemplate TargetType=\"ToggleButton\" x:Key=\"ComboBoxToggleButtonTemplate\">\n        <Grid>\n            <Grid.ColumnDefinitions>\n                <ColumnDefinition />\n                <ColumnDefinition Width=\"30\" />\n            </Grid.ColumnDefinitions>\n\n            <Border Grid.ColumnSpan=\"2\" Name=\"Border\"\n                    CornerRadius=\"6\" \n                    BorderThickness=\"1\" \n                    Background=\"{DynamicResource BackgroundBrush}\"\n                    BorderBrush=\"{DynamicResource BorderBrush}\">\n            </Border>\n\n            <Path Name=\"Arrow\" Grid.Column=\"1\" \n                  Data=\"M 0 0 L 4 4 L 8 0\" \n                  Stroke=\"{StaticResource BrushSecondaryText}\" StrokeThickness=\"1.5\"\n                  HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"/>\n        </Grid>\n\n        <ControlTemplate.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                <Setter TargetName=\"Border\" Property=\"Background\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                <Setter TargetName=\"Border\" Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderSoft}\"/>\n                <Setter TargetName=\"Arrow\" Property=\"Stroke\" Value=\"{StaticResource BrushWhiteSmoke}\"/>\n            </DataTrigger>\n\n            <Trigger Property=\"UIElement.IsMouseOver\" Value=\"True\">\n                <Setter TargetName=\"Border\" Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderAlt}\"/>\n            </Trigger>\n        </ControlTemplate.Triggers>\n    </ControlTemplate>\n\n    <Style x:Key=\"ComboBoxItemContainerStyle\" TargetType=\"{x:Type ComboBoxItem}\">\n        <Setter Property=\"Padding\" Value=\"10,8\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type ComboBoxItem}\">\n                    <Border Name=\"Border\" Padding=\"{TemplateBinding Padding}\" Background=\"Transparent\" CornerRadius=\"4\" Margin=\"2,1\">\n                        <ContentPresenter />\n                    </Border>\n                    <ControlTemplate.Triggers>\n                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                            <Setter TargetName=\"Border\" Property=\"Background\" Value=\"{StaticResource BrushAccentPeach}\"/>\n                        </Trigger>\n                        <Trigger Property=\"IsSelected\" Value=\"True\">\n                            <Setter TargetName=\"Border\" Property=\"Background\" Value=\"{StaticResource BrushAccentPeachSelected}\"/>\n                            <Setter Property=\"FontWeight\" Value=\"SemiBold\"/>\n                        </Trigger>\n                        <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                            <Setter Property=\"Foreground\" Value=\"{StaticResource BrushPrimaryTextDark}\"/>\n                        </DataTrigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n    <Style x:Key=\"ComboBoxFlatStyle\" TargetType=\"{x:Type ComboBox}\">\n        <Setter Property=\"Cursor\" Value=\"Hand\" />\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"True\"/>\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n        <Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Auto\"/>\n        <Setter Property=\"ScrollViewer.VerticalScrollBarVisibility\" Value=\"Auto\"/>\n        <Setter Property=\"ScrollViewer.CanContentScroll\" Value=\"True\"/>\n        <Setter Property=\"ItemContainerStyle\" Value=\"{StaticResource ComboBoxItemContainerStyle}\"/>\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource TextBrush}\"/>\n        <Setter Property=\"Height\" Value=\"28\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"ComboBox\">\n                    <Grid>\n                        <ToggleButton Name=\"ToggleButton\" \n                                      Template=\"{StaticResource ComboBoxToggleButtonTemplate}\" \n                                      Grid.Column=\"2\" \n                                      Focusable=\"false\"\n                                      IsChecked=\"{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}\"\n                                      ClickMode=\"Press\"/>\n\n                        <ContentPresenter Name=\"ContentSite\" IsHitTestVisible=\"False\" \n                                          Margin=\"12,4\"\n                                          VerticalAlignment=\"Center\"\n                                          HorizontalAlignment=\"Left\" \n                                          Content=\"{TemplateBinding SelectionBoxItem}\"\n                                          ContentTemplate=\"{TemplateBinding SelectionBoxItemTemplate}\"\n                                          ContentTemplateSelector=\"{TemplateBinding ItemTemplateSelector}\"/>\n\n                        <Popup Name=\"Popup\"\n                               Placement=\"Bottom\"\n                               IsOpen=\"{TemplateBinding IsDropDownOpen}\"\n                               AllowsTransparency=\"True\" \n                               Focusable=\"False\"\n                               PopupAnimation=\"Slide\">\n\n                            <Grid Name=\"DropDown\"\n                                  SnapsToDevicePixels=\"True\" \n                                  MinWidth=\"{TemplateBinding ActualWidth}\"\n                                  MaxHeight=\"{TemplateBinding MaxDropDownHeight}\">\n\n                                <Border x:Name=\"DropDownBorder\" \n                                        Background=\"{DynamicResource BackgroundBrush}\"\n                                        BorderThickness=\"1\"\n                                        BorderBrush=\"{DynamicResource BorderBrush}\"\n                                        CornerRadius=\"6\"\n                                        Margin=\"2\">\n                                    <Border.Effect>\n                                        <DropShadowEffect Color=\"{StaticResource ColorShadowDefault}\" Opacity=\"0.1\" BlurRadius=\"10\" ShadowDepth=\"3\" Direction=\"270\"/>\n                                    </Border.Effect>\n                                </Border>\n\n                                <ScrollViewer Margin=\"4,6,4,6\" SnapsToDevicePixels=\"True\">\n                                    <StackPanel IsItemsHost=\"True\" KeyboardNavigation.DirectionalNavigation=\"Contained\" />\n                                </ScrollViewer>\n                            </Grid>\n                        </Popup>\n                    </Grid>\n\n                    <ControlTemplate.Triggers>\n                        <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                            <Setter Property=\"Foreground\" Value=\"{StaticResource BrushPrimaryTextDark}\"/>\n                            <Setter TargetName=\"DropDownBorder\" Property=\"Background\" Value=\"{StaticResource BrushBorderStrong}\"/>\n                            <Setter TargetName=\"DropDownBorder\" Property=\"BorderBrush\" Value=\"{StaticResource BrushBorderSoft}\"/>\n                        </DataTrigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n    </Style>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.ContextMenu.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:res=\"clr-namespace:OpenNetMeter.Properties\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Theme.Colors.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n\n    <!-- mini widget context menu style  -->\n\n    <Style TargetType=\"ContextMenu\" x:Key=\"miniWidgetContextMenuColor\">\n        <Setter Property=\"OverridesDefaultStyle\" Value=\"True\"/>\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"True\"/>\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushSurfaceDarkTab}\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type ContextMenu}\">\n                    <Border Background=\"{StaticResource BrushSurfaceLightContext}\" BorderBrush=\"{StaticResource BrushDarkGray}\" BorderThickness=\"1\">\n                        <StackPanel ClipToBounds=\"True\" Orientation=\"Vertical\" IsItemsHost=\"True\"/>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushHeaderTextDark}\"/>\n                <Setter Property=\"Template\">\n                    <Setter.Value>\n                        <ControlTemplate TargetType=\"{x:Type ContextMenu}\">\n                            <Border Background=\"{StaticResource BrushSurfaceDarkTab}\" BorderBrush=\"{StaticResource BrushDarkGray}\" BorderThickness=\"1\">\n                                <StackPanel ClipToBounds=\"True\" Orientation=\"Vertical\" IsItemsHost=\"True\"/>\n                            </Border>\n                        </ControlTemplate>\n                    </Setter.Value>\n                </Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n</ResourceDictionary>\n\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.DataGrid.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:res=\"clr-namespace:OpenNetMeter.Properties\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Theme.Colors.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n\n    <!-- Modern DataGrid Header Style -->\n    <Style x:Key=\"ModernDGCHeaderStyle\" TargetType=\"DataGridColumnHeader\">\n        <Setter Property=\"Height\" Value=\"36\"/>\n        <Setter Property=\"FontFamily\" Value=\"Segoe UI\" />\n        <Setter Property=\"FontWeight\" Value=\"SemiBold\"/>\n        <Setter Property=\"FontSize\" Value=\"12\"/>\n        <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n        <Setter Property=\"Padding\" Value=\"12,4\"/>\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentGreenSoft}\"/>\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushHeaderText}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushAccentGreenBorderLight}\"/>\n        <Setter Property=\"BorderThickness\" Value=\"0,0,1,0\"/>\n\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"DataGridColumnHeader\">\n                    <Grid SnapsToDevicePixels=\"True\">\n                        <!-- main header -->\n                        <Border x:Name=\"HeaderBorder\"\n                            Background=\"{TemplateBinding Background}\"\n                            BorderBrush=\"{TemplateBinding BorderBrush}\"\n                            BorderThickness=\"{TemplateBinding BorderThickness}\">\n                            <ContentPresenter Margin=\"{TemplateBinding Padding}\"\n                                          VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"\n                                          HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"/>\n                        </Border>\n\n                        <!-- optional left gripper (kept invisible but functional) -->\n                        <Thumb x:Name=\"PART_LeftHeaderGripper\"\n                           HorizontalAlignment=\"Left\"\n                           Width=\"1\"\n                           Cursor=\"SizeWE\"\n                           Background=\"Transparent\"/>\n\n                        <!-- right gripper with flat line -->\n                        <Thumb x:Name=\"PART_RightHeaderGripper\"\n                           HorizontalAlignment=\"Right\"\n                           Width=\"1\"\n                           Cursor=\"SizeWE\"\n                           Background=\"Transparent\">\n                            <Thumb.Template>\n                                <ControlTemplate TargetType=\"Thumb\">\n                                    <Grid>\n                                        <!-- thin line, center -->\n                                        <Border x:Name=\"GripLine\"\n                                            Width=\"1\"\n                                            Margin=\"0\"\n                                            HorizontalAlignment=\"Center\"\n                                            Background=\"Transparent\"/>\n                                    </Grid>\n                                    <ControlTemplate.Triggers>\n                                        <!-- show line on hover -->\n                                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                            <Setter TargetName=\"GripLine\" Property=\"Background\" Value=\"{StaticResource BrushAccentGreenBorderHover}\"/>\n                                        </Trigger>\n                                    </ControlTemplate.Triggers>\n                                </ControlTemplate>\n                            </Thumb.Template>\n                        </Thumb>\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n\n        <!-- your existing triggers for hover / dark mode -->\n        <Style.Triggers>\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentGreenSoftHover}\"/>\n            </Trigger>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentGreenDark}\"/>\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushHeaderTextDark}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushAccentGreenBorderDark}\"/>\n            </DataTrigger>\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}\" Value=\"True\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentGreenHoverDark}\"/>\n            </MultiDataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"ModernDGCHeaderStyle1\" TargetType=\"DataGridColumnHeader\" BasedOn=\"{StaticResource ModernDGCHeaderStyle}\">\n        <Setter Property=\"HorizontalContentAlignment\" Value=\"Left\"/>\n    </Style>\n\n    <Style x:Key=\"ModernDGCHeaderStyle2\" TargetType=\"DataGridColumnHeader\" BasedOn=\"{StaticResource ModernDGCHeaderStyle}\">\n        <Setter Property=\"HorizontalContentAlignment\" Value=\"Right\"/>\n    </Style>\n\n    <!-- Modern DataGrid Row Style -->\n    <Style x:Key=\"ModernDGRowStyle\" TargetType=\"DataGridRow\">\n        <Setter Property=\"Height\" Value=\"32\"/>\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLight}\"/>\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushHeaderText}\"/>\n        <Setter Property=\"BorderThickness\" Value=\"0\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"DataGridRow\">\n                    <Border x:Name=\"DGR_Border\"\n                       Background=\"{TemplateBinding Background}\"\n                       BorderBrush=\"{TemplateBinding BorderBrush}\"\n                       BorderThickness=\"{TemplateBinding BorderThickness}\"\n                       SnapsToDevicePixels=\"True\">\n                        <SelectiveScrollingGrid>\n                            <SelectiveScrollingGrid.ColumnDefinitions>\n                                <ColumnDefinition Width=\"Auto\"/>\n                                <ColumnDefinition Width=\"*\"/>\n                            </SelectiveScrollingGrid.ColumnDefinitions>\n                            <SelectiveScrollingGrid.RowDefinitions>\n                                <RowDefinition Height=\"*\"/>\n                                <RowDefinition Height=\"Auto\"/>\n                            </SelectiveScrollingGrid.RowDefinitions>\n                            <DataGridCellsPresenter Grid.Column=\"1\"\n                                               ItemsPanel=\"{TemplateBinding ItemsPanel}\"\n                                               SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\"/>\n                            <DataGridDetailsPresenter Grid.Column=\"1\" Grid.Row=\"1\"\n                                                 SelectiveScrollingGrid.SelectiveScrollingOrientation=\"Vertical\"\n                                                 Visibility=\"{TemplateBinding DetailsVisibility}\"/>\n                            <DataGridRowHeader Grid.RowSpan=\"2\"\n                                         SelectiveScrollingGrid.SelectiveScrollingOrientation=\"Vertical\"\n                                         Visibility=\"{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Row}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}\"/>\n                        </SelectiveScrollingGrid>\n                    </Border>\n                    <!-- Removed ControlTemplate.Triggers - now handled in Style.Triggers -->\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <!-- Light mode - Alternation 0 -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=AlternationIndex}\" Value=\"0\"/>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"False\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLight}\"/>\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushHeaderText}\"/>\n            </MultiDataTrigger>\n\n            <!-- Light mode - Alternation 1 -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=AlternationIndex}\" Value=\"1\"/>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"False\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLightAlt2}\"/>\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushHeaderText}\"/>\n            </MultiDataTrigger>\n\n            <!-- Light mode - MouseOver -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"False\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeach}\"/>\n            </MultiDataTrigger>\n\n            <!-- Light mode - Selected -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=IsSelected}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"False\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeachSelected}\"/>\n            </MultiDataTrigger>\n\n            <!-- Dark mode - Alternation 0 -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=AlternationIndex}\" Value=\"0\"/>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceDark3}\"/>\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushHeaderTextDark}\"/>\n            </MultiDataTrigger>\n\n            <!-- Dark mode - Alternation 1 -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=AlternationIndex}\" Value=\"1\"/>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceDark4}\"/>\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushHeaderTextDark}\"/>\n            </MultiDataTrigger>\n\n            <!-- Dark mode - MouseOver -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeachDark}\"/>\n            </MultiDataTrigger>\n\n            <!-- Dark mode - Selected -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Self}, Path=IsSelected}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeachSelectedDark}\"/>\n            </MultiDataTrigger>\n        </Style.Triggers>\n    </Style>\n    \n    <!-- Modern DataGrid Cell Style -->\n    <Style x:Key=\"ModernDGCellStyle\" TargetType=\"DataGridCell\">\n        <Setter Property=\"Padding\" Value=\"12,4\"/>\n        <Setter Property=\"BorderThickness\" Value=\"0\"/>\n        <Setter Property=\"Background\" Value=\"Transparent\"/>\n        <Setter Property=\"FocusVisualStyle\" Value=\"{x:Null}\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"DataGridCell\">\n                    <Border Background=\"{TemplateBinding Background}\"\n                           BorderBrush=\"{TemplateBinding BorderBrush}\"\n                           BorderThickness=\"{TemplateBinding BorderThickness}\"\n                           Padding=\"{TemplateBinding Padding}\"\n                           SnapsToDevicePixels=\"True\">\n                        <ContentPresenter SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\"/>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <Trigger Property=\"IsSelected\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"Transparent\"/>\n                <Setter Property=\"Foreground\" Value=\"{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=Foreground}\"/>\n            </Trigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"ModernDGCellStyle1\" TargetType=\"DataGridCell\" BasedOn=\"{StaticResource ModernDGCellStyle}\">\n        <Setter Property=\"TextBlock.TextAlignment\" Value=\"Left\"/>\n    </Style>\n\n    <Style x:Key=\"ModernDGCellStyle2\" TargetType=\"DataGridCell\" BasedOn=\"{StaticResource ModernDGCellStyle}\">\n        <Setter Property=\"TextBlock.TextAlignment\" Value=\"Right\"/>\n    </Style>\n\n    <!-- Modern DataGrid Container Style -->\n    <Style TargetType=\"DataGrid\" x:Key=\"ModernDGHeaderStyle\">\n        <Setter Property=\"RowStyle\" Value=\"{StaticResource ModernDGRowStyle}\"/>\n        <Setter Property=\"ColumnHeaderStyle\" Value=\"{StaticResource ModernDGCHeaderStyle}\"/>\n        <Setter Property=\"GridLinesVisibility\" Value=\"None\"/>\n        <Setter Property=\"HeadersVisibility\" Value=\"Column\"/>\n        <Setter Property=\"AlternationCount\" Value=\"2\"/>\n        <Setter Property=\"RowHeaderWidth\" Value=\"0\"/>\n        <Setter Property=\"CanUserResizeRows\" Value=\"False\"/>\n        <Setter Property=\"SelectionMode\" Value=\"Single\"/>\n        <Setter Property=\"SelectionUnit\" Value=\"FullRow\"/>\n        <Setter Property=\"AutoGenerateColumns\" Value=\"False\"/>\n        <Setter Property=\"FontFamily\" Value=\"Segoe UI\"/>\n        <Setter Property=\"IsReadOnly\" Value=\"True\"/>\n        <Setter Property=\"FontSize\" Value=\"13\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"DataGrid\">\n                    <Border>\n                        <ScrollViewer x:Name=\"DG_ScrollViewer\" Focusable=\"False\">\n                            <ScrollViewer.Template>\n                                <ControlTemplate TargetType=\"ScrollViewer\">\n                                    <Grid>\n                                        <Grid.ColumnDefinitions>\n                                            <ColumnDefinition Width=\"Auto\"/>\n                                            <ColumnDefinition Width=\"*\"/>\n                                            <ColumnDefinition Width=\"Auto\"/>\n                                        </Grid.ColumnDefinitions>\n                                        <Grid.RowDefinitions>\n                                            <RowDefinition Height=\"Auto\"/>\n                                            <RowDefinition Height=\"*\"/>\n                                            <RowDefinition Height=\"Auto\"/>\n                                        </Grid.RowDefinitions>\n\n                                        <DataGridColumnHeadersPresenter Grid.Column=\"1\"\n                                                                       Visibility=\"{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}\"/>\n\n                                        <ScrollContentPresenter Grid.Row=\"1\" Grid.ColumnSpan=\"2\"\n                                                              CanContentScroll=\"{TemplateBinding CanContentScroll}\"/>\n\n                                        <ScrollBar Grid.Row=\"1\" Grid.Column=\"2\"\n                                                 Name=\"PART_VerticalScrollBar\"\n                                                 Orientation=\"Vertical\"\n                                                 Maximum=\"{TemplateBinding ScrollableHeight}\"\n                                                 ViewportSize=\"{TemplateBinding ViewportHeight}\"\n                                                 Value=\"{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}\"\n                                                 Visibility=\"{TemplateBinding ComputedVerticalScrollBarVisibility}\"/>\n\n                                        <ScrollBar Grid.Row=\"2\" Grid.Column=\"1\"\n                                                 Name=\"PART_HorizontalScrollBar\"\n                                                 Orientation=\"Horizontal\"\n                                                 Maximum=\"{TemplateBinding ScrollableWidth}\"\n                                                 ViewportSize=\"{TemplateBinding ViewportWidth}\"\n                                                 Value=\"{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}\"\n                                                 Visibility=\"{TemplateBinding ComputedHorizontalScrollBarVisibility}\"/>\n                                    </Grid>\n                                </ControlTemplate>\n                            </ScrollViewer.Template>\n                            <ItemsPresenter SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\"/>\n                        </ScrollViewer>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceDark2}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <!-- Modern Footer Style -->\n    <Style TargetType=\"Grid\" x:Key=\"ModernFooterGridStyle\">\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentGreenSoft}\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentGreenDark}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"Border\" x:Key=\"ModernFooterTextBorder\">\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushAccentGreenBorderLight}\"/>\n        <Setter Property=\"BorderThickness\" Value=\"0,0,1,0\"/>\n        <Setter Property=\"Padding\" Value=\"8,4\"/>\n        <Setter Property=\"SnapsToDevicePixels\" Value=\"True\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushAccentGreenBorderDark}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"TextBlock\" x:Key=\"ModernFooterTextStyle\">\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushHeaderText}\"/>\n        <Setter Property=\"FontWeight\" Value=\"SemiBold\"/>\n        <Setter Property=\"FontSize\" Value=\"13\"/>\n        <Setter Property=\"TextAlignment\" Value=\"Right\"/>\n        <Setter Property=\"HorizontalAlignment\" Value=\"Stretch\"/>\n        <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n        <Setter Property=\"TextTrimming\" Value=\"None\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushHeaderTextDark}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n</ResourceDictionary>\n\n\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.MainWindowTabs.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:res=\"clr-namespace:OpenNetMeter.Properties\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Theme.Colors.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n\n    <Style TargetType=\"Button\" x:Key=\"ButtonColor\">\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLight}\"></Setter>\n        <!-- set button highlight template color -->\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type Button}\">\n                    <Border Background=\"{TemplateBinding Background}\" >\n                        <ContentPresenter HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"/>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceDark}\"></Setter>\n            </DataTrigger>\n            <!-- set button highlight light mode color -->\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeach}\"/>\n            </Trigger>\n\n            <!-- set button highlight dark mode color -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsMouseOver}\" Value=\"true\" />\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeachDark}\"/>\n            </MultiDataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"Button\" x:Key=\"MainWindowSummaryTab\">\n\n        <!-- set unselected button light mode color -->\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLightAlt}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushSurfaceLightAlt}\"/>\n\n        <!-- set button highlight template color -->\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type Button}\">\n                    <Border Background=\"{TemplateBinding Background}\"  BorderThickness=\"0,0,0,5\" BorderBrush=\"{TemplateBinding BorderBrush}\">\n                        <ContentPresenter HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"/>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n\n        <Style.Triggers>\n\n            <!-- set unselected button dark mode color -->\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceDarkTab}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushSurfaceDarkTab}\"/>\n            </DataTrigger>\n\n            <!-- set button highlight light mode color -->\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeach}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushAccentPeach}\"/>\n            </Trigger>\n\n            <!-- set button highlight dark mode color -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsMouseOver}\" Value=\"true\" />\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeachDark}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushAccentPeachDark}\"/>\n            </MultiDataTrigger>\n\n            <!-- set selected button border light mode color -->\n            <DataTrigger Binding=\"{Binding TabBtnToggle}\" Value=\"0\">\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBlack}\"/>\n            </DataTrigger>\n\n            <!-- set selected button border dark mode color -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding TabBtnToggle}\" Value=\"0\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushSurfaceLightAlt}\"/>\n            </MultiDataTrigger>\n\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"Button\" x:Key=\"MainWindowHistoryTab\">\n\n        <!-- set unselected button light mode color -->\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLightAlt}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushSurfaceLightAlt}\"/>\n\n        <!-- set button highlight template color -->\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type Button}\">\n                    <Border Background=\"{TemplateBinding Background}\"  BorderThickness=\"0,0,0,5\" BorderBrush=\"{TemplateBinding BorderBrush}\">\n                        <ContentPresenter HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"/>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n\n            <!-- set unselected button dark mode color -->\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceDarkTab}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushSurfaceDarkTab}\"/>\n            </DataTrigger>\n\n            <!-- set button highlight light mode color -->\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeach}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushAccentPeach}\"/>\n            </Trigger>\n\n            <!-- set button highlight dark mode color -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsMouseOver}\" Value=\"true\" />\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeachDark}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushAccentPeachDark}\"/>\n            </MultiDataTrigger>\n\n            <!-- set selected button border light mode color -->\n            <DataTrigger Binding=\"{Binding TabBtnToggle}\" Value=\"1\">\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBlack}\"/>\n            </DataTrigger>\n\n            <!-- set selected button border dark mode color -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding TabBtnToggle}\" Value=\"1\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushSurfaceLightAlt}\"/>\n            </MultiDataTrigger>\n\n        </Style.Triggers>\n    </Style>\n    \n    <Style TargetType=\"Button\" x:Key=\"MainWindowSettingsTab\">\n        \n        <!-- set unselected button light mode color -->\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLightAlt}\"/>\n        <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushSurfaceLightAlt}\"/>\n        \n        <!-- set button highlight template color -->\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"{x:Type Button}\">\n                    <Border Background=\"{TemplateBinding Background}\"  BorderThickness=\"0,0,0,5\" BorderBrush=\"{TemplateBinding BorderBrush}\">\n                        <ContentPresenter HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"/>\n                    </Border>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n\n            <!-- set unselected button dark mode color -->\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceDarkTab}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushSurfaceDarkTab}\"/>\n            </DataTrigger>\n\n            <!-- set button highlight light mode color -->\n            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeach}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushAccentPeach}\"/>\n            </Trigger>\n\n            <!-- set button highlight dark mode color -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsMouseOver}\" Value=\"true\" />\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentPeachDark}\"/>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushAccentPeachDark}\"/>\n            </MultiDataTrigger>\n\n            <!-- set selected button border light mode color -->\n            <DataTrigger Binding=\"{Binding TabBtnToggle}\" Value=\"2\">\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushBlack}\"/>\n            </DataTrigger>\n\n            <!-- set selected button border dark mode color -->\n            <MultiDataTrigger>\n                <MultiDataTrigger.Conditions>\n                    <Condition Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\"/>\n                    <Condition Binding=\"{Binding TabBtnToggle}\" Value=\"2\"/>\n                </MultiDataTrigger.Conditions>\n                <Setter Property=\"BorderBrush\" Value=\"{StaticResource BrushSurfaceLightAlt}\"/>\n            </MultiDataTrigger>\n\n        </Style.Triggers>\n    </Style>\n\n</ResourceDictionary>\n\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.Styles.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:res=\"clr-namespace:OpenNetMeter.Properties\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Theme.Colors.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n\n    <!-- Modern Card Style -->\n    <Style x:Key=\"ModernCard\" TargetType=\"Border\">\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLight}\"/>\n        <Setter Property=\"CornerRadius\" Value=\"12\"/>\n        <Setter Property=\"Effect\">\n            <Setter.Value>\n                <DropShadowEffect BlurRadius=\"10\" ShadowDepth=\"2\" Opacity=\"0.2\" Direction=\"270\"/>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceDark3}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n    \n    <Style TargetType=\"Line\" x:Key=\"LineColor\">\n        <Setter Property=\"Stroke\" Value=\"{StaticResource BrushBlack}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Stroke\" Value=\"{StaticResource BrushWhite}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <!-- Title Text Style -->\n    <Style x:Key=\"TitleText\" TargetType=\"TextBlock\">\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"FontWeight\" Value=\"SemiBold\"/>\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushPrimaryText}\"/>\n        <Setter Property=\"VerticalAlignment\" Value=\"Center\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushPrimaryTextDark}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <!-- Primary Text Style -->\n    <Style x:Key=\"PrimaryText\" TargetType=\"TextBlock\">\n        <Setter Property=\"FontSize\" Value=\"13\"/>\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushPrimaryText}\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushPrimaryTextDark}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <!-- Secondary Text Style -->\n    <Style x:Key=\"SecondaryText\" TargetType=\"TextBlock\">\n        <Setter Property=\"FontSize\" Value=\"12\"/>\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushSecondaryText}\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushSecondaryText}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <!-- Foreground colors -->\n    <Style x:Key=\"DownloadForeground\">\n        <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushAccentGreenDark}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushAccentGreenSoft}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"UploadForeground\">\n        <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushAccentPeachDark}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushAccentPeach}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"DarkModeBackgroundColor\">\n        <Setter Property=\"Control.Background\" Value=\"{StaticResource BrushBlack}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Control.Background\" Value=\"{StaticResource BrushWhite}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"Fill1\" TargetType=\"Path\">\n        <Setter Property=\"Fill\" Value=\"Black\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path=DarkMode}\" Value=\"True\">\n                <Setter Property=\"Fill\" Value=\"White\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"FontColor1\">\n        <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushBlack}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushWhite}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"FontColor2\">\n        <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushGray}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushLightGray}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n    \n    <Style x:Key=\"FontColor3\">\n        <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushTertiaryText}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushTertiaryTextDark}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"FontColor4\">\n        <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushLinkText}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Control.Foreground\" Value=\"{StaticResource BrushLinkTextDark}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"Grid\" x:Key=\"BackgroundColor1\">\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLight}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceDark}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"Grid\" x:Key=\"BackgroundColor2\">\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLightAlt}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceDarkAlt}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"Border\" x:Key=\"CardHeaderStyle\">\n        <Setter Property=\"Height\" Value=\"40\"/>\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentGreenSoft}\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentGreenDark}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"Border\" x:Key=\"BorderColor\">\n        <Setter Property=\"Control.BorderBrush\" Value=\"{StaticResource BrushBorderNeutral}\"></Setter>\n        <Setter Property=\"Control.BorderThickness\" Value=\"1\"></Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Control.BorderBrush\" Value=\"{StaticResource BrushBorderNeutralDark}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"DataRowAccentDownload\" TargetType=\"Border\">\n        <Setter Property=\"CornerRadius\" Value=\"6\"/>\n        <Setter Property=\"Padding\" Value=\"8,6\"/>\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushDownloadAccent}\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushAccentGreenDark}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"DataRowAccentUpload\" TargetType=\"Border\">\n        <Setter Property=\"CornerRadius\" Value=\"6\"/>\n        <Setter Property=\"Padding\" Value=\"8,6\"/>\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushUploadAccent}\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushUploadDarkAccent}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <!-- Modern Radio Button Style -->\n    <Style x:Key=\"ModernRadioButton\" TargetType=\"RadioButton\">\n        <Setter Property=\"Foreground\" Value=\"{StaticResource BrushMutedRadioText}\"/>\n        <Setter Property=\"FontSize\" Value=\"13\"/>\n        <Setter Property=\"Cursor\" Value=\"Hand\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"RadioButton\">\n                    <Grid Margin=\"{TemplateBinding Margin}\"\n                          Cursor=\"{TemplateBinding Cursor}\">\n                        <Grid.ColumnDefinitions>\n                            <ColumnDefinition Width=\"Auto\"/>\n                            <ColumnDefinition Width=\"*\"/>\n                        </Grid.ColumnDefinitions>\n\n                        <!-- Custom Radio Circle -->\n                        <Grid Grid.Column=\"0\" Width=\"20\" Height=\"20\" VerticalAlignment=\"Center\">\n                            <!-- Outer Circle -->\n                            <Ellipse x:Name=\"OuterCircle\" \n                                        Width=\"20\" Height=\"20\"\n                                        Stroke=\"{StaticResource BrushBorderLight}\"\n                                        StrokeThickness=\"2\"\n                                        Fill=\"{StaticResource BrushWhite}\">\n                                <Ellipse.Effect>\n                                    <DropShadowEffect ShadowDepth=\"0\" BlurRadius=\"2\" Opacity=\"0.1\"/>\n                                </Ellipse.Effect>\n                            </Ellipse>\n\n                            <!-- Inner Dot -->\n                            <Ellipse x:Name=\"InnerDot\" \n                                        Width=\"10\" Height=\"10\"\n                                        Fill=\"{StaticResource BrushAccentTeal}\"\n                                        HorizontalAlignment=\"Center\"\n                                        VerticalAlignment=\"Center\"\n                                        Opacity=\"0\">\n                                <Ellipse.RenderTransform>\n                                    <ScaleTransform x:Name=\"DotScale\" ScaleX=\"0\" ScaleY=\"0\" CenterX=\"5\" CenterY=\"5\"/>\n                                </Ellipse.RenderTransform>\n                            </Ellipse>\n                        </Grid>\n\n                        <!-- Content -->\n                        <ContentPresenter Grid.Column=\"1\" \n                                            Margin=\"10,0,0,0\"\n                                            VerticalAlignment=\"Center\"\n                                            RecognizesAccessKey=\"True\"/>\n                    </Grid>\n\n                    <ControlTemplate.Triggers>\n                        <!-- Hover State -->\n                        <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                            <Setter TargetName=\"OuterCircle\" Property=\"Stroke\" Value=\"{StaticResource BrushAccentTeal}\"/>\n                            <Setter TargetName=\"OuterCircle\" Property=\"Fill\" Value=\"{StaticResource BrushAccentTealHover}\"/>\n                        </Trigger>\n\n                        <!-- Checked State -->\n                        <Trigger Property=\"IsChecked\" Value=\"True\">\n                            <Trigger.EnterActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <DoubleAnimation Storyboard.TargetName=\"InnerDot\"\n                                                           Storyboard.TargetProperty=\"Opacity\"\n                                                           To=\"1\" Duration=\"0:0:0.2\"/>\n                                        <DoubleAnimation Storyboard.TargetName=\"DotScale\"\n                                                           Storyboard.TargetProperty=\"ScaleX\"\n                                                           To=\"1\" Duration=\"0:0:0.3\">\n                                            <DoubleAnimation.EasingFunction>\n                                                <BackEase EasingMode=\"EaseOut\" Amplitude=\"0.5\"/>\n                                            </DoubleAnimation.EasingFunction>\n                                        </DoubleAnimation>\n                                        <DoubleAnimation Storyboard.TargetName=\"DotScale\"\n                                                           Storyboard.TargetProperty=\"ScaleY\"\n                                                           To=\"1\" Duration=\"0:0:0.3\">\n                                            <DoubleAnimation.EasingFunction>\n                                                <BackEase EasingMode=\"EaseOut\" Amplitude=\"0.5\"/>\n                                            </DoubleAnimation.EasingFunction>\n                                        </DoubleAnimation>\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.EnterActions>\n                            <Trigger.ExitActions>\n                                <BeginStoryboard>\n                                    <Storyboard>\n                                        <DoubleAnimation Storyboard.TargetName=\"InnerDot\"\n                                                           Storyboard.TargetProperty=\"Opacity\"\n                                                           To=\"0\" Duration=\"0:0:0.2\"/>\n                                        <DoubleAnimation Storyboard.TargetName=\"DotScale\"\n                                                           Storyboard.TargetProperty=\"ScaleX\"\n                                                           To=\"0\" Duration=\"0:0:0.2\"/>\n                                        <DoubleAnimation Storyboard.TargetName=\"DotScale\"\n                                                           Storyboard.TargetProperty=\"ScaleY\"\n                                                           To=\"0\" Duration=\"0:0:0.2\"/>\n                                    </Storyboard>\n                                </BeginStoryboard>\n                            </Trigger.ExitActions>\n                            <Setter TargetName=\"OuterCircle\" Property=\"Stroke\" Value=\"{StaticResource BrushAccentTeal}\"/>\n                            <Setter TargetName=\"OuterCircle\" Property=\"StrokeThickness\" Value=\"2\"/>\n                        </Trigger>\n\n                        <!-- Pressed State -->\n                        <Trigger Property=\"IsPressed\" Value=\"True\">\n                            <Setter TargetName=\"OuterCircle\" Property=\"Fill\" Value=\"{StaticResource BrushAccentTealPressed}\"/>\n                        </Trigger>\n\n                        <!-- Disabled State -->\n                        <Trigger Property=\"IsEnabled\" Value=\"False\">\n                            <Setter Property=\"Opacity\" Value=\"0.5\"/>\n                            <Setter Property=\"Cursor\" Value=\"Arrow\"/>\n                        </Trigger>\n                    </ControlTemplate.Triggers>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Foreground\" Value=\"{StaticResource BrushPrimaryTextDark}\"></Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style x:Key=\"ModernSlider\" TargetType=\"Slider\">\n        <Setter Property=\"Cursor\" Value=\"Hand\"/>\n        <Setter Property=\"Template\">\n            <Setter.Value>\n                <ControlTemplate TargetType=\"Slider\">\n                    <Grid>\n                        <Grid.RowDefinitions>\n                            <RowDefinition Height=\"Auto\"/>\n                            <RowDefinition Height=\"Auto\" MinHeight=\"{TemplateBinding MinHeight}\"/>\n                            <RowDefinition Height=\"Auto\"/>\n                        </Grid.RowDefinitions>\n\n                        <Track Grid.Row=\"1\" x:Name=\"PART_Track\">\n                            <Track.DecreaseRepeatButton>\n                                <RepeatButton>\n                                    <RepeatButton.Style>\n                                        <Style TargetType=\"RepeatButton\">\n                                            <Setter Property=\"Template\">\n                                                <Setter.Value>\n                                                    <ControlTemplate TargetType=\"RepeatButton\">\n                                                        <Border Height=\"4\" Background=\"{StaticResource BrushAccentTeal}\" CornerRadius=\"2\"/>\n                                                    </ControlTemplate>\n                                                </Setter.Value>\n                                            </Setter>\n                                        </Style>\n                                    </RepeatButton.Style>\n                                </RepeatButton>\n                            </Track.DecreaseRepeatButton>\n                            <Track.IncreaseRepeatButton>\n                                <RepeatButton>\n                                    <RepeatButton.Style>\n                                        <Style TargetType=\"RepeatButton\">\n                                            <Setter Property=\"Template\">\n                                                <Setter.Value>\n                                                    <ControlTemplate TargetType=\"RepeatButton\">\n                                                        <Border Height=\"4\" Background=\"{StaticResource BrushAccentTealMuted}\" CornerRadius=\"2\"/>\n                                                    </ControlTemplate>\n                                                </Setter.Value>\n                                            </Setter>\n                                        </Style>\n                                    </RepeatButton.Style>\n                                </RepeatButton>\n                            </Track.IncreaseRepeatButton>\n                            <Track.Thumb>\n                                <Thumb x:Name=\"Thumb\">\n                                    <Thumb.Style>\n                                        <Style TargetType=\"Thumb\">\n                                            <Setter Property=\"Cursor\" Value=\"Hand\"/>\n                                            <Setter Property=\"Template\">\n                                                <Setter.Value>\n                                                    <ControlTemplate TargetType=\"Thumb\">\n                                                        <Ellipse x:Name=\"ThumbCircle\" Width=\"20\" Height=\"20\" Fill=\"{StaticResource BrushWhite}\" \n                                                                   Stroke=\"{StaticResource BrushAccentTeal}\" StrokeThickness=\"2\">\n                                                            <Ellipse.Effect>\n                                                                <DropShadowEffect ShadowDepth=\"0\" BlurRadius=\"4\" Opacity=\"0.2\"/>\n                                                            </Ellipse.Effect>\n                                                        </Ellipse>\n                                                        <ControlTemplate.Triggers>\n                                                            <Trigger Property=\"IsMouseOver\" Value=\"True\">\n                                                                <Setter TargetName=\"ThumbCircle\" Property=\"Width\" Value=\"22\"/>\n                                                                <Setter TargetName=\"ThumbCircle\" Property=\"Height\" Value=\"22\"/>\n                                                            </Trigger>\n                                                            <Trigger Property=\"IsDragging\" Value=\"True\">\n                                                                <Setter TargetName=\"ThumbCircle\" Property=\"Fill\" Value=\"{StaticResource BrushAccentTealPressed}\"/>\n                                                            </Trigger>\n                                                        </ControlTemplate.Triggers>\n                                                    </ControlTemplate>\n                                                </Setter.Value>\n                                            </Setter>\n                                        </Style>\n                                    </Thumb.Style>\n                                </Thumb>\n                            </Track.Thumb>\n                        </Track>\n                    </Grid>\n                </ControlTemplate>\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <Trigger Property=\"IsEnabled\" Value=\"False\">\n                <Setter Property=\"Opacity\" Value=\"0.45\"/>\n                <Setter Property=\"Cursor\" Value=\"Arrow\"/>\n            </Trigger>\n        </Style.Triggers>\n    </Style>\n    \n</ResourceDictionary>\n\n\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/Theme.SummaryPage.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:res=\"clr-namespace:OpenNetMeter.Properties\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Theme.Colors.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n\n    <!-- Summary page styles -->\n    \n    <Style TargetType=\"Border\" x:Key=\"BorderStyle1\">\n        <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceLight}\"/>\n        <Setter Property=\"CornerRadius\" Value=\"10\"/>\n        <Setter Property=\"Effect\">\n            <Setter.Value>\n                <DropShadowEffect BlurRadius=\"20\" Color=\"{StaticResource ColorShadowLight}\" Opacity=\"0.4\" Direction=\"280\" ShadowDepth=\"0\" />\n            </Setter.Value>\n        </Setter>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Background\" Value=\"{StaticResource BrushSurfaceSummaryDark}\"/>\n                <Setter Property=\"Effect\">\n                    <Setter.Value>\n                        <DropShadowEffect BlurRadius=\"20\" Color=\"{StaticResource ColorShadowDark}\" Opacity=\"0.4\" Direction=\"280\" ShadowDepth=\"0\" />\n                    </Setter.Value>\n                </Setter>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"ItemsControl\" x:Key=\"GraphLabels\">\n        <Setter Property=\"TextBlock.Foreground\" Value=\"{StaticResource BrushBlack}\"/>\n        <Setter Property=\"TextBlock.FontSize\" Value=\"20\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"TextBlock.Foreground\" Value=\"{StaticResource BrushWhite}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"Line\" x:Key=\"GraphXYLines\">\n        <Setter Property=\"Stroke\" Value=\"{StaticResource BrushLightGray}\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Stroke\" Value=\"{StaticResource BrushDarkGray}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Style TargetType=\"Line\" x:Key=\"GraphBorder\">\n        <Setter Property=\"Stroke\" Value=\"{StaticResource BrushBlack}\"/>\n        <Style.Triggers>\n            <DataTrigger Binding=\"{Binding Source={x:Static res:SettingsManager.Current}, Path = DarkMode}\" Value=\"True\">\n                <Setter Property=\"Stroke\" Value=\"{StaticResource BrushLightGray}\"/>\n            </DataTrigger>\n        </Style.Triggers>\n    </Style>\n\n    <Grid x:Key=\"Download_Icon\"  x:Shared=\"False\">\n        <Line\n                                X1=\"0\" Y1=\"0\"\n                                X2=\"0\" Y2=\"20\"\n                                Stroke=\"{StaticResource BrushDownloadAccent}\"\n                                StrokeThickness=\"2\" Margin=\"0,0,10,0\" VerticalAlignment=\"Center\" />\n        <Line\n                                X1=\"0\" Y1=\"20\"\n                                X2=\"6\" Y2=\"10\"\n                                Stroke=\"{StaticResource BrushDownloadAccent}\"\n                                StrokeThickness=\"2\" Margin=\"0,0,0,0\" VerticalAlignment=\"Center\" />\n        <Line\n                                X1=\"0\" Y1=\"20\"\n                                X2=\"-6\" Y2=\"10\"\n                                Stroke=\"{StaticResource BrushDownloadAccent}\"\n                                StrokeThickness=\"2\" Margin=\"0,0,0,0\" VerticalAlignment=\"Center\" />\n    </Grid>\n    <Grid x:Key=\"Upload_Icon\"  x:Shared=\"False\">\n        <Line\n                                X1=\"0\" Y1=\"0\"\n                                X2=\"0\" Y2=\"20\"\n                                Stroke=\"{StaticResource BrushUploadAccent}\"\n                                StrokeThickness=\"2\" Margin=\"0,0,10,0\" VerticalAlignment=\"Center\" />\n        <Line\n                                X1=\"0\" Y1=\"-8\"\n                                X2=\"6\" Y2=\"5\"\n                                Stroke=\"{StaticResource BrushUploadAccent}\"\n                                StrokeThickness=\"2\" Margin=\"0,0,0,0\" VerticalAlignment=\"Center\" />\n        <Line\n                                X1=\"0\" Y1=\"-8\"\n                                X2=\"-6\" Y2=\"5\"\n                                Stroke=\"{StaticResource BrushUploadAccent}\"\n                                StrokeThickness=\"2\" Margin=\"0,0,0,0\" VerticalAlignment=\"Center\" />\n    </Grid>\n\n</ResourceDictionary>\n\n\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/Views/ResourceDictionaries/ThemeResources.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"Theme.Colors.xaml\"/>\n        <ResourceDictionary Source=\"Theme.Styles.xaml\"/>\n        <ResourceDictionary Source=\"Theme.MainWindowTabs.xaml\"/>\n        <ResourceDictionary Source=\"Theme.SummaryPage.xaml\"/>\n        <ResourceDictionary Source=\"Theme.DataGrid.xaml\"/>\n        <ResourceDictionary Source=\"Theme.ComboBox.xaml\"/>\n        <ResourceDictionary Source=\"Theme.ContextMenu.xaml\"/>\n        <ResourceDictionary Source=\"CustomDatePicker.xaml\"/>\n    </ResourceDictionary.MergedDictionaries>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "OpenNetMeter.Old/OpenNetMeter/app.manifest",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <assemblyIdentity version=\"1.0.0.0\" name=\"MyApplication.app\"/>\n  <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v2\">\n    <security>\n      <requestedPrivileges xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n        <!-- UAC Manifest Options\n             If you want to change the Windows User Account Control level replace the \n             requestedExecutionLevel node with one of the following.\n\n        <requestedExecutionLevel  level=\"asInvoker\" uiAccess=\"false\" />\n        <requestedExecutionLevel  level=\"requireAdministrator\" uiAccess=\"false\" />\n        <requestedExecutionLevel  level=\"highestAvailable\" uiAccess=\"false\" />\n\n            Specifying requestedExecutionLevel element will disable file and registry virtualization. \n            Remove this element if your application requires this virtualization for backwards\n            compatibility.\n        -->\n        <requestedExecutionLevel level=\"requireAdministrator\" uiAccess=\"false\" />\n      </requestedPrivileges>\n    </security>\n  </trustInfo>\n\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- A list of the Windows versions that this application has been tested on\n           and is designed to work with. Uncomment the appropriate elements\n           and Windows will automatically select the most compatible environment. -->\n\n      <!-- Windows Vista -->\n      <!--<supportedOS Id=\"{e2011457-1546-43c5-a5fe-008deee3d3f0}\" />-->\n\n      <!-- Windows 7 -->\n      <!--<supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\" />-->\n\n      <!-- Windows 8 -->\n      <!--<supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\" />-->\n\n      <!-- Windows 8.1 -->\n      <!--<supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\" />-->\n\n      <!-- Windows 10 -->\n      <!--<supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\" />-->\n\n    </application>\n  </compatibility>\n\n  <!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher\n       DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need \n       to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should \n       also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. \n       \n       Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->\n  <!--\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true</dpiAware>\n      <longPathAware xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">true</longPathAware>\n    </windowsSettings>\n  </application>\n  -->\n\n  <!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->\n  <!--\n  <dependency>\n    <dependentAssembly>\n      <assemblyIdentity\n          type=\"win32\"\n          name=\"Microsoft.Windows.Common-Controls\"\n          version=\"6.0.0.0\"\n          processorArchitecture=\"*\"\n          publicKeyToken=\"6595b64144ccf1df\"\n          language=\"*\"\n        />\n    </dependentAssembly>\n  </dependency>\n  -->\n\n</assembly>\n"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/IExternalLinkService.cs",
    "content": "namespace OpenNetMeter.PlatformAbstractions;\n\npublic interface IExternalLinkService\n{\n    void Open(string uri);\n}\n"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/INetworkCaptureService.cs",
    "content": "﻿using System;\n\nnamespace OpenNetMeter.PlatformAbstractions;\n\npublic interface INetworkCaptureService : IDisposable\n{\n    event EventHandler<NetworkSnapshotChangedEventArgs>? NetworkChanged;\n    event EventHandler<NetworkTrafficEventArgs>? TrafficObserved;\n\n    void Start();\n    void Stop();\n}\n\npublic sealed class NetworkSnapshotChangedEventArgs : EventArgs\n{\n    public NetworkSnapshotChangedEventArgs(string adapterName, string adapterId)\n    {\n        AdapterName = adapterName;\n        AdapterId = adapterId;\n    }\n\n    public string AdapterName { get; }\n    public string AdapterId { get; }\n}\n\npublic sealed class NetworkTrafficEventArgs : EventArgs\n{\n    public NetworkTrafficEventArgs(string processName, long bytes, bool isReceive)\n    {\n        ProcessName = processName;\n        Bytes = bytes;\n        IsReceive = isReceive;\n    }\n\n    public string ProcessName { get; }\n    public long Bytes { get; }\n    public bool IsReceive { get; }\n}\n"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/IProcessIconService.cs",
    "content": "﻿namespace OpenNetMeter.PlatformAbstractions;\n\npublic interface IProcessIconService\n{\n    object? GetProcessIcon(string processName);\n}\n"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/IStartupRegistrationService.cs",
    "content": "﻿namespace OpenNetMeter.PlatformAbstractions;\n\npublic interface IStartupRegistrationService\n{\n    bool IsEnabled();\n    void SetEnabled(bool enabled, bool startMinimized);\n}\n"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/IUiDispatcher.cs",
    "content": "﻿using System;\n\nnamespace OpenNetMeter.PlatformAbstractions;\n\npublic interface IUiDispatcher\n{\n    bool CheckAccess();\n    void Post(Action action);\n}\n"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/IWindowService.cs",
    "content": "namespace OpenNetMeter.PlatformAbstractions;\n\npublic interface IWindowService\n{\n    void MinimizeMainWindow();\n    void CloseMainWindow();\n    void ShowAbout();\n}\n"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/OpenNetMeter.PlatformAbstractions.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n</Project>\n"
  },
  {
    "path": "OpenNetMeter.PlatformAbstractions/UiVisibility.cs",
    "content": "﻿namespace OpenNetMeter.PlatformAbstractions;\n\npublic enum UiVisibility\n{\n    Hidden,\n    Visible,\n    Collapsed,\n}\n"
  },
  {
    "path": "OpenNetMeter.Tests/NetworkProcessTests.cs",
    "content": "using System;\nusing System.Net;\nusing OpenNetMeter.Models;\nusing OpenNetMeter.Properties;\n\nnamespace OpenNetMeter.Tests;\n\npublic class NetworkProcessTests\n{\n    private static readonly byte[] LocalIPv4 = IPAddress.Parse(\"192.168.1.50\").GetAddressBytes();\n    private static readonly byte[] EmptyIPv6 = new byte[16];\n\n    [Fact]\n    public void RecvProcess_AccumulatesDownloadPerProcess()\n    {\n        using var networkType = new NetworkTypeScope(2);\n        using var netProc = CreateNetworkProcess();\n\n        netProc.TestInvokeRecvProcess(IPAddress.Parse(\"93.184.216.34\"), IPAddress.Parse(\"192.168.1.50\"), 400, \"chrome\");\n        netProc.TestInvokeRecvProcess(IPAddress.Parse(\"93.184.216.34\"), IPAddress.Parse(\"192.168.1.50\"), 200, \"chrome\");\n\n        Assert.Equal(600, netProc.CurrentSessionDownloadData);\n        Assert.True(netProc.MyProcesses!.TryGetValue(\"chrome\", out var process));\n        Assert.NotNull(process);\n        Assert.Equal(600, process!.CurrentDataRecv);\n        Assert.Equal(0, process.CurrentDataSend);\n    }\n\n    [Fact]\n    public void SendProcess_TracksUploadWhileBuffering()\n    {\n        using var networkType = new NetworkTypeScope(2);\n        using var netProc = CreateNetworkProcess();\n        netProc.IsBufferTime = true;\n\n        netProc.TestInvokeSendProcess(IPAddress.Parse(\"192.168.1.50\"), IPAddress.Parse(\"203.0.113.10\"), 1024, \"\");\n\n        Assert.Equal(1024, netProc.CurrentSessionUploadData);\n        Assert.Empty(netProc.MyProcesses!);\n        Assert.True(netProc.MyProcessesBuffer!.TryGetValue(\"System\", out var process));\n        Assert.NotNull(process);\n        Assert.Equal(1024, process!.CurrentDataSend);\n        Assert.Equal(0, process.CurrentDataRecv);\n    }\n\n    [Fact]\n    public void RecvProcess_PublicOnlyDropsPrivatePeer()\n    {\n        using var networkType = new NetworkTypeScope(1);\n        using var netProc = CreateNetworkProcess();\n\n        netProc.TestInvokeRecvProcess(IPAddress.Parse(\"192.168.0.10\"), IPAddress.Parse(\"192.168.1.50\"), 500, \"lan-app\");\n\n        Assert.Equal(0, netProc.CurrentSessionDownloadData);\n        Assert.Empty(netProc.MyProcesses!);\n    }\n\n    [Fact]\n    public void RecvProcess_PrivateOnlyAcceptsPrivateTraffic()\n    {\n        using var networkType = new NetworkTypeScope(0);\n        using var netProc = CreateNetworkProcess();\n\n        netProc.TestInvokeRecvProcess(IPAddress.Parse(\"10.0.0.5\"), IPAddress.Parse(\"192.168.1.50\"), 300, \"lan\");\n        netProc.TestInvokeRecvProcess(IPAddress.Parse(\"93.184.216.34\"), IPAddress.Parse(\"192.168.1.50\"), 200, \"lan\");\n\n        Assert.Equal(300, netProc.CurrentSessionDownloadData);\n        Assert.True(netProc.MyProcesses!.TryGetValue(\"lan\", out var process));\n        Assert.NotNull(process);\n        Assert.Equal(300, process!.CurrentDataRecv);\n    }\n\n    private static NetworkProcess CreateNetworkProcess()\n    {\n        var proc = new NetworkProcess();\n        proc.TestSetLocalIPs(LocalIPv4, EmptyIPv6);\n        return proc;\n    }\n\n    private sealed class NetworkTypeScope : IDisposable\n    {\n        private readonly int originalNetworkType;\n\n        public NetworkTypeScope(int networkType)\n        {\n            originalNetworkType = SettingsManager.Current.NetworkType;\n            SettingsManager.Current.NetworkType = networkType;\n        }\n\n        public void Dispose()\n        {\n            SettingsManager.Current.NetworkType = originalNetworkType;\n        }\n    }\n}\n"
  },
  {
    "path": "OpenNetMeter.Tests/OpenNetMeter.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>\n    <UseWPF>true</UseWPF>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"coverlet.collector\" Version=\"6.0.2\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.12.0\" />\n    <PackageReference Include=\"xunit\" Version=\"2.9.2\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.8.2\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Using Include=\"Xunit\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\\\OpenNetMeter.Old\\\\OpenNetMeter\\\\OpenNetMeter.Old.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "OpenNetMeter.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 17\r\nVisualStudioVersion = 17.2.32516.85\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"OpenNetMeter.Old\", \"OpenNetMeter.Old\\OpenNetMeter\\OpenNetMeter.Old.csproj\", \"{711263C7-44E1-4694-8B30-E65FBD2ECBF2}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{F081DC62-1772-4E91-BDCB-9D69FD1ED0A4}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\t.gitignore = .gitignore\r\n\t\tDirectory.Build.props = Directory.Build.props\r\n\t\t.github\\workflows\\dotnet.yml = .github\\workflows\\dotnet.yml\r\n\t\tLICENSE = LICENSE\r\n\t\tNOTICE = NOTICE\r\n\t\tREADME.md = README.md\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"DatabaseEngine\", \"OpenNetMeter.Old\\DatabaseEngine\\DatabaseEngine.csproj\", \"{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}\"\nEndProject\r\nProject(\"{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}\") = \"OpenNetMeter-Installer\", \"Installer\\OpenNetMeter-Installer.wixproj\", \"{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}\"\r\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E} = {46E06019-21CF-47C9-A4F2-EF81BF7FA49E}\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"OpenNetMeter.Tests\", \"OpenNetMeter.Tests\\OpenNetMeter.Tests.csproj\", \"{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}\"\r\nEndProject\r\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"OpenNetMeter.PlatformAbstractions\", \"OpenNetMeter.PlatformAbstractions\\OpenNetMeter.PlatformAbstractions.csproj\", \"{840B95F6-EBF8-4637-9B85-44E14A25E96A}\"\r\nEndProject\r\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"OpenNetMeter.Core\", \"OpenNetMeter.Core\\OpenNetMeter.Core.csproj\", \"{DC67BDB8-6F2B-4285-B787-4C438A925389}\"\r\nEndProject\r\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"OpenNetMeter\", \"OpenNetMeter\\OpenNetMeter.csproj\", \"{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}\"\nEndProject\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Any CPU = Debug|Any CPU\r\n\t\tDebug|ARM64 = Debug|ARM64\r\n\t\tDebug|x64 = Debug|x64\r\n\t\tDebug|x86 = Debug|x86\r\n\t\tRelease|Any CPU = Release|Any CPU\r\n\t\tRelease|ARM64 = Release|ARM64\r\n\t\tRelease|x64 = Release|x64\r\n\t\tRelease|x86 = Release|x86\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Debug|ARM64.ActiveCfg = Debug|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Debug|ARM64.Build.0 = Debug|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Debug|x64.ActiveCfg = Debug|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Debug|x64.Build.0 = Debug|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Debug|x86.ActiveCfg = Debug|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Debug|x86.Build.0 = Debug|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Release|ARM64.ActiveCfg = Release|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Release|ARM64.Build.0 = Release|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Release|x64.ActiveCfg = Release|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Release|x64.Build.0 = Release|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Release|x86.ActiveCfg = Release|Any CPU\r\n\t\t{711263C7-44E1-4694-8B30-E65FBD2ECBF2}.Release|x86.Build.0 = Release|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Debug|ARM64.ActiveCfg = Debug|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Debug|ARM64.Build.0 = Debug|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Debug|x64.ActiveCfg = Debug|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Debug|x64.Build.0 = Debug|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Debug|x86.ActiveCfg = Debug|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Debug|x86.Build.0 = Debug|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Release|ARM64.ActiveCfg = Release|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Release|ARM64.Build.0 = Release|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Release|x64.ActiveCfg = Release|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Release|x64.Build.0 = Release|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Release|x86.ActiveCfg = Release|Any CPU\r\n\t\t{49CF6C08-3735-449D-AB1E-33BF6EDEAE4E}.Release|x86.Build.0 = Release|Any CPU\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Debug|Any CPU.ActiveCfg = Debug|x64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Debug|Any CPU.Build.0 = Debug|x64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Debug|ARM64.ActiveCfg = Debug|ARM64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Debug|ARM64.Build.0 = Debug|ARM64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Debug|x64.ActiveCfg = Debug|x64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Debug|x64.Build.0 = Debug|x64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Debug|x86.ActiveCfg = Debug|x86\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Debug|x86.Build.0 = Debug|x86\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Release|Any CPU.ActiveCfg = Release|x64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Release|Any CPU.Build.0 = Release|x64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Release|ARM64.ActiveCfg = Release|ARM64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Release|ARM64.Build.0 = Release|ARM64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Release|x64.Build.0 = Release|x64\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Release|x86.ActiveCfg = Release|x86\r\n\t\t{99E49CF2-A3E6-42D9-B41C-06E2CFDC77AA}.Release|x86.Build.0 = Release|x86\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Debug|ARM64.ActiveCfg = Debug|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Debug|ARM64.Build.0 = Debug|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Debug|x64.ActiveCfg = Debug|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Debug|x64.Build.0 = Debug|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Debug|x86.ActiveCfg = Debug|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Debug|x86.Build.0 = Debug|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Release|ARM64.ActiveCfg = Release|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Release|ARM64.Build.0 = Release|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Release|x64.ActiveCfg = Release|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Release|x64.Build.0 = Release|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Release|x86.ActiveCfg = Release|Any CPU\r\n\t\t{88D1EC9C-FD79-49BA-A7D5-CC0AB271A85D}.Release|x86.Build.0 = Release|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Debug|ARM64.ActiveCfg = Debug|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Debug|ARM64.Build.0 = Debug|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Debug|x64.ActiveCfg = Debug|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Debug|x64.Build.0 = Debug|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Debug|x86.ActiveCfg = Debug|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Debug|x86.Build.0 = Debug|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Release|ARM64.ActiveCfg = Release|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Release|ARM64.Build.0 = Release|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Release|x64.ActiveCfg = Release|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Release|x64.Build.0 = Release|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Release|x86.ActiveCfg = Release|Any CPU\r\n\t\t{840B95F6-EBF8-4637-9B85-44E14A25E96A}.Release|x86.Build.0 = Release|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Debug|ARM64.ActiveCfg = Debug|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Debug|ARM64.Build.0 = Debug|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Debug|x64.ActiveCfg = Debug|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Debug|x64.Build.0 = Debug|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Debug|x86.ActiveCfg = Debug|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Debug|x86.Build.0 = Debug|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Release|ARM64.ActiveCfg = Release|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Release|ARM64.Build.0 = Release|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Release|x64.ActiveCfg = Release|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Release|x64.Build.0 = Release|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Release|x86.ActiveCfg = Release|Any CPU\r\n\t\t{DC67BDB8-6F2B-4285-B787-4C438A925389}.Release|x86.Build.0 = Release|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Debug|ARM64.ActiveCfg = Debug|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Debug|ARM64.Build.0 = Debug|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Debug|x64.ActiveCfg = Debug|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Debug|x64.Build.0 = Debug|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Debug|x86.ActiveCfg = Debug|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Debug|x86.Build.0 = Debug|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Release|ARM64.ActiveCfg = Release|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Release|ARM64.Build.0 = Release|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Release|x64.ActiveCfg = Release|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Release|x64.Build.0 = Release|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Release|x86.ActiveCfg = Release|Any CPU\r\n\t\t{46E06019-21CF-47C9-A4F2-EF81BF7FA49E}.Release|x86.Build.0 = Release|Any CPU\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {09D1D226-B6A2-47FB-9C0B-6D9E7C6313A2}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "README.md",
    "content": "\n# OpenNetMeter\n\nA simple program to monitor your network/data usage. Made for the average windows user.\n\n## Description\n\nThis program provides the following features,\n\n- network speed.\n- current connection's session data usage.\n- Data usage for today.\n- retrieve data usage up to the past 60 days and anywhere in between in detailed format.\n- A mini widget to show the network speed (can be placed over the taskbar). \n\n## Installation\n\n1. Download and Install [.NET 8.0 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0/runtime) desktop apps version for the latest release.\n2. Download the latest release .msi from this repository and run it (no more .zip format).\n3. This application requires admin privileges.\n4. Optional : To add this as a startup program, go to the settings tab and tick the checkbox.\n\n## Uninstallation\n\n1. simply uninstall from the control panel.\n2. for versions before, 0.12.1 and below, Simply delete the folder.\n    \n## Usage/Examples\n\n### Summary tab\n\n<img src=\"https://github.com/user-attachments/assets/f5d3e9dd-c64a-4ba4-979c-4e00786c27f6\" alt=\"OpenNetMeter summary tab with labels\" width=\"960\" />\n\n### History tab\n\n<img src=\"https://github.com/user-attachments/assets/5495baa4-4e03-4ba2-b5c2-5cb6c98be9f4\" alt=\"OpenNetMeter history tab with labels\" width=\"960\" />\n\n### Darkmode\n\n<img src=\"https://github.com/user-attachments/assets/4345217a-aac1-4543-afcf-f7f6b53cdb12\" alt=\"OpenNetMeter dark mode view\" width=\"960\" />\n\n### Network speed mini widget\n![onm-widget-v1](https://github.com/user-attachments/assets/13af9d12-8858-4e2b-b02f-348a2fe58d62)\n\n\n\n"
  },
  {
    "path": "Resources/documentation/README.md",
    "content": "This directory is meant for any documentation for OpenNetMeter"
  },
  {
    "path": "scripts/build-avalonia-msi.ps1",
    "content": "param(\n    [ValidateSet(\"Release\", \"Debug\")]\n    [string]$Configuration = \"Release\",\n    [ValidateSet(\"win-x64\")]\n    [string]$Runtime = \"win-x64\"\n)\n\n$ErrorActionPreference = \"Stop\"\n\n$repoRoot = Split-Path -Parent $PSScriptRoot\nSet-Location $repoRoot\n\n$propsPath = Join-Path $repoRoot \"Directory.Build.props\"\n[xml]$propsXml = Get-Content $propsPath\n$productVersion = $propsXml.Project.PropertyGroup.ProductVersion\n$productName = $propsXml.Project.PropertyGroup.ProductName\n\nif ([string]::IsNullOrWhiteSpace($productVersion)) {\n    throw \"ProductVersion not found in $propsPath\"\n}\n\nif ([string]::IsNullOrWhiteSpace($productName)) {\n    throw \"ProductName not found in $propsPath\"\n}\n\nWrite-Host \"Publishing Avalonia app...\"\nWrite-Host \"Cleaning Avalonia project...\"\ndotnet clean \".\\OpenNetMeter\\OpenNetMeter.csproj\" --configuration $Configuration\n\nWrite-Host \"Cleaning installer project...\"\ndotnet clean \".\\Installer\\OpenNetMeter-Installer.wixproj\" --configuration $Configuration\n\n$publishOutput = Join-Path $repoRoot \"_rc\\avalonia\\$Runtime\"\nif (Test-Path $publishOutput) {\n    Write-Host \"Removing previous publish output: $publishOutput\"\n    Remove-Item $publishOutput -Recurse -Force\n}\n\n& \".\\scripts\\publish-avalonia-rc.ps1\" -Runtime $Runtime -Configuration $Configuration\n\n$publishedExe = Join-Path $repoRoot \"_rc\\avalonia\\$Runtime\\$productName.exe\"\nif (-not (Test-Path $publishedExe)) {\n    throw \"Expected published exe not found: $publishedExe\"\n}\n\nWrite-Host \"Verified publish output: $publishedExe\"\nWrite-Host \"Building WiX installer...\"\ndotnet build \".\\Installer\\OpenNetMeter-Installer.wixproj\" --configuration $Configuration -m:1\n\n$candidateMsiPaths = @(\n    (Join-Path $repoRoot \"Installer\\bin\\$Configuration\\en-us\\$productName-$productVersion.msi\"),\n    (Join-Path $repoRoot \"Installer\\bin\\x64\\$Configuration\\en-us\\$productName-$productVersion.msi\")\n)\n\n$msiPath = $candidateMsiPaths | Where-Object { Test-Path $_ } | Select-Object -First 1\nif (-not $msiPath) {\n    throw \"MSI build completed but the expected MSI file was not found.\"\n}\n\nWrite-Host \"MSI ready: $msiPath\"\n"
  },
  {
    "path": "scripts/publish-avalonia-rc.ps1",
    "content": "param(\n    [ValidateSet(\"win-x64\", \"win-arm64\", \"win-x86\", \"linux-x64\")]\n    [string]$Runtime = \"win-x64\",\n    [ValidateSet(\"Debug\", \"Release\")]\n    [string]$Configuration = \"Release\"\n)\n\n$ErrorActionPreference = \"Stop\"\n\n$repoRoot = Split-Path -Parent $PSScriptRoot\n$project = Join-Path $repoRoot \"OpenNetMeter\\OpenNetMeter.csproj\"\n$output = Join-Path $repoRoot \"_rc\\avalonia\\$Runtime\\\"\n\nWrite-Host \"Publishing Avalonia RC...\"\nWrite-Host \"Project: $project\"\nWrite-Host \"Runtime: $Runtime\"\nWrite-Host \"Configuration: $Configuration\"\nWrite-Host \"Output: $output\"\n\ndotnet publish $project `\n  --configuration $Configuration `\n  --runtime $Runtime `\n  --self-contained true `\n  -p:PublishSingleFile=true `\n  -p:IncludeNativeLibrariesForSelfExtract=true `\n  --output $output\n\nWrite-Host \"Done. RC files are in $output\"\n"
  }
]