Full Code of exelban/stats for AI

master c457e0fafb58 cached
283 files
17.7 MB
660.7k tokens
73 symbols
1 requests
Download .txt
Showing preview only (2,635K chars total). Download the full file or copy to clipboard to get everything.
Repository: exelban/stats
Branch: master
Commit: c457e0fafb58
Files: 283
Total size: 17.7 MB

Directory structure:
gitextract_0i6vpw9i/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.md
│   └── workflows/
│       ├── build.yaml
│       ├── i18n.yaml
│       └── linter.yaml
├── .gitignore
├── .swiftlint.yml
├── Kit/
│   ├── Supporting Files/
│   │   ├── Assets.xcassets/
│   │   │   ├── Contents.json
│   │   │   ├── calendar.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── cancel.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── chart.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── close.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── refresh.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── settings.imageset/
│   │   │   │   └── Contents.json
│   │   │   └── tune.imageset/
│   │   │       └── Contents.json
│   │   ├── Info.plist
│   │   └── Kit.h
│   ├── Widgets/
│   │   ├── BarChart.swift
│   │   ├── Battery.swift
│   │   ├── Label.swift
│   │   ├── LineChart.swift
│   │   ├── Memory.swift
│   │   ├── Mini.swift
│   │   ├── NetworkChart.swift
│   │   ├── PieChart.swift
│   │   ├── Speed.swift
│   │   ├── Stack.swift
│   │   ├── State.swift
│   │   ├── Tachometer.swift
│   │   └── Text.swift
│   ├── constants.swift
│   ├── extensions.swift
│   ├── helpers.swift
│   ├── lldb/
│   │   ├── LICENSE.txt
│   │   ├── include/
│   │   │   ├── c.h
│   │   │   ├── cache.h
│   │   │   ├── comparator.h
│   │   │   ├── db.h
│   │   │   ├── dumpfile.h
│   │   │   ├── env.h
│   │   │   ├── export.h
│   │   │   ├── filter_policy.h
│   │   │   ├── iterator.h
│   │   │   ├── options.h
│   │   │   ├── slice.h
│   │   │   ├── status.h
│   │   │   ├── table.h
│   │   │   ├── table_builder.h
│   │   │   └── write_batch.h
│   │   ├── libleveldb.a
│   │   ├── lldb.h
│   │   └── lldb.m
│   ├── module/
│   │   ├── module.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── reader.swift
│   │   ├── widget.swift
│   │   └── window.swift
│   ├── plugins/
│   │   ├── Charts.swift
│   │   ├── DB.swift
│   │   ├── Logger.swift
│   │   ├── Reachability.swift
│   │   ├── Remote.swift
│   │   ├── Repeater.swift
│   │   ├── Store.swift
│   │   ├── SystemKit.swift
│   │   └── Updater.swift
│   ├── process.swift
│   ├── scripts/
│   │   ├── SMJobBlessUtil.py
│   │   ├── changelog.py
│   │   ├── i18n.py
│   │   ├── uninstall.sh
│   │   └── updater.sh
│   └── types.swift
├── LICENSE
├── LaunchAtLogin/
│   ├── Info.plist
│   ├── LaunchAtLogin.entitlements
│   └── main.swift
├── Makefile
├── Modules/
│   ├── Battery/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── readers.swift
│   │   └── settings.swift
│   ├── Bluetooth/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── readers.swift
│   │   └── settings.swift
│   ├── CPU/
│   │   ├── Info.plist
│   │   ├── bridge.h
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── readers.swift
│   │   ├── settings.swift
│   │   └── widget.swift
│   ├── Clock/
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── reader.swift
│   │   └── settings.swift
│   ├── Disk/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── header.h
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── readers.swift
│   │   ├── settings.swift
│   │   └── widget.swift
│   ├── GPU/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── reader.swift
│   │   ├── settings.swift
│   │   └── widget.swift
│   ├── Net/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── readers.swift
│   │   ├── settings.swift
│   │   └── widget.swift
│   ├── RAM/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── readers.swift
│   │   ├── settings.swift
│   │   └── widget.swift
│   └── Sensors/
│       ├── Info.plist
│       ├── bridge.h
│       ├── config.plist
│       ├── main.swift
│       ├── notifications.swift
│       ├── popup.swift
│       ├── portal.swift
│       ├── reader.m
│       ├── readers.swift
│       ├── settings.swift
│       └── values.swift
├── README.md
├── SMC/
│   ├── Helper/
│   │   ├── Info.plist
│   │   ├── Launchd.plist
│   │   ├── main.swift
│   │   └── protocol.swift
│   ├── Makefile
│   ├── main.swift
│   └── smc.swift
├── Stats/
│   ├── AppDelegate.swift
│   ├── Supporting Files/
│   │   ├── Assets.xcassets/
│   │   │   ├── AppIcon.appiconset/
│   │   │   │   └── Contents.json
│   │   │   ├── Contents.json
│   │   │   ├── ac_unit.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── apps.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── bug.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── devices/
│   │   │   │   ├── Contents.json
│   │   │   │   ├── imac.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── imacPro.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macMini.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macMini2020.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macMini2024.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macPro.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macPro2019.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macStudio.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macbookAir.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macbookAir4thGen.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macbookNeo.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macbookPro.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   └── macbookPro5thGen.imageset/
│   │   │   │       └── Contents.json
│   │   │   ├── donate.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── high-battery.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── low-battery.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── pause.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── power.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── record.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── resume.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── settings.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── stop.imageset/
│   │   │   │   └── Contents.json
│   │   │   └── support/
│   │   │       ├── Contents.json
│   │   │       ├── github.imageset/
│   │   │       │   └── Contents.json
│   │   │       ├── ko-fi.imageset/
│   │   │       │   └── Contents.json
│   │   │       ├── patreon.imageset/
│   │   │       │   └── Contents.json
│   │   │       └── paypal.imageset/
│   │   │           └── Contents.json
│   │   ├── Info.plist
│   │   ├── Stats/
│   │   │   └── en.xcloc/
│   │   │       ├── Localized Contents/
│   │   │       │   └── en.xliff
│   │   │       ├── Source Contents/
│   │   │       │   ├── LaunchAtLogin/
│   │   │       │   │   └── en.lproj/
│   │   │       │   │       └── InfoPlist.strings
│   │   │       │   ├── ModuleKit/
│   │   │       │   │   └── Supporting Files/
│   │   │       │   │       └── en.lproj/
│   │   │       │   │           └── InfoPlist.strings
│   │   │       │   ├── Modules/
│   │   │       │   │   ├── Battery/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   ├── CPU/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   ├── Disk/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   ├── GPU/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   ├── Memory/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   ├── Net/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   └── Sensors/
│   │   │       │   │       └── en.lproj/
│   │   │       │   │           └── InfoPlist.strings
│   │   │       │   ├── Stats/
│   │   │       │   │   └── Supporting Files/
│   │   │       │   │       └── en.lproj/
│   │   │       │   │           ├── InfoPlist.strings
│   │   │       │   │           └── Localizable.strings
│   │   │       │   └── StatsKit/
│   │   │       │       └── en.lproj/
│   │   │       │           └── InfoPlist.strings
│   │   │       └── contents.json
│   │   ├── Stats.entitlements
│   │   ├── ar.lproj/
│   │   │   └── Localizable.strings
│   │   ├── bg.lproj/
│   │   │   └── Localizable.strings
│   │   ├── ca.lproj/
│   │   │   └── Localizable.strings
│   │   ├── cs.lproj/
│   │   │   └── Localizable.strings
│   │   ├── da.lproj/
│   │   │   └── Localizable.strings
│   │   ├── de.lproj/
│   │   │   └── Localizable.strings
│   │   ├── el.lproj/
│   │   │   └── Localizable.strings
│   │   ├── en-AU.lproj/
│   │   │   └── Localizable.strings
│   │   ├── en-GB.lproj/
│   │   │   └── Localizable.strings
│   │   ├── en.lproj/
│   │   │   └── Localizable.strings
│   │   ├── es.lproj/
│   │   │   └── Localizable.strings
│   │   ├── et.lproj/
│   │   │   └── Localizable.strings
│   │   ├── fa.lproj/
│   │   │   └── Localizable.strings
│   │   ├── fi.lproj/
│   │   │   └── Localizable.strings
│   │   ├── fr.lproj/
│   │   │   └── Localizable.strings
│   │   ├── he.lproj/
│   │   │   └── Localizable.strings
│   │   ├── hi.lproj/
│   │   │   └── Localizable.strings
│   │   ├── hr.lproj/
│   │   │   └── Localizable.strings
│   │   ├── hu.lproj/
│   │   │   └── Localizable.strings
│   │   ├── id.lproj/
│   │   │   └── Localizable.strings
│   │   ├── it.lproj/
│   │   │   └── Localizable.strings
│   │   ├── ja.lproj/
│   │   │   └── Localizable.strings
│   │   ├── ko.lproj/
│   │   │   └── Localizable.strings
│   │   ├── menus.psd
│   │   ├── nb.lproj/
│   │   │   └── Localizable.strings
│   │   ├── nl.lproj/
│   │   │   └── Localizable.strings
│   │   ├── pl.lproj/
│   │   │   └── Localizable.strings
│   │   ├── popups.psd
│   │   ├── pt-BR.lproj/
│   │   │   └── Localizable.strings
│   │   ├── pt-PT.lproj/
│   │   │   └── Localizable.strings
│   │   ├── ro.lproj/
│   │   │   └── Localizable.strings
│   │   ├── ru.lproj/
│   │   │   └── Localizable.strings
│   │   ├── sk.lproj/
│   │   │   └── Localizable.strings
│   │   ├── sl.lproj/
│   │   │   └── Localizable.strings
│   │   ├── sv.lproj/
│   │   │   └── Localizable.strings
│   │   ├── th.lproj/
│   │   │   └── Localizable.strings
│   │   ├── tr.lproj/
│   │   │   └── Localizable.strings
│   │   ├── uk.lproj/
│   │   │   └── Localizable.strings
│   │   ├── vi.lproj/
│   │   │   └── Localizable.strings
│   │   ├── zh-Hans.lproj/
│   │   │   └── Localizable.strings
│   │   └── zh-Hant.lproj/
│   │       └── Localizable.strings
│   ├── Views/
│   │   ├── AppSettings.swift
│   │   ├── CombinedView.swift
│   │   ├── Dashboard.swift
│   │   ├── Settings.swift
│   │   ├── Setup.swift
│   │   ├── Support.swift
│   │   └── Update.swift
│   └── helpers.swift
├── Stats.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── IDEWorkspaceChecks.plist
│   └── xcshareddata/
│       └── xcschemes/
│           ├── SMC.xcscheme
│           ├── Stats.xcscheme
│           └── WidgetsExtension.xcscheme
├── Tests/
│   ├── Info.plist
│   └── RAM.swift
├── Widgets/
│   ├── Supporting Files/
│   │   ├── Assets.xcassets/
│   │   │   ├── AccentColor.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── AppIcon.appiconset/
│   │   │   │   └── Contents.json
│   │   │   ├── Contents.json
│   │   │   └── WidgetBackground.colorset/
│   │   │       └── Contents.json
│   │   ├── Info.plist
│   │   └── Widgets.entitlements
│   ├── UnitedWidget.swift
│   └── widgets.swift
└── exportOptions.plist

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

================================================
FILE: .github/FUNDING.yml
================================================
github: [exelban]
patreon: exelban
ko_fi: exelban
custom: ["https://www.paypal.com/donate?hosted_button_id=3DS5JHDBATMTC"]

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

---

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

**Details:**
 - Device: [e.g. Macbook Pro 2016]
 - macOS: [e.g. 10.15.5]
 - Application version: [e.g. 2.1.11]


================================================
FILE: .github/workflows/build.yaml
================================================
name: build

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master

concurrency:
  group: build-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4
      - run: xcodebuild -scheme Stats -destination 'platform=macOS' -configuration Release archive CODE_SIGNING_ALLOWED=NO


================================================
FILE: .github/workflows/i18n.yaml
================================================
name: i18n check

on:
  push:
    paths:
      - '.github/workflows/i18n.yaml'
      - '**/*.strings'
  pull_request:
    paths:
      - '.github/workflows/i18n.yaml'
      - '**/*.strings'

jobs:
  i18n:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v2
      - run: python3 Kit/scripts/i18n.py


================================================
FILE: .github/workflows/linter.yaml
================================================
name: Linter

on:
  push:
    paths:
      - '.github/workflows/linter.yaml'
      - '.swiftlint.yml'
      - '**/*.swift'
  pull_request:
    paths:
      - '.github/workflows/linter.yaml'
      - '.swiftlint.yml'
      - '**/*.swift'

jobs:
  SwiftLint:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - uses: norio-nomura/action-swiftlint@3.2.1

================================================
FILE: .gitignore
================================================
.DS_Store

Pods
Carthage
build
xcuserdata

Stats.dmg
Stats.app
create-dmg
dSYMs.zip
Stats.dmg.zip
SMC/smc

Cartfile.resolved
web


================================================
FILE: .swiftlint.yml
================================================
disabled_rules:
  - force_cast # todo
  - type_name # todo
  - cyclomatic_complexity # todo
  - trailing_whitespace
  - opening_brace
  - implicit_getter
  - implicit_optional_initialization
  - large_tuple
  - function_body_length

opt_in_rules:
  - control_statement
  - empty_count
  - trailing_newline
  - colon
  - comma

identifier_name:
  min_length: 1
  excluded:
    - AppUpdateIntervals
    - TemperatureUnits
    - SpeedBase
    - SensorsWidgetMode
    - SpeedPictogram
    - BatteryAdditionals
    - ShortLong
    - ReaderUpdateIntervals
    - NumbersOfProcesses
    - NetworkReaders
    - SensorsList
    - Alignments
    - _devices
    - _uuidAddress
    - AppleSiliconSensorsList
    - FanValues
    - CombinedModulesSpacings
    - BatteryInfo
    - PublicIPAddressRefreshIntervals
    - _values
    - _writeTS
    - LineChartHistory
    - SpeedPictogramColor
    - SensorsWidgetValue
    - access_token
    - refresh_token
    - device_code
    - user_code
    - verification_uri_complete
    - expires_in

line_length: 200

type_body_length:
  - 700
  - 1000

file_length:
  - 1400
  - 1800


================================================
FILE: Kit/Supporting Files/Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Kit/Supporting Files/Assets.xcassets/calendar.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "baseline_calendar_month_black_24pt_1x.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "baseline_calendar_month_black_24pt_2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "baseline_calendar_month_black_24pt_3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "properties" : {
    "template-rendering-intent" : "template"
  }
}


================================================
FILE: Kit/Supporting Files/Assets.xcassets/cancel.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "outline_close_white_12pt_1x.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "outline_close_white_12pt_2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "outline_close_white_12pt_3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "properties" : {
    "template-rendering-intent" : "template"
  }
}


================================================
FILE: Kit/Supporting Files/Assets.xcassets/chart.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "baseline_insert_chart_outlined_white_24pt_1x.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "baseline_insert_chart_outlined_white_24pt_2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "baseline_insert_chart_outlined_white_24pt_3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "properties" : {
    "template-rendering-intent" : "template"
  }
}


================================================
FILE: Kit/Supporting Files/Assets.xcassets/close.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "baseline_cancel_white_24pt_1x.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "baseline_cancel_white_24pt_2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "baseline_cancel_white_24pt_3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "properties" : {
    "template-rendering-intent" : "template"
  }
}


================================================
FILE: Kit/Supporting Files/Assets.xcassets/refresh.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "outline_refresh_black_18pt_1x.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "outline_refresh_black_18pt_2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "outline_refresh_black_18pt_3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "properties" : {
    "template-rendering-intent" : "template"
  }
}


================================================
FILE: Kit/Supporting Files/Assets.xcassets/settings.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "baseline_settings_black_24pt_1x.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "baseline_settings_black_24pt_2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "baseline_settings_black_24pt_3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "properties" : {
    "template-rendering-intent" : "template"
  }
}


================================================
FILE: Kit/Supporting Files/Assets.xcassets/tune.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "outline_tune_black_18pt_1x.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "outline_tune_black_18pt_2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "outline_tune_black_18pt_3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "properties" : {
    "template-rendering-intent" : "template"
  }
}


================================================
FILE: Kit/Supporting Files/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleVersion</key>
	<string>$(CURRENT_PROJECT_VERSION)</string>
	<key>NSHumanReadableCopyright</key>
	<string>Copyright © 2021 Serhiy Mytrovtsiy. All rights reserved.</string>
</dict>
</plist>


================================================
FILE: Kit/Supporting Files/Kit.h
================================================
//
//  Kit.h
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 05/02/2024
//  Using Swift 5.0
//  Running on macOS 14.3
//
//  Copyright © 2024 Serhiy Mytrovtsiy. All rights reserved.
//

#import <Foundation/Foundation.h>

//! Project version number for Kit.
FOUNDATION_EXPORT double KitVersionNumber;

//! Project version string for Kit.
FOUNDATION_EXPORT const unsigned char KitVersionString[];

#import "lldb.h"


================================================
FILE: Kit/Widgets/BarChart.swift
================================================
//
//  BarChart.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 26/04/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class BarChart: WidgetWrapper {
    private var labelState: Bool = false
    private var boxState: Bool = true
    private var frameState: Bool = false
    public var colorState: SColor = .systemAccent
    private var colors: [SColor] = SColor.allCases
    
    private var _value: [[ColorValue]] = [[]]
    private var _pressureLevel: RAMPressure = .normal
    private var _colorZones: colorZones = (0.6, 0.8)
    
    private var boxSettingsView: NSSwitch? = nil
    private var frameSettingsView: NSSwitch? = nil
    
    public var NSLabelCharts: [NSAttributedString] = []
    
    public init(title: String, config: NSDictionary?, preview: Bool = false) {
        var widgetTitle: String = title
        
        if config != nil {
            var configuration = config!
            if let titleFromConfig = config!["Title"] as? String {
                widgetTitle = titleFromConfig
            }
            
            if preview {
                if let previewConfig = config!["Preview"] as? NSDictionary {
                    configuration = previewConfig
                    if let value = configuration["Value"] as? String {
                        self._value = value.split(separator: ",").map{ ([ColorValue(Double($0) ?? 0)]) }
                    }
                }
            }
            
            if let label = configuration["Label"] as? Bool {
                self.labelState = label
            }
            if let box = configuration["Box"] as? Bool {
                self.boxState = box
            }
            if let unsupportedColors = configuration["Unsupported colors"] as? [String] {
                self.colors = self.colors.filter{ !unsupportedColors.contains($0.key) }
            }
            if let color = configuration["Color"] as? String {
                if let defaultColor = self.colors.first(where: { "\($0.self)" == color }) {
                    self.colorState = defaultColor
                }
            }
        }
        
        super.init(.barChart, title: widgetTitle, frame: CGRect(
            x: Constants.Widget.margin.x,
            y: Constants.Widget.margin.y,
            width: Constants.Widget.width + (2*Constants.Widget.margin.x),
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        self.canDrawConcurrently = true
        
        if !preview {
            self.boxState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_box", defaultValue: self.boxState)
            self.frameState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_frame", defaultValue: self.frameState)
            self.labelState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_label", defaultValue: self.labelState)
            self.colorState = SColor.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_color", defaultValue: self.colorState.key))
        }
        
        if preview {
            if self._value.isEmpty {
                self._value = [[ColorValue(0.72)], [ColorValue(0.38)]]
            }
            self.setFrameSize(NSSize(width: 36, height: self.frame.size.height))
            self.invalidateIntrinsicContentSize()
            self.display()
        }
        
        let style = NSMutableParagraphStyle()
        style.alignment = .center
        let stringAttributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 7, weight: .regular),
            NSAttributedString.Key.foregroundColor: NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: style
        ]
        
        for char in String(self.title.prefix(3)).uppercased().reversed() {
            let str = NSAttributedString.init(string: "\(char)", attributes: stringAttributes)
            self.NSLabelCharts.append(str)
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        var value: [[ColorValue]] = []
        var pressureLevel: RAMPressure = .normal
        var colorZones: colorZones = (0.6, 0.8)
        self.queue.sync {
            value = self._value
            pressureLevel = self._pressureLevel
            colorZones = self._colorZones
        }
        
        guard !value.isEmpty else {
            self.setWidth(0)
            return
        }
        
        var width: CGFloat = Constants.Widget.margin.x*2
        var x: CGFloat = 0
        let lineWidth = 1 / (NSScreen.main?.backingScaleFactor ?? 1)
        let offset = lineWidth / 2
        
        switch value.count {
        case 0, 1:
            width += 10 + (offset*2)
        case 2:
            width += 22
        case 3...4: // 3,4
            width += 30
        case 5...8: // 5,6,7,8
            width += 40
        case 9...12: // 9..12
            width += 50
        case 13...16: // 13..16
            width += 76
        case 17...32: // 17..32
            width += 84
        default: // > 32
            width += 118
        }
        
        if self.labelState {
            let letterHeight = self.frame.height / 3
            let letterWidth: CGFloat = 6.0
            
            var yMargin: CGFloat = 0
            for char in self.NSLabelCharts {
                let rect = CGRect(x: x, y: yMargin, width: letterWidth, height: letterHeight)
                char.draw(with: rect)
                yMargin += letterHeight
            }
            
            width += letterWidth + Constants.Widget.spacing
            x = letterWidth + Constants.Widget.spacing
        }
        
        let box = NSBezierPath(roundedRect: NSRect(
            x: x + offset,
            y: offset,
            width: width - x - (offset*2) - (Constants.Widget.margin.x*2),
            height: self.frame.size.height - (offset*2)
        ), xRadius: 2, yRadius: 2)
        
        if self.boxState {
            (isDarkMode ? NSColor.white : NSColor.black).set()
            box.stroke()
            box.fill()
        }
        
        let widthForBarChart = box.bounds.width
        let partitionMargin: CGFloat = 0.5
        let partitionsMargin: CGFloat = (CGFloat(value.count - 1)) * partitionMargin / CGFloat(value.count - 1)
        let partitionWidth: CGFloat = (widthForBarChart / CGFloat(value.count)) - CGFloat(partitionsMargin.isNaN ? 0 : partitionsMargin)
        let maxPartitionHeight: CGFloat = box.bounds.height
        
        x += offset
        for i in 0..<value.count {
            var y = offset
            for a in 0..<value[i].count {
                let partitionValue = value[i][a]
                let partitionHeight = maxPartitionHeight * CGFloat(partitionValue.value)
                let partition = NSBezierPath(rect: NSRect(x: x, y: y, width: partitionWidth, height: partitionHeight))
                
                if partitionValue.color == nil {
                    switch self.colorState {
                    case .systemAccent: NSColor.controlAccentColor.set()
                    case .utilization: partitionValue.value.usageColor(zones: colorZones, reversed: self.title == "Battery").set()
                    case .pressure: pressureLevel.pressureColor().set()
                    case .monochrome:
                        if self.boxState {
                            (isDarkMode ? NSColor.black : NSColor.white).set()
                        } else {
                            (isDarkMode ? NSColor.white : NSColor.black).set()
                        }
                    default: (self.colorState.additional as? NSColor ?? .controlAccentColor).set()
                    }
                } else {
                    partitionValue.color?.set()
                }
                
                partition.fill()
                partition.close()
                
                y += partitionHeight
            }
            
            x += partitionWidth + partitionMargin
        }
        
        if self.boxState || self.frameState {
            (isDarkMode ? NSColor.white : NSColor.black).set()
            box.lineWidth = lineWidth
            box.stroke()
        }
        
        self.setWidth(width)
    }
    
    public func setValue(_ newValue: [[ColorValue]]) {
        DispatchQueue.main.async(execute: {
            let tolerance: CGFloat = 0.01
            let isDifferent = self._value.count != newValue.count || zip(self._value, newValue).contains { row1, row2 in
                row1.count != row2.count || zip(row1, row2).contains { val1, val2 in
                    abs(val1.value - val2.value) > tolerance || val1.color != val2.color
                }
            }
            guard isDifferent else { return }
            self._value = newValue
            self.redraw()
        })
    }
    
    public func setPressure(_ newPressureLevel: RAMPressure) {
        DispatchQueue.main.async(execute: {
            guard self._pressureLevel != newPressureLevel else { return }
            self._pressureLevel = newPressureLevel
            self.redraw()
        })
    }
    
    public func setColorZones(_ newColorZones: colorZones) {
        DispatchQueue.main.async(execute: {
            guard self._colorZones != newColorZones else { return }
            self._colorZones = newColorZones
            self.redraw()
        })
    }
    
    // MARK: - Settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        let box = switchView(
            action: #selector(self.toggleBox),
            state: self.boxState
        )
        self.boxSettingsView = box
        let frame = switchView(
            action: #selector(self.toggleFrame),
            state: self.frameState
        )
        self.frameSettingsView = frame
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Label"), component: switchView(
                action: #selector(self.toggleLabel),
                state: self.labelState
            )),
            PreferencesRow(localizedString("Color"), component: selectView(
                action: #selector(self.toggleColor),
                items: self.colors,
                selected: self.colorState.key
            )),
            PreferencesRow(localizedString("Box"), component: box),
            PreferencesRow(localizedString("Frame"), component: frame)
        ]))
        
        return view
    }
    
    @objc private func toggleLabel(_ sender: NSControl) {
        self.labelState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_label", value: self.labelState)
        self.redraw()
    }
    
    @objc private func toggleBox(_ sender: NSControl) {
        self.boxState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_box", value: self.boxState)
        
        if self.frameState {
            self.frameSettingsView?.state = .off
            self.frameState = false
            Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_frame", value: self.frameState)
        }
        
        self.redraw()
    }
    
    @objc private func toggleFrame(_ sender: NSControl) {
        self.frameState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_frame", value: self.frameState)
        
        if self.boxState {
            self.boxSettingsView?.state = .off
            self.boxState = false
            Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_box", value: self.boxState)
        }
        
        self.redraw()
    }
    
    @objc private func toggleColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newColor = self.colors.first(where: { $0.key == key }) {
            self.colorState = newColor
        }
        
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_color", value: key)
        self.redraw()
    }
}


================================================
FILE: Kit/Widgets/Battery.swift
================================================
//
//  Battery.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 06/06/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class BatteryWidget: WidgetWrapper {
    private var additional: String = "none"
    private var timeFormat: String = "short"
    private var iconState: Bool = true
    private var colorState: Bool = false
    private var hideAdditionalWhenFull: Bool = true
    private var xlSizeState: Bool = false
    private var chargerIconInside: Bool = true
    
    private var _percentage: Double? = nil
    private var _time: Int = 0
    private var _charging: Bool = false
    private var _ACStatus: Bool = false
    private var _optimizedCharging: Bool = false
    
    public init(title: String, preview: Bool = false) {
        let widgetTitle: String = title
        
        super.init(.battery, title: widgetTitle, frame: CGRect(
            x: Constants.Widget.margin.x,
            y: Constants.Widget.margin.y,
            width: 30 + (2*Constants.Widget.margin.x),
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        self.canDrawConcurrently = true
        
        if !preview {
            self.additional = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_additional", defaultValue: self.additional)
            self.timeFormat = Store.shared.string(key: "\(self.title)_timeFormat", defaultValue: self.timeFormat)
            self.iconState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_icon", defaultValue: self.iconState)
            self.colorState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_color", defaultValue: self.colorState)
            self.hideAdditionalWhenFull = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_hideAdditionalWhenFull", defaultValue: self.hideAdditionalWhenFull)
            self.xlSizeState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_xlSize", defaultValue: self.xlSizeState)
            self.chargerIconInside = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_chargerInside", defaultValue: self.chargerIconInside)
        }
        
        if preview {
            self._percentage = 0.72
            self.additional = "none"
            self.iconState = true
            self.colorState = false
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        guard let ctx = NSGraphicsContext.current?.cgContext else { return }
        
        var percentage: Double? = nil
        var time: Int = 0
        var charging: Bool = false
        var ACStatus: Bool = false
        var optimizedCharging: Bool = false
        self.queue.sync {
            percentage = self._percentage
            time = self._time
            charging = self._charging
            ACStatus = self._ACStatus
            optimizedCharging = self._optimizedCharging
        }
        
        var width: CGFloat = 0
        var x: CGFloat = 0
        let isShortTimeFormat: Bool = self.timeFormat == "short"
        
        if !self.hideAdditionalWhenFull || (self.hideAdditionalWhenFull && percentage != 1 && !optimizedCharging) {
            switch self.additional {
            case "percentage":
                var value = "n/a"
                if let percentage {
                    value = "\(Int((percentage.rounded(toPlaces: 2)) * 100))%"
                }
                let rowWidth = self.drawOneRow(value: value, x: x).rounded(.up)
                width += rowWidth
                x += rowWidth + Constants.Widget.spacing
            case "time":
                let rowWidth = self.drawOneRow(
                    value: Double(time*60).printSecondsToHoursMinutesSeconds(short: isShortTimeFormat),
                    x: x
                ).rounded(.up)
                width += rowWidth
                x += rowWidth + Constants.Widget.spacing
            case "percentageAndTime":
                var value = "n/a"
                if let percentage {
                    value = "\(Int((percentage.rounded(toPlaces: 2)) * 100))%"
                }
                let rowWidth = self.drawTwoRows(
                    first: value,
                    second: Double(time*60).printSecondsToHoursMinutesSeconds(short: isShortTimeFormat),
                    x: x
                ).rounded(.up)
                width += rowWidth
                x += rowWidth + Constants.Widget.spacing
            case "timeAndPercentage":
                var value = "n/a"
                if let percentage {
                    value = "\(Int((percentage.rounded(toPlaces: 2)) * 100))%"
                }
                let rowWidth = self.drawTwoRows(
                    first: Double(time*60).printSecondsToHoursMinutesSeconds(short: isShortTimeFormat),
                    second: value,
                    x: x
                ).rounded(.up)
                width += rowWidth
                x += rowWidth + Constants.Widget.spacing
            default: break
            }
        }
        
        let batterySize: CGSize = self.xlSizeState ? CGSize(width: 26, height: 14) : CGSize(width: 22, height: 12)
        
        if ACStatus && !self.chargerIconInside {
            if x != 0 {
                width += Constants.Widget.spacing
                x += Constants.Widget.spacing
            }
            self.drawACIcon(
                ctx: ctx,
                center: CGPoint(x: x+3, y: self.frame.size.height/2),
                height: 12,
                charging: charging
            )
            width += 6
            x += 6 + Constants.Widget.spacing
        }
        
        let borderWidth: CGFloat = 1
        let batteryRadius: CGFloat = self.xlSizeState ? 3 : 2
        let offset: CGFloat = 0.5 // contant!
        width += batterySize.width + borderWidth*2 // add battery width
        if x != 0 {
            width += Constants.Widget.spacing
            x += Constants.Widget.spacing
        }
        
        let batteryFrame = NSBezierPath(roundedRect: NSRect(
            x: x + borderWidth + offset,
            y: ((self.frame.size.height - batterySize.height)/2) + offset,
            width: batterySize.width - borderWidth,
            height: batterySize.height - borderWidth
        ), xRadius: batteryRadius, yRadius: batteryRadius)
        
        NSColor.textColor.withAlphaComponent(0.5).set()
        batteryFrame.lineWidth = borderWidth
        batteryFrame.stroke()
        
        let bPX: CGFloat = batteryFrame.bounds.origin.x + batteryFrame.bounds.width + 1
        let bPY: CGFloat = batteryFrame.bounds.origin.y + batteryFrame.bounds.height/2 - 2
        let batteryPoint = NSBezierPath(roundedRect: NSRect(x: bPX - 1, y: bPY, width: 3, height: 4), xRadius: 2, yRadius: 2)
        batteryPoint.fill()
        
        let batteryPointSeparator = NSBezierPath()
        batteryPointSeparator.move(to: CGPoint(x: bPX, y: batteryFrame.bounds.origin.y))
        batteryPointSeparator.line(to: CGPoint(x: bPX, y: batteryFrame.bounds.origin.y + batteryFrame.bounds.height))
        ctx.saveGState()
        ctx.setBlendMode(.destinationOut)
        NSColor.white.set()
        batteryPointSeparator.lineWidth = borderWidth
        batteryPointSeparator.stroke()
        ctx.restoreGState()
        width += 2 // add battery point width
        
        if let percentage {
            let maxWidth = batterySize.width - offset*2 - borderWidth*2 - 1
            let innerWidth: CGFloat = max(1, maxWidth * CGFloat(percentage))
            let innerOffset: CGFloat = -offset + borderWidth + 1
            let innerRadius: CGFloat = self.xlSizeState ? 2 : 1
            var colorState = self.colorState
            let color = percentage.batteryColor(color: colorState)
            let innerPercentage = self.additional == "innerPercentage" && (!ACStatus || !self.chargerIconInside)
            
            if innerPercentage {
                colorState = false
                let innerUnderground = NSBezierPath(roundedRect: NSRect(
                    x: batteryFrame.bounds.origin.x + innerOffset,
                    y: batteryFrame.bounds.origin.y + innerOffset,
                    width: maxWidth,
                    height: batterySize.height - offset*2 - borderWidth*2 - 1
                ), xRadius: innerRadius, yRadius: innerRadius)
                (self.colorState ? color : NSColor.textColor).withAlphaComponent(0.5).set()
                innerUnderground.fill()
            }
            
            let inner = NSBezierPath(roundedRect: NSRect(
                x: batteryFrame.bounds.origin.x + innerOffset,
                y: batteryFrame.bounds.origin.y + innerOffset,
                width: innerWidth,
                height: batterySize.height - offset*2 - borderWidth*2 - 1
            ), xRadius: innerRadius, yRadius: innerRadius)
            
            color.set()
            inner.fill()
            
            if innerPercentage {
                let fontSize: CGFloat = self.xlSizeState ? 9 : 8
                let style = NSMutableParagraphStyle()
                style.alignment = .center
                let attributes = [
                    NSAttributedString.Key.font: NSFont.systemFont(ofSize: fontSize, weight: .bold),
                    NSAttributedString.Key.foregroundColor: NSColor.clear,
                    NSAttributedString.Key.paragraphStyle: style
                ]
                
                let value = "\(Int((percentage.rounded(toPlaces: 2)) * 100))"
                let rect = CGRect(x: inner.bounds.origin.x, y: (Constants.Widget.height-(fontSize+2))/2, width: maxWidth, height: fontSize)
                let str = NSAttributedString.init(string: value, attributes: attributes)
                
                ctx.saveGState()
                ctx.setBlendMode(.destinationIn)
                str.draw(with: rect)
                ctx.restoreGState()
            }
        } else {
            let attributes = [
                NSAttributedString.Key.font: NSFont.systemFont(ofSize: 11, weight: .regular),
                NSAttributedString.Key.foregroundColor: isDarkMode ? NSColor.white : NSColor.textColor,
                NSAttributedString.Key.paragraphStyle: NSMutableParagraphStyle()
            ]
            
            let batteryCenter: CGPoint = CGPoint(
                x: batteryFrame.bounds.origin.x + (batteryFrame.bounds.width/2),
                y: batteryFrame.bounds.origin.y + (batteryFrame.bounds.height/2)
            )
            
            let rect = CGRect(x: batteryCenter.x-3, y: batteryCenter.y-4, width: 8, height: 12)
            NSAttributedString.init(string: "?", attributes: attributes).draw(with: rect)
        }
        
        if ACStatus && self.chargerIconInside {
            let batteryCenter: CGPoint = CGPoint(
                x: batteryFrame.bounds.origin.x + (batteryFrame.bounds.width/2),
                y: batteryFrame.bounds.origin.y + (batteryFrame.bounds.height/2)
            )
            self.drawACIcon(
                ctx: ctx,
                center: batteryCenter,
                height: 12,
                charging: charging
            )
        }
        
        self.setWidth(width)
    }
    
    private func drawOneRow(value: String, x: CGFloat) -> CGFloat {
        let attributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 12, weight: .regular),
            NSAttributedString.Key.foregroundColor: isDarkMode ? NSColor.white : NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: NSMutableParagraphStyle()
        ]
        
        let rowWidth = value.widthOfString(usingFont: .systemFont(ofSize: 12, weight: .regular))
        let rect = CGRect(x: x, y: (Constants.Widget.height-13)/2, width: rowWidth, height: 12)
        let str = NSAttributedString.init(string: value, attributes: attributes)
        str.draw(with: rect)
        
        return rowWidth
    }
    
    private func drawTwoRows(first: String, second: String, x: CGFloat) -> CGFloat {
        let style = NSMutableParagraphStyle()
        style.alignment = .center
        let attributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 9, weight: .regular),
            NSAttributedString.Key.foregroundColor: NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: style
        ]
        let rowHeight: CGFloat = self.frame.height / 2
        
        let rowWidth = max(
            first.widthOfString(usingFont: .systemFont(ofSize: 9, weight: .regular)),
            second.widthOfString(usingFont: .systemFont(ofSize: 9, weight: .regular))
        )
        
        var str = NSAttributedString.init(string: first, attributes: attributes)
        str.draw(with: CGRect(x: x, y: rowHeight+1, width: rowWidth, height: rowHeight))
        
        str = NSAttributedString.init(string: second, attributes: attributes)
        str.draw(with: CGRect(x: x, y: 1, width: rowWidth, height: rowHeight))
        
        return rowWidth
    }
    
    private func drawACIcon(ctx: CGContext, center batteryCenter: CGPoint, height: CGFloat, charging: Bool) {
        var points: [CGPoint] = []
        
        if charging {
            let iconSize: CGSize = CGSize(width: 9, height: height + 6)
            let min = CGPoint(
                x: batteryCenter.x - (iconSize.width/2),
                y: batteryCenter.y - (iconSize.height/2)
            )
            let max = CGPoint(
                x: batteryCenter.x + (iconSize.width/2),
                y: batteryCenter.y + (iconSize.height/2)
            )
            
            points = [
                CGPoint(x: batteryCenter.x-3, y: min.y), // bottom
                CGPoint(x: max.x, y: batteryCenter.y+1.5),
                CGPoint(x: batteryCenter.x+1, y: batteryCenter.y+1.5),
                CGPoint(x: batteryCenter.x+3, y: max.y), // top
                CGPoint(x: min.x, y: batteryCenter.y-1.5),
                CGPoint(x: batteryCenter.x-1, y: batteryCenter.y-1.5)
            ]
        } else {
            let iconSize: CGSize = CGSize(width: 9, height: height + 2)
            let minY = batteryCenter.y - (iconSize.height/2)
            let maxY = batteryCenter.y + (iconSize.height/2)
            
            points = [
                CGPoint(x: batteryCenter.x-1.5, y: minY+0.5),
                
                CGPoint(x: batteryCenter.x+1.5, y: minY+0.5),
                CGPoint(x: batteryCenter.x+1.5, y: batteryCenter.y - 2.5),
                
                CGPoint(x: batteryCenter.x+4, y: batteryCenter.y + 0.5),
                CGPoint(x: batteryCenter.x+4, y: batteryCenter.y + 4.25),
                
                // right
                CGPoint(x: batteryCenter.x+2.75, y: batteryCenter.y + 4.25),
                CGPoint(x: batteryCenter.x+2.75, y: maxY-0.25),
                CGPoint(x: batteryCenter.x+0.25, y: maxY-0.25),
                CGPoint(x: batteryCenter.x+0.25, y: batteryCenter.y + 4.25),
                
                // left
                CGPoint(x: batteryCenter.x-0.25, y: batteryCenter.y + 4.25),
                CGPoint(x: batteryCenter.x-0.25, y: maxY-0.25),
                CGPoint(x: batteryCenter.x-2.75, y: maxY-0.25),
                CGPoint(x: batteryCenter.x-2.75, y: batteryCenter.y + 4.25),
                
                CGPoint(x: batteryCenter.x-4, y: batteryCenter.y + 4.25),
                CGPoint(x: batteryCenter.x-4, y: batteryCenter.y + 0.5),
                
                CGPoint(x: batteryCenter.x-1.5, y: batteryCenter.y - 2.5),
                CGPoint(x: batteryCenter.x-1.5, y: minY+0.5)
            ]
        }
        
        let linePath = NSBezierPath()
        linePath.move(to: CGPoint(x: points[0].x, y: points[0].y))
        for i in 1..<points.count {
            linePath.line(to: CGPoint(x: points[i].x, y: points[i].y))
        }
        linePath.line(to: CGPoint(x: points[0].x, y: points[0].y))
        
        NSColor.textColor.set()
        linePath.fill()
        
        ctx.saveGState()
        ctx.setBlendMode(.destinationOut)
        
        NSColor.textColor.set()
        linePath.lineWidth = 1
        linePath.stroke()
        
        ctx.restoreGState()
    }
    
    public func setValue(percentage: Double? = nil, ACStatus: Bool? = nil, isCharging: Bool? = nil, optimizedCharging: Bool? = nil, time: Int? = nil) {
        var updated: Bool = false
        let timeFormat: String = Store.shared.string(key: "\(self.title)_timeFormat", defaultValue: self.timeFormat)
        
        if self._percentage != percentage {
            self._percentage = percentage
            updated = true
        }
        if let status = ACStatus, self._ACStatus != status {
            self._ACStatus = status
            updated = true
        }
        if let charging = isCharging, self._charging != charging {
            self._charging = charging
            updated = true
        }
        if let time = time, self._time != time {
            self._time = time
            updated = true
        }
        if self.timeFormat != timeFormat {
            self.timeFormat = timeFormat
            updated = true
        }
        if let state = optimizedCharging, self._optimizedCharging != state {
            self._optimizedCharging = state
            updated = true
        }
        
        if updated {
            self.needsDisplay = true
            DispatchQueue.main.async(execute: {
                self.display()
            })
        }
    }
    
    // MARK: - Settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        var additionalOptions = BatteryAdditionals
        if self.title == "Bluetooth" {
            additionalOptions = additionalOptions.filter({ $0.key == "none" || $0.key == "percentage" })
        }
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Additional information"), component: selectView(
                action: #selector(self.toggleAdditional),
                items: additionalOptions,
                selected: self.additional
            )),
            PreferencesRow(localizedString("Hide additional information when full"), component: switchView(
                action: #selector(self.toggleHideAdditionalWhenFull),
                state: self.hideAdditionalWhenFull
            )),
            PreferencesRow(localizedString("Colorize"), component: switchView(
                action: #selector(self.toggleColor),
                state: self.colorState
            )),
            PreferencesRow(localizedString("XL size"), component: switchView(
                action: #selector(self.toggleXLSize),
                state: self.xlSizeState
            )),
            PreferencesRow(localizedString("Charger state inside the battery"), component: switchView(
                action: #selector(self.toggleChargerIconInside),
                state: self.chargerIconInside
            ))
        ]))
        
        return view
    }
    
    @objc private func toggleAdditional(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        self.additional = key
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_additional", value: key)
        self.display()
    }
    
    @objc private func toggleHideAdditionalWhenFull(_ sender: NSControl) {
        self.hideAdditionalWhenFull = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_hideAdditionalWhenFull", value: self.hideAdditionalWhenFull)
        self.display()
    }
    
    @objc private func toggleColor(_ sender: NSControl) {
        self.colorState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_color", value: self.colorState)
        self.display()
    }
    
    @objc private func toggleXLSize(_ sender: NSControl) {
        self.xlSizeState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_xlSize", value: self.xlSizeState)
        self.display()
    }
    
    @objc private func toggleChargerIconInside(_ sender: NSControl) {
        self.chargerIconInside = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_chargerInside", value: self.chargerIconInside)
        self.display()
    }
}

public class BatteryDetailsWidget: WidgetWrapper {
    private var mode: String = "percentage"
    private var timeFormat: String = "short"
    
    private var percentage: Double? = nil
    private var time: Int = 0
    
    public init(title: String, preview: Bool = false) {
        super.init(.batteryDetails, title: title, frame: CGRect(
            x: Constants.Widget.margin.x,
            y: Constants.Widget.margin.y,
            width: 20 + (2*Constants.Widget.margin.x),
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        self.canDrawConcurrently = true
        
        if preview {
            self.percentage = 0.72
            self.time = 415
            self.mode = "percentageAndTime"
        } else {
            self.mode = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_mode", defaultValue: self.mode)
            self.timeFormat = Store.shared.string(key: "\(self.title)_timeFormat", defaultValue: self.timeFormat)
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        var width: CGFloat = Constants.Widget.margin.x*2
        let x: CGFloat = Constants.Widget.margin.x
        let isShortTimeFormat: Bool = self.timeFormat == "short"
        
        switch self.mode {
        case "percentage":
            var value = "n/a"
            if let percentage = self.percentage {
                value = "\(Int((percentage.rounded(toPlaces: 2)) * 100))%"
            }
            width = self.drawOneRow(value: value, x: x).rounded(.up)
        case "time":
            width = self.drawOneRow(
                value: Double(self.time*60).printSecondsToHoursMinutesSeconds(short: isShortTimeFormat),
                x: x
            ).rounded(.up)
        case "percentageAndTime":
            var value = "n/a"
            if let percentage = self.percentage {
                value = "\(Int((percentage.rounded(toPlaces: 2)) * 100))%"
            }
            if self.time > 0 {
                width = self.drawTwoRows(
                    first: value,
                    second: Double(self.time*60).printSecondsToHoursMinutesSeconds(short: isShortTimeFormat),
                    x: x
                ).rounded(.up)
            } else {
                width = self.drawOneRow(value: value, x: x).rounded(.up)
            }
        case "timeAndPercentage":
            var value = "n/a"
            if let percentage = self.percentage {
                value = "\(Int((percentage.rounded(toPlaces: 2)) * 100))%"
            }
            if self.time > 0 {
                width = self.drawTwoRows(
                    first: Double(self.time*60).printSecondsToHoursMinutesSeconds(short: isShortTimeFormat),
                    second: value,
                    x: x
                ).rounded(.up)
            } else {
                width = self.drawOneRow(value: value, x: x).rounded(.up)
            }
        default: break
        }
        
        self.setWidth(width)
    }
    
    private func drawOneRow(value: String, x: CGFloat) -> CGFloat {
        let attributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 12, weight: .regular),
            NSAttributedString.Key.foregroundColor: isDarkMode ? NSColor.white : NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: NSMutableParagraphStyle()
        ]
        
        let rowWidth = value.widthOfString(usingFont: .systemFont(ofSize: 12, weight: .regular))
        let rect = CGRect(x: x, y: (Constants.Widget.height-12)/2, width: rowWidth, height: 12)
        let str = NSAttributedString.init(string: value, attributes: attributes)
        str.draw(with: rect)
        
        return rowWidth
    }
    
    private func drawTwoRows(first: String, second: String, x: CGFloat) -> CGFloat {
        let style = NSMutableParagraphStyle()
        style.alignment = .center
        let attributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 9, weight: .regular),
            NSAttributedString.Key.foregroundColor: NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: style
        ]
        let rowHeight: CGFloat = self.frame.height / 2
        
        let rowWidth = max(
            first.widthOfString(usingFont: .systemFont(ofSize: 9, weight: .regular)),
            second.widthOfString(usingFont: .systemFont(ofSize: 9, weight: .regular))
        )
        
        var str = NSAttributedString.init(string: first, attributes: attributes)
        str.draw(with: CGRect(x: x, y: rowHeight+1, width: rowWidth, height: rowHeight))
        
        str = NSAttributedString.init(string: second, attributes: attributes)
        str.draw(with: CGRect(x: x, y: 1, width: rowWidth, height: rowHeight))
        
        return rowWidth
    }
    
    public func setValue(percentage: Double? = nil, time: Int? = nil) {
        var updated: Bool = false
        let timeFormat: String = Store.shared.string(key: "\(self.title)_timeFormat", defaultValue: self.timeFormat)
        
        if self.percentage != percentage {
            self.percentage = percentage
            updated = true
        }
        if let time = time, self.time != time {
            self.time = time
            updated = true
        }
        if self.timeFormat != timeFormat {
            self.timeFormat = timeFormat
            updated = true
        }
        
        if updated {
            self.needsDisplay = true
            DispatchQueue.main.async(execute: {
                self.display()
            })
        }
    }
    
    // MARK: - Settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Details"), component: selectView(
                action: #selector(self.toggleMode),
                items: BatteryInfo,
                selected: self.mode
            ))
        ]))
        
        return view
    }
    
    @objc private func toggleMode(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        self.mode = key
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_mode", value: key)
        self.display()
    }
}


================================================
FILE: Kit/Widgets/Label.swift
================================================
//
//  Label.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 30/03/2021.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2021 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class Label: WidgetWrapper {
    private var label: String
    
    internal init(title: String, config: NSDictionary) {
        if let title = config["Title"] as? String {
            self.label = title
        } else {
            self.label = title
        }
        
        super.init(.label, title: title, frame: CGRect(
            x: 0,
            y: Constants.Widget.margin.y,
            width: 6 + (2*Constants.Widget.margin.x),
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        self.canDrawConcurrently = true
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        let size: CGSize = CGSize(width: 6, height: self.frame.height / 3)
        var margin: CGPoint = CGPoint(x: Constants.Widget.margin.x, y: 0)
        let style = NSMutableParagraphStyle()
        style.alignment = .center
        
        let stringAttributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 7, weight: .regular),
            NSAttributedString.Key.foregroundColor: NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: style
        ]
        
        for char in String(self.label.prefix(3)).uppercased().reversed() {
            let rect = CGRect(x: margin.x, y: margin.y, width: size.width, height: size.height)
            let str = NSAttributedString.init(string: "\(char)", attributes: stringAttributes)
            str.draw(with: rect)
            margin.y += size.height
        }
    }
}


================================================
FILE: Kit/Widgets/LineChart.swift
================================================
//
//  Chart.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 18/04/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class LineChart: WidgetWrapper {
    private var labelState: Bool = false
    private var boxState: Bool = true
    private var frameState: Bool = false
    private var valueState: Bool = false
    private var valueColorState: Bool = false
    private var colorState: SColor = .systemAccent
    private var historyCount: Int = 60
    private var scaleState: Scale = .none
    
    private var chart: LineChartView = LineChartView(frame: NSRect(
        x: 0,
        y: 0,
        width: 32,
        height: Constants.Widget.height - (2*Constants.Widget.margin.y)
    ), num: 60)
    private var colors: [SColor] = SColor.allCases.filter({ $0 != SColor.cluster })
    private var _value: Double = 0
    private var _pressureLevel: RAMPressure = .normal
    
    private var historyNumbers: [KeyValue_p] = [
        KeyValue_t(key: "30", value: "30"),
        KeyValue_t(key: "60", value: "60"),
        KeyValue_t(key: "90", value: "90"),
        KeyValue_t(key: "120", value: "120")
    ]
    private var width: CGFloat {
        get {
            switch self.historyCount {
            case 30:
                return 24
            case 60:
                return 32
            case 90:
                return 42
            case 120:
                return 52
            default:
                return 32
            }
        }
    }
    
    private var boxSettingsView: NSSwitch? = nil
    private var frameSettingsView: NSSwitch? = nil
    
    public var NSLabelCharts: [NSAttributedString] = []
    
    public init(title: String, config: NSDictionary?, preview: Bool = false) {
        var widgetTitle: String = title
        if config != nil {
            if let titleFromConfig = config!["Title"] as? String {
                widgetTitle = titleFromConfig
            }
            if let label = config!["Label"] as? Bool {
                self.labelState = label
            }
            if let box = config!["Box"] as? Bool {
                self.boxState = box
            }
            if let value = config!["Value"] as? Bool {
                self.valueState = value
            }
            if let unsupportedColors = config!["Unsupported colors"] as? [String] {
                self.colors = self.colors.filter{ !unsupportedColors.contains($0.key) }
            }
            if let color = config!["Color"] as? String {
                if let defaultColor = colors.first(where: { "\($0.self)" == color }) {
                    self.colorState = defaultColor
                }
            }
        }
        
        super.init(.lineChart, title: widgetTitle, frame: CGRect(
            x: Constants.Widget.margin.x,
            y: Constants.Widget.margin.y,
            width: 32 + (Constants.Widget.margin.x*2),
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        self.canDrawConcurrently = true
        
        if !preview {
            self.boxState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_box", defaultValue: self.boxState)
            self.frameState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_frame", defaultValue: self.frameState)
            self.valueState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_value", defaultValue: self.valueState)
            self.labelState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_label", defaultValue: self.labelState)
            self.valueColorState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_valueColor", defaultValue: self.valueColorState)
            self.colorState = SColor.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_color", defaultValue: self.colorState.key))
            self.historyCount = Store.shared.int(key: "\(self.title)_\(self.type.rawValue)_historyCount", defaultValue: self.historyCount)
            self.scaleState = Scale.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_scale", defaultValue: self.scaleState.key))
            
            self.chart.setScale(self.scaleState)
            self.chart.reinit(self.historyCount)
        }
        
        if self.labelState {
            self.setFrameSize(NSSize(width: Constants.Widget.width + 6 + (Constants.Widget.margin.x*2), height: self.frame.size.height))
        }
        
        if preview {
            var list: [DoubleValue] = []
            for _ in 0..<16 {
                list.append(DoubleValue(Double.random(in: 0..<1)))
            }
            self.chart.points = list
            self._value = 0.38
        }
        
        let style = NSMutableParagraphStyle()
        style.alignment = .center
        let stringAttributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 7, weight: .regular),
            NSAttributedString.Key.foregroundColor: NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: style
        ]
        
        for char in String(self.title.prefix(3)).uppercased().reversed() {
            let str = NSAttributedString.init(string: "\(char)", attributes: stringAttributes)
            self.NSLabelCharts.append(str)
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        guard let context = NSGraphicsContext.current?.cgContext else { return }
        
        var value: Double = 0
        var pressureLevel: RAMPressure = .normal
        self.queue.sync {
            value = self._value
            pressureLevel = self._pressureLevel
        }
        
        var width = self.width + (Constants.Widget.margin.x*2)
        var x: CGFloat = 0
        let lineWidth = 1 / (NSScreen.main?.backingScaleFactor ?? 1)
        let offset = lineWidth / 2
        var boxSize: CGSize = CGSize(width: self.width - (Constants.Widget.margin.x*2), height: self.frame.size.height)
        
        var color: NSColor = .controlAccentColor
        switch self.colorState {
        case .systemAccent: color = .controlAccentColor
        case .utilization: color = value.usageColor()
        case .pressure: color = pressureLevel.pressureColor()
        case .monochrome:
            if self.boxState {
                color = (isDarkMode ? NSColor.black : NSColor.white)
            } else {
                color = (isDarkMode ? NSColor.white : NSColor.black)
            }
        default: color = self.colorState.additional as? NSColor ?? .controlAccentColor
        }
        
        if self.labelState {
            let letterHeight = self.frame.height / 3
            let letterWidth: CGFloat = 6.0
            
            var yMargin: CGFloat = 0
            for char in self.NSLabelCharts {
                let rect = CGRect(x: x, y: yMargin, width: letterWidth, height: letterHeight)
                char.draw(with: rect)
                yMargin += letterHeight
            }
            
            width += letterWidth + Constants.Widget.spacing
            x = letterWidth + Constants.Widget.spacing
        }
        
        if self.valueState {
            let style = NSMutableParagraphStyle()
            style.alignment = .right
            
            var valueColor = isDarkMode ? NSColor.white : NSColor.black
            if self.valueColorState {
                valueColor = color
            }
            
            let stringAttributes = [
                NSAttributedString.Key.font: NSFont.systemFont(ofSize: 8, weight: .regular),
                NSAttributedString.Key.foregroundColor: valueColor,
                NSAttributedString.Key.paragraphStyle: style
            ]
            
            let rect = CGRect(x: x+2, y: boxSize.height-7, width: boxSize.width - 2, height: 7)
            let str = NSAttributedString.init(string: "\(Int((value.rounded(toPlaces: 2)) * 100))%", attributes: stringAttributes)
            str.draw(with: rect)
            
            boxSize.height = offset == 0.5 ? 10 : 9
        }
        
        let box = NSBezierPath(roundedRect: NSRect(
            x: x+offset,
            y: offset,
            width: self.width - offset*2,
            height: boxSize.height - (offset*2)
        ), xRadius: 2, yRadius: 2)
        
        if self.boxState {
            (isDarkMode ? NSColor.white : NSColor.black).set()
            box.stroke()
            box.fill()
            self.chart.transparent = false
        } else if self.frameState {
            self.chart.transparent = true
        } else {
            self.chart.transparent = true
        }
        
        context.saveGState()
        
        let chartFrame = NSRect(
            x: x+offset+lineWidth,
            y: offset,
            width: box.bounds.width - (offset*2+lineWidth),
            height: box.bounds.height - offset
        )
        self.chart.color = color
        self.chart.setFrameSize(NSSize(width: chartFrame.width, height: chartFrame.height))
        self.chart.draw(chartFrame)
        
        context.restoreGState()
        
        if self.boxState || self.frameState {
            (isDarkMode ? NSColor.white : NSColor.black).set()
            box.lineWidth = lineWidth
            box.stroke()
        }
        
        self.setWidth(width)
    }
    
    public func setValue(_ newValue: Double) {
        DispatchQueue.main.async(execute: {
            self._value = newValue
            self.chart.addValue(newValue)
            self.display()
        })
    }
    
    public func setPressure(_ newPressureLevel: RAMPressure) {
        DispatchQueue.main.async(execute: {
            guard self._pressureLevel != newPressureLevel else { return }
            self._pressureLevel = newPressureLevel
            self.display()
        })
    }
    
    // MARK: - Settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        let box = switchView(
            action: #selector(self.toggleBox),
            state: self.boxState
        )
        self.boxSettingsView = box
        let frame = switchView(
            action: #selector(self.toggleFrame),
            state: self.frameState
        )
        self.frameSettingsView = frame
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Label"), component: switchView(
                action: #selector(self.toggleLabel),
                state: self.labelState
            )),
            PreferencesRow(localizedString("Value"), component: switchView(
                action: #selector(self.toggleValue),
                state: self.valueState
            )),
            PreferencesRow(localizedString("Box"), component: box),
            PreferencesRow(localizedString("Frame"), component: frame),
            PreferencesRow(localizedString("Color"), component: selectView(
                action: #selector(self.toggleColor),
                items: self.colors,
                selected: self.colorState.key
            )),
            PreferencesRow(localizedString("Colorize value"), component: switchView(
                action: #selector(self.toggleValueColor),
                state: self.valueColorState
            )),
            PreferencesRow(localizedString("Number of reads in the chart"), component: selectView(
                action: #selector(self.toggleHistoryCount),
                items: self.historyNumbers,
                selected: "\(self.historyCount)"
            )),
            PreferencesRow(localizedString("Scaling"), component: selectView(
                action: #selector(self.toggleScale),
                items: Scale.allCases.filter({ $0 != .fixed }),
                selected: self.scaleState.key
            ))
        ]))
        
        return view
    }
    
    @objc private func toggleLabel(_ sender: NSControl) {
        self.labelState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_label", value: self.labelState)
        self.display()
    }
    
    @objc private func toggleBox(_ sender: NSControl) {
        self.boxState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_box", value: self.boxState)
        
        if self.frameState {
            self.frameSettingsView?.state = .off
            self.frameState = false
            Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_frame", value: self.frameState)
        }
        
        self.display()
    }
    
    @objc private func toggleFrame(_ sender: NSControl) {
        self.frameState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_frame", value: self.frameState)
        
        if self.boxState {
            self.boxSettingsView?.state = .off
            self.boxState = false
            Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_box", value: self.boxState)
        }
        
        self.display()
    }
    
    @objc private func toggleValue(_ sender: NSControl) {
        self.valueState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_value", value: self.valueState)
        self.display()
    }
    
    @objc private func toggleColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newColor = SColor.allCases.first(where: { $0.key == key }) {
            self.colorState = newColor
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_color", value: key)
        self.display()
    }
    
    @objc private func toggleValueColor(_ sender: NSControl) {
        self.valueColorState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_valueColor", value: self.valueColorState)
        self.display()
    }
    
    @objc private func toggleHistoryCount(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String, let value = Int(key) else { return }
        self.historyCount = value
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_historyCount", value: value)
        self.chart.reinit(value)
        self.display()
    }
    
    @objc private func toggleScale(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String,
              let value = Scale.allCases.first(where: { $0.key == key }) else { return }
        self.scaleState = value
        self.chart.setScale(value)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_scale", value: key)
        self.display()
    }
}


================================================
FILE: Kit/Widgets/Memory.swift
================================================
//
//  Memory.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 30/06/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class MemoryWidget: WidgetWrapper {
    private var orderReversedState: Bool = false
    private var value: (String, String) = ("0", "0")
    private var percentage: Double = 0
    private var pressureLevel: RAMPressure = .normal
    private var symbolsState: Bool = true
    private var colorState: SColor = .monochrome
    
    private let width: CGFloat = 50
    
    public init(title: String, config: NSDictionary?, preview: Bool = false) {
        if config != nil {
            var configuration = config!
            
            if preview {
                if let previewConfig = config!["Preview"] as? NSDictionary {
                    configuration = previewConfig
                    if let value = configuration["Value"] as? String {
                        let values = value.split(separator: ",").map{ (String($0) ) }
                        if values.count == 2 {
                            self.value.0 = values[0]
                            self.value.1 = values[1]
                        }
                    }
                }
            }
        }
        
        super.init(.memory, title: title, frame: CGRect(
            x: Constants.Widget.margin.x,
            y: Constants.Widget.margin.y,
            width: self.width + (Constants.Widget.margin.x*2),
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        self.canDrawConcurrently = true
        
        if !preview {
            self.orderReversedState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_orderReversed", defaultValue: self.orderReversedState)
            self.symbolsState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_symbols", defaultValue: self.symbolsState)
            self.colorState = SColor.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_color", defaultValue: self.colorState.key))
        }
        
        if preview {
            self.orderReversedState = false
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        let letterWidth: CGFloat = 8
        let rowHeight: CGFloat = self.frame.height / 2
        var width: CGFloat = self.width
        var x: CGFloat = 0
        
        let freeY: CGFloat = !self.orderReversedState ? rowHeight+1 : 1
        let usedY: CGFloat = !self.orderReversedState ? 1 : rowHeight+1
        
        let style = NSMutableParagraphStyle()
        style.alignment = .right
        var attributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 9, weight: .light),
            NSAttributedString.Key.foregroundColor: NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: style
        ]
        
        if self.symbolsState {
            var rect = CGRect(x: Constants.Widget.margin.x, y: freeY, width: letterWidth, height: rowHeight)
            var str = NSAttributedString.init(string: "F:", attributes: attributes)
            str.draw(with: rect)
            
            rect = CGRect(x: Constants.Widget.margin.x, y: usedY, width: letterWidth, height: rowHeight)
            str = NSAttributedString.init(string: "U:", attributes: attributes)
            str.draw(with: rect)
            
            x = letterWidth + Constants.Widget.spacing*2
            width += x
        }
        
        var freeColor: NSColor = .controlAccentColor
        var usedColor: NSColor = .controlAccentColor
        switch self.colorState {
        case .systemAccent: 
            freeColor = .controlAccentColor
            usedColor = .controlAccentColor
        case .utilization: 
            freeColor = (1 - self.percentage).usageColor()
            usedColor = self.percentage.usageColor()
        case .pressure:
            usedColor = self.pressureLevel.pressureColor()
            freeColor = self.pressureLevel.pressureColor()
        case .monochrome:
            freeColor = (isDarkMode ? NSColor.white : NSColor.black)
            usedColor = (isDarkMode ? NSColor.white : NSColor.black)
        default: 
            freeColor = self.colorState.additional as? NSColor ?? .controlAccentColor
            usedColor = self.colorState.additional as? NSColor ?? .controlAccentColor
        }
        
        attributes[NSAttributedString.Key.foregroundColor] = freeColor
        var rect = CGRect(x: x, y: freeY, width: width - x, height: rowHeight)
        var str = NSAttributedString.init(string: self.value.0, attributes: attributes)
        str.draw(with: rect)
        
        attributes[NSAttributedString.Key.foregroundColor] = usedColor
        rect = CGRect(x: x, y: usedY, width: width - x, height: rowHeight)
        str = NSAttributedString.init(string: self.value.1, attributes: attributes)
        str.draw(with: rect)
        
        self.setWidth(width + (Constants.Widget.margin.x*2))
    }
    
    public func setValue(_ value: (String, String), usedPercentage: Double) {
        self.value = value
        self.percentage = usedPercentage
        
        DispatchQueue.main.async(execute: {
            self.display()
        })
    }
    
    public func setPressure(_ newPressureLevel: RAMPressure) {
        guard self.pressureLevel != newPressureLevel else { return }
        self.pressureLevel = newPressureLevel
        DispatchQueue.main.async(execute: {
            self.display()
        })
    }
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Color"), component: selectView(
                action: #selector(self.toggleColor),
                items: SColor.allCases.filter({ $0 != .cluster }),
                selected: self.colorState.key
            )),
            PreferencesRow(localizedString("Show symbols"), component: switchView(
                action: #selector(self.toggleSymbols),
                state: self.symbolsState
            )),
            PreferencesRow(localizedString("Reverse order"), component: switchView(
                action: #selector(self.toggleOrder),
                state: self.orderReversedState
            ))
        ]))
        
        return view
    }
    
    @objc private func toggleOrder(_ sender: NSControl) {
        self.orderReversedState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_orderReversed", value: self.orderReversedState)
        self.display()
    }
    
    @objc private func toggleSymbols(_ sender: NSControl) {
        self.symbolsState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_symbols", value: self.symbolsState)
        self.display()
    }
    
    @objc private func toggleColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newColor = SColor.allCases.first(where: { $0.key == key }) {
            self.colorState = newColor
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_color", value: key)
        self.display()
    }
}


================================================
FILE: Kit/Widgets/Mini.swift
================================================
//
//  Mini.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 10/04/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class Mini: WidgetWrapper {
    private var labelState: Bool = true
    private var colorState: SColor = .monochrome
    private var alignmentState: String = "left"
    
    private var colors: [SColor] = SColor.allCases
    
    private var _value: Double = 0
    private var _pressureLevel: RAMPressure = .normal
    private var _colorZones: colorZones = (0.6, 0.8)
    private var _suffix: String = "%"
    
    private var defaultLabel: String
    private var _label: String
    
    private var width: CGFloat {
        (self.labelState ? 31 : 36) + (2*Constants.Widget.margin.x)
    }
    
    private var alignment: NSTextAlignment {
        if let alignmentPair = Alignments.first(where: { $0.key == self.alignmentState }) {
            return alignmentPair.additional as? NSTextAlignment ?? .left
        }
        return .left
    }
    
    public init(title: String, config: NSDictionary?, preview: Bool = false) {
        var widgetTitle: String = title
        if config != nil {
            var configuration = config!
            
            if preview {
                if let previewConfig = config!["Preview"] as? NSDictionary {
                    configuration = previewConfig
                    if let value = configuration["Value"] as? String {
                        self._value = Double(value) ?? 0
                    }
                }
            }
            
            if let titleFromConfig = configuration["Title"] as? String {
                widgetTitle = titleFromConfig
            }
            if let label = configuration["Label"] as? Bool {
                self.labelState = label
            }
            if let unsupportedColors = configuration["Unsupported colors"] as? [String] {
                self.colors = self.colors.filter{ !unsupportedColors.contains($0.key) }
            }
            if let color = configuration["Color"] as? String {
                if let defaultColor = colors.first(where: { "\($0.self)" == color }) {
                    self.colorState = defaultColor
                }
            }
        }
        
        self.defaultLabel = widgetTitle
        self._label = widgetTitle
        super.init(.mini, title: widgetTitle, frame: CGRect(
            x: 0,
            y: Constants.Widget.margin.y,
            width: Constants.Widget.width + (2*Constants.Widget.margin.x),
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        self.canDrawConcurrently = true
        
        if !preview {
            self.colorState = SColor.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_color", defaultValue: self.colorState.key))
            self.labelState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_label", defaultValue: self.labelState)
            self.alignmentState = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_alignment", defaultValue: self.alignmentState)
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        var value: Double = 0
        var pressureLevel: RAMPressure = .normal
        var colorZones: colorZones = (0.6, 0.8)
        var label: String = ""
        var suffix: String = ""
        self.queue.sync {
            value = self._value
            pressureLevel = self._pressureLevel
            colorZones = self._colorZones
            label = self._label
            suffix = self._suffix
        }
        
        let valueSize: CGFloat = self.labelState ? 12 : 14
        var origin: CGPoint = CGPoint(x: Constants.Widget.margin.x, y: (Constants.Widget.height-valueSize)/2)
        let style = NSMutableParagraphStyle()
        style.alignment = self.labelState ? self.alignment : .center
        
        if self.labelState {
            let style = NSMutableParagraphStyle()
            style.alignment = self.alignment
            
            let stringAttributes = [
                NSAttributedString.Key.font: NSFont.systemFont(ofSize: 7, weight: .light),
                NSAttributedString.Key.foregroundColor: isDarkMode ? NSColor.white : NSColor.textColor,
                NSAttributedString.Key.paragraphStyle: style
            ]
            let rect = CGRect(x: origin.x, y: 12, width: self.width - (Constants.Widget.margin.x*2), height: 7)
            let str = NSAttributedString.init(string: label, attributes: stringAttributes)
            str.draw(with: rect)
            
            origin.y = 1
        }
        
        var color: NSColor = .controlAccentColor
        switch self.colorState {
        case .systemAccent: color = .controlAccentColor
        case .utilization: color = value.usageColor(zones: colorZones, reversed: self.title == "BAT")
        case .pressure: color = pressureLevel.pressureColor()
        case .monochrome: color = (isDarkMode ? NSColor.white : NSColor.black)
        default: color = self.colorState.additional as? NSColor ?? .controlAccentColor
        }
        
        let stringAttributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: valueSize, weight: .regular),
            NSAttributedString.Key.foregroundColor: color,
            NSAttributedString.Key.paragraphStyle: style
        ]
        let rect = CGRect(x: origin.x, y: origin.y, width: self.width - (Constants.Widget.margin.x*2), height: valueSize+1)
        let str = NSAttributedString.init(string: "\(Int(value.rounded(toPlaces: 2) * 100))\(suffix)", attributes: stringAttributes)
        str.draw(with: rect)
        
        self.setWidth(width)
    }
    
    public func setValue(_ newValue: Double) {
        guard self._value != newValue else { return }
        self._value = newValue
        DispatchQueue.main.async(execute: {
            self.display()
        })
    }
    
    public func setPressure(_ newPressureLevel: RAMPressure) {
        guard self._pressureLevel != newPressureLevel else { return }
        self._pressureLevel = newPressureLevel
        DispatchQueue.main.async(execute: {
            self.needsDisplay = true
        })
    }
    
    public func setTitle(_ newTitle: String?) {
        var title = self.defaultLabel
        if let new = newTitle {
            title = new
        }
        guard self._label != title else { return }
        self._label = title
        DispatchQueue.main.async(execute: {
            self.needsDisplay = true
        })
    }
    
    public func setColorZones(_ newColorZones: colorZones) {
        guard self._colorZones != newColorZones else { return }
        self._colorZones = newColorZones
        DispatchQueue.main.async(execute: {
            self.display()
        })
    }
    
    public func setSuffix(_ newSuffix: String) {
        guard self._suffix != newSuffix else { return }
        self._suffix = newSuffix
        DispatchQueue.main.async(execute: {
            self.display()
        })
    }
    
    // MARK: - Settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Label"), component: switchView(
                action: #selector(self.toggleLabel),
                state: self.labelState
            )),
            PreferencesRow(localizedString("Color"), component: selectView(
                action: #selector(self.toggleColor),
                items: self.colors,
                selected: self.colorState.key
            )),
            PreferencesRow(localizedString("Alignment"), component: selectView(
                action: #selector(self.toggleAlignment),
                items: Alignments,
                selected: self.alignmentState
            ))
        ]))
        
        return view
    }
    
    @objc private func toggleColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newColor = SColor.allCases.first(where: { $0.key == key }) {
            self.colorState = newColor
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_color", value: key)
        self.display()
    }
    
    @objc private func toggleLabel(_ sender: NSControl) {
        self.labelState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_label", value: self.labelState)
        self.display()
    }
    
    @objc private func toggleAlignment(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newAlignment = Alignments.first(where: { $0.key == key }) {
            self.alignmentState = newAlignment.key
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_alignment", value: key)
        self.display()
    }
}


================================================
FILE: Kit/Widgets/NetworkChart.swift
================================================
//
//  NetworkChart.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 19/01/2021.
//  Using Swift 5.0.
//  Running on macOS 11.1.
//
//  Copyright © 2021 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class NetworkChart: WidgetWrapper {
    private var boxState: Bool = false
    private var frameState: Bool = false
    private var labelState: Bool = false
    private var historyCount: Int = 60
    private var downloadColor: SColor = .secondBlue
    private var uploadColor: SColor = .secondRed
    private var scaleState: Scale = .linear
    private var reverseOrderState: Bool = false
    
    private var points: [(Double, Double)] = Array(repeating: (0, 0), count: 60)
    
    private var width: CGFloat {
        get {
            switch self.historyCount {
            case 30:
                return 22
            case 60:
                return 30
            case 90:
                return 40
            case 120:
                return 50
            default:
                return 30
            }
        }
    }
    
    private var historyNumbers: [KeyValue_p] = [
        KeyValue_t(key: "30", value: "30"),
        KeyValue_t(key: "60", value: "60"),
        KeyValue_t(key: "90", value: "90"),
        KeyValue_t(key: "120", value: "120")
    ]
    private var colors: [SColor] = SColor.allCases
    
    private var boxSettingsView: NSSwitch? = nil
    private var frameSettingsView: NSSwitch? = nil
    
    public var NSLabelCharts: [NSAttributedString] = []
    
    public init(title: String, config: NSDictionary?, preview: Bool = false) {
        var widgetTitle: String = title
        if let config = config {
            if let titleFromConfig = config["Title"] as? String {
                widgetTitle = titleFromConfig
            }
            if let unsupportedColors = config["Unsupported colors"] as? [String] {
                self.colors = self.colors.filter{ !unsupportedColors.contains($0.key) }
            }
        }
        
        super.init(.networkChart, title: widgetTitle, frame: CGRect(
            x: Constants.Widget.margin.x,
            y: Constants.Widget.margin.y,
            width: 30 + (2*Constants.Widget.margin.x),
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        self.canDrawConcurrently = true
        
        if !preview {
            self.boxState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_box", defaultValue: self.boxState)
            self.frameState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_frame", defaultValue: self.frameState)
            self.labelState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_label", defaultValue: self.labelState)
            self.historyCount = Store.shared.int(key: "\(self.title)_\(self.type.rawValue)_historyCount", defaultValue: self.historyCount)
            self.downloadColor = SColor.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_downloadColor", defaultValue: self.downloadColor.key))
            self.uploadColor = SColor.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_uploadColor", defaultValue: self.uploadColor.key))
            self.scaleState = Scale.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_scale", defaultValue: self.scaleState.key))
            self.reverseOrderState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_reverseOrder", defaultValue: self.reverseOrderState)
        }
        
        if preview {
            var list: [(Double, Double)] = []
            for _ in 0..<60 {
                list.append((Double.random(in: 0..<23), Double.random(in: 0..<23)))
            }
            self.points = list
        }
        
        let style = NSMutableParagraphStyle()
        style.alignment = .center
        let stringAttributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 7, weight: .regular),
            NSAttributedString.Key.foregroundColor: NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: style
        ]
        
        for char in String(self.title.prefix(3)).uppercased().reversed() {
            let str = NSAttributedString.init(string: "\(char)", attributes: stringAttributes)
            self.NSLabelCharts.append(str)
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        guard let context = NSGraphicsContext.current?.cgContext else { return }
        
        var points: [(Double, Double)] = []
        var labelState: Bool = false
        var boxState: Bool = false
        var frameState: Bool = false
        var scaleState: Scale = .linear
        var reverseOrderState: Bool = false
        var originWidth: CGFloat = 0
        var labelString: [NSAttributedString] = []
        var downloadColor: SColor = .secondBlue
        var uploadColor: SColor = .secondRed
        self.queue.sync {
            points = self.points
            labelState = self.labelState
            boxState = self.boxState
            frameState = self.frameState
            scaleState = self.scaleState
            reverseOrderState = self.reverseOrderState
            labelString = self.NSLabelCharts
            originWidth = self.width
            downloadColor = self.downloadColor
            uploadColor = self.uploadColor
        }
        
        let lineWidth = 1 / (NSScreen.main?.backingScaleFactor ?? 1)
        let offset = lineWidth / 2
        let boxSize: CGSize = CGSize(width: originWidth - (Constants.Widget.margin.x*2), height: self.frame.size.height)
        var x: CGFloat = 0
        var width = originWidth + (Constants.Widget.margin.x*2)
        
        if labelState {
            let letterHeight = self.frame.height / 3
            let letterWidth: CGFloat = 6.0
            
            var yMargin: CGFloat = 0
            for char in labelString {
                let rect = CGRect(x: x, y: yMargin, width: letterWidth, height: letterHeight)
                char.draw(with: rect)
                yMargin += letterHeight
            }
            
            width += letterWidth + Constants.Widget.spacing
            x = letterWidth + Constants.Widget.spacing
        }
        
        let box = NSBezierPath(roundedRect: NSRect(
            x: x + offset,
            y: offset,
            width: originWidth - offset*2,
            height: boxSize.height - (offset*2)
        ), xRadius: 2, yRadius: 2)
        
        if boxState {
            (isDarkMode ? NSColor.white : NSColor.black).set()
            box.stroke()
            box.fill()
        }
        
        context.saveGState()
        
        let chartFrame = NSRect(
            x: x+offset+lineWidth,
            y: offset,
            width: box.bounds.width - (offset*2+lineWidth),
            height: box.bounds.height - offset
        )
        var topMax: Double = (reverseOrderState ? points.map{ $0.1 }.max() : points.map{ $0.0 }.max()) ?? 0
        var bottomMax: Double = (reverseOrderState ? points.map{ $0.0 }.max() : points.map{ $0.1 }.max()) ?? 0
        if topMax == 0 {
            topMax = 1
        }
        if bottomMax == 0 {
            bottomMax = 1
        }
        
        let zero: CGFloat = (chartFrame.height/2) + chartFrame.origin.y
        let xRatio: CGFloat = (chartFrame.width + (lineWidth*3)) / CGFloat(points.count)
        let xCenter: CGFloat = chartFrame.height/2 + chartFrame.origin.y
        
        let columnXPoint = { (point: Int) -> CGFloat in
            return (CGFloat(point) * xRatio) + (chartFrame.origin.x - lineWidth)
        }
        
        let topYPoint = { (point: Int) -> CGFloat in
            let value = reverseOrderState ? points[point].1 : points[point].0
            return scaleValue(scale: scaleState, value: value, maxValue: topMax, zeroValue: 256.0, maxHeight: chartFrame.height/2, limit: 1) + xCenter
        }
        let bottomYPoint = { (point: Int) -> CGFloat in
            let value = reverseOrderState ? points[point].0 : points[point].1
            return xCenter - scaleValue(scale: scaleState, value: value, maxValue: bottomMax, zeroValue: 256.0, maxHeight: chartFrame.height/2, limit: 1)
        }
        
        let topLinePath = NSBezierPath()
        topLinePath.move(to: CGPoint(x: columnXPoint(0), y: topYPoint(0)))
        let bottomLinePath = NSBezierPath()
        bottomLinePath.move(to: CGPoint(x: columnXPoint(0), y: bottomYPoint(0)))
        
        for i in 1..<points.count {
            topLinePath.line(to: CGPoint(x: columnXPoint(i), y: topYPoint(i)))
            bottomLinePath.line(to: CGPoint(x: columnXPoint(i), y: bottomYPoint(i)))
        }
        
        let topColor = (reverseOrderState ? self.uploadColor : downloadColor).additional as? NSColor
        let bottomColor = (reverseOrderState ? self.downloadColor : uploadColor).additional as? NSColor
        
        bottomColor?.setStroke()
        topLinePath.lineWidth = lineWidth
        topLinePath.stroke()
        
        topColor?.setStroke()
        bottomLinePath.lineWidth = lineWidth
        bottomLinePath.stroke()
        
        context.restoreGState()
        context.saveGState()
        
        guard let topUnderLinePath = topLinePath.copy() as? NSBezierPath else { return }
        topUnderLinePath.line(to: CGPoint(x: columnXPoint(points.count - 1), y: zero))
        topUnderLinePath.line(to: CGPoint(x: columnXPoint(0), y: zero))
        topUnderLinePath.close()
        topUnderLinePath.addClip()
        bottomColor?.withAlphaComponent(0.5).setFill()
        let topFillRect = NSRect(x: chartFrame.origin.x - lineWidth, y: chartFrame.origin.y, width: chartFrame.width + (lineWidth*3), height: chartFrame.height)
        NSBezierPath(rect: topFillRect).fill()
        
        context.restoreGState()
        context.saveGState()
        
        guard let bottomUnderLinePath = bottomLinePath.copy() as? NSBezierPath else { return }
        bottomUnderLinePath.line(to: CGPoint(x: columnXPoint(points.count - 1), y: zero))
        bottomUnderLinePath.line(to: CGPoint(x: columnXPoint(0), y: zero))
        bottomUnderLinePath.close()
        bottomUnderLinePath.addClip()
        topColor?.withAlphaComponent(0.5).setFill()
        let bottomFillRect = NSRect(x: chartFrame.origin.x - lineWidth, y: chartFrame.origin.y, width: chartFrame.width + (lineWidth*3), height: chartFrame.height)
        NSBezierPath(rect: bottomFillRect).fill()
        
        context.restoreGState()
        
        if boxState || frameState {
            (isDarkMode ? NSColor.white : NSColor.black).set()
            box.lineWidth = lineWidth
            box.stroke()
        }
        
        self.setWidth(width)
    }
    
    public func setValue(upload: Double, download: Double) {
        DispatchQueue.main.async(execute: {
            self.points.remove(at: 0)
            self.points.append((upload, download))
            
            if self.window?.isVisible ?? false {
                self.display()
            }
        })
    }
    
    // MARK: - Settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        let box = switchView(
            action: #selector(self.toggleBox),
            state: self.boxState
        )
        self.boxSettingsView = box
        let frame = switchView(
            action: #selector(self.toggleFrame),
            state: self.frameState
        )
        self.frameSettingsView = frame
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Label"), component: switchView(
                action: #selector(self.toggleLabel),
                state: self.labelState
            )),
            PreferencesRow(localizedString("Box"), component: box),
            PreferencesRow(localizedString("Frame"), component: frame),
            PreferencesRow(localizedString("Reverse order"), component: switchView(
                action: #selector(self.toggleReverseOrder),
                state: self.reverseOrderState
            )),
            PreferencesRow(localizedString("Color of download"), component: selectView(
                action: #selector(self.toggleDownloadColor),
                items: self.colors,
                selected: self.downloadColor.key
            )),
            PreferencesRow(localizedString("Color of upload"), component: selectView(
                action: #selector(self.toggleUploadColor),
                items: self.colors,
                selected: self.uploadColor.key
            )),
            PreferencesRow(localizedString("Number of reads in the chart"), component: selectView(
                action: #selector(self.toggleHistoryCount),
                items: self.historyNumbers,
                selected: "\(self.historyCount)"
            )),
            PreferencesRow(localizedString("Scaling"), component: selectView(
                action: #selector(self.toggleScale),
                items: Scale.allCases.filter({ $0 != .fixed }),
                selected: self.scaleState.key
            ))
        ]))
        
        return view
    }
    
    @objc private func toggleLabel(_ sender: NSControl) {
        self.labelState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_label", value: self.labelState)
        self.display()
    }
    
    @objc private func toggleBox(_ sender: NSControl) {
        self.boxState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_box", value: self.boxState)
        
        if self.frameState {
            self.frameSettingsView?.state = .off
            self.frameState = false
            Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_frame", value: self.frameState)
        }
        
        self.display()
    }
    
    @objc private func toggleFrame(_ sender: NSControl) {
        self.frameState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_frame", value: self.frameState)
        
        if self.boxState {
            self.boxSettingsView?.state = .off
            self.boxState = false
            Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_box", value: self.boxState)
        }
        
        self.display()
    }
    
    @objc private func toggleHistoryCount(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String, let num = Int(key) else { return }
        self.historyCount = num
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_historyCount", value: self.historyCount)
        
        if num < self.points.count {
            self.points = Array(self.points.suffix(num))
        } else if num > self.points.count {
            self.points = Array(repeating: (0, 0), count: num - self.points.count) + self.points
        }
        
        self.display()
    }
    
    @objc private func toggleDownloadColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newColor = SColor.allCases.first(where: { $0.key == key }) {
            self.downloadColor = newColor
            Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_downloadColor", value: newColor.key)
        }
        self.display()
    }
    
    @objc private func toggleUploadColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newColor = SColor.allCases.first(where: { $0.key == key }) {
            self.uploadColor = newColor
            Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_uploadColor", value: newColor.key)
        }
        self.display()
    }
    
    @objc private func toggleScale(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String,
              let value = Scale.allCases.first(where: { $0.key == key }) else { return }
        self.scaleState = value
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_scale", value: key)
        self.display()
    }
    
    @objc private func toggleReverseOrder(_ sender: NSControl) {
        self.reverseOrderState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_reverseOrder", value: self.reverseOrderState)
        self.display()
    }
}


================================================
FILE: Kit/Widgets/PieChart.swift
================================================
//
//  PieChart.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 30/11/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class PieChart: WidgetWrapper {
    private var labelState: Bool = false
    private var monochromeState: Bool = false
    
    private var chart: PieChartView = PieChartView(
        frame: NSRect(
            x: Constants.Widget.margin.x,
            y: Constants.Widget.margin.y,
            width: Constants.Widget.height,
            height: Constants.Widget.height
        ),
        segments: [], filled: true, drawValue: false
    )
    private var labelView: NSView? = nil
    
    private let size: CGFloat = Constants.Widget.height - (Constants.Widget.margin.y*2) + (Constants.Widget.margin.x*2)
    
    public init(title: String, config: NSDictionary?, preview: Bool = false) {
        var widgetTitle: String = title
        if config != nil {
            if let titleFromConfig = config!["Title"] as? String {
                widgetTitle = titleFromConfig
            }
        }
        
        super.init(.pieChart, title: widgetTitle, frame: CGRect(
            x: Constants.Widget.margin.x,
            y: Constants.Widget.margin.y,
            width: self.size,
            height: Constants.Widget.height - (Constants.Widget.margin.y*2)
        ))
        
        self.canDrawConcurrently = true
        
        if preview {
            if self.title == "CPU" {
                self.chart.setSegments([
                    circle_segment(value: 0.16, color: NSColor.systemRed),
                    circle_segment(value: 0.28, color: NSColor.systemBlue)
                ])
            } else if self.title == "RAM" {
                self.chart.setSegments([
                    circle_segment(value: 0.36, color: NSColor.systemBlue),
                    circle_segment(value: 0.12, color: NSColor.systemOrange),
                    circle_segment(value: 0.08, color: NSColor.systemPink)
                ])
            } else if self.title == "Disk" {
                self.chart.setSegments([
                    circle_segment(value: 0.86, color: NSColor.systemBlue)
                ])
            }
        } else {
            self.labelState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_label", defaultValue: self.labelState)
            self.monochromeState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_monochrome", defaultValue: self.monochromeState)
        }
        
        self.draw()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func draw() {
        let x: CGFloat = self.labelState ? 8 + Constants.Widget.spacing : 0
        
        self.labelView = WidgetLabelView(self.title, height: self.frame.height)
        self.labelView!.isHidden = !self.labelState
        
        self.addSubview(self.labelView!)
        self.addSubview(self.chart)
        
        self.chart.setFrame(NSRect(x: x, y: 0, width: self.frame.size.height, height: self.frame.size.height))
        
        self.setFrameSize(NSSize(width: self.size + x, height: self.frame.size.height))
        self.setWidth(self.size + x)
    }
    
    public func setValue(_ list: [circle_segment]) {
        var segments = list
        
        if self.monochromeState {
            for i in 0..<segments.count {
                segments[i].color = segments[i].color.grayscaled()
            }
        }
        
        DispatchQueue.main.async(execute: {
            self.chart.setSegments(segments)
        })
    }
    
    // MARK: - Settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Label"), component: switchView(
                action: #selector(self.toggleLabel),
                state: self.labelState
            )),
            PreferencesRow(localizedString("Monochrome accent"), component: switchView(
                action: #selector(self.toggleMonochrome),
                state: self.monochromeState
            ))
        ]))
        
        return view
    }
    
    @objc private func toggleLabel(_ sender: NSControl) {
        self.labelState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_label", value: self.labelState)
        
        let x = self.labelState ? 6 + Constants.Widget.spacing : 0
        self.labelView!.isHidden = !self.labelState
        self.chart.setFrameOrigin(NSPoint(x: x, y: 0))
        self.setWidth(self.labelState ? self.size+x : self.size)
    }
    
    @objc private func toggleMonochrome(_ sender: NSControl) {
        self.monochromeState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_monochrome", value: self.monochromeState)
    }
}


================================================
FILE: Kit/Widgets/Speed.swift
================================================
//
//  Speed.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 24/05/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class SpeedWidget: WidgetWrapper {
    private var icon: String = "dots"
    private var valueState: Bool = true
    private var unitsState: Bool = true
    private var monochromeState: Bool = false
    private var valueColorState: String = "none"
    private var iconColorState: String = "default"
    private var valueAlignmentState: String = "right"
    private var modeState: String = "twoRows"
    private var iconAlignmentState: String = "left"
    private var displayValueState: String = "oi"
    
    private var inputColorState: SColor = .secondBlue
    private var outputColorState: SColor = .secondRed
    
    private var symbols: (input: String, output: String) = ("I", "O")
    private var words: (input: String, output: String) = ("Input", "Output")
    
    private var inputValue: Int64 = 0
    private var outputValue: Int64 = 0
    
    private var width: CGFloat = 58
    
    private var valueColorView: NSPopUpButton? = nil
    private var valueAlignmentView: NSPopUpButton? = nil
    private var iconAlignmentView: NSPopUpButton? = nil
    private var iconColorView: NSPopUpButton? = nil
    private var displayModeView: NSPopUpButton? = nil
    
    private var inputColor: (String) -> NSColor {{ state in
        if state == "none" { return .textColor }
        var color = self.monochromeState ? MonochromeColor.blue : (self.inputColorState.additional as? NSColor ?? NSColor.systemBlue)
        if self.inputValue < 1024 {
            if state == "transparent" {
                color = .clear
            } else if state == "default" {
                color = .textColor
            }
        }
        return color
    }}
    private var outputColor: (String) -> NSColor {{ state in
        if state == "none" { return .textColor }
        var color = self.monochromeState ? MonochromeColor.red : (self.outputColorState.additional as? NSColor ?? NSColor.red)
        if self.outputValue < 1024 {
            if state == "transparent" {
                color = .clear
            } else if state == "default" {
                color = .textColor
            }
        }
        return color
    }}
    
    private var valueAlignment: NSTextAlignment {
        get {
            if let alignmentPair = Alignments.first(where: { $0.key == self.valueAlignmentState }) {
                return alignmentPair.additional as? NSTextAlignment ?? .left
            }
            return .left
        }
    }
    
    private var base: DataSizeBase {
        DataSizeBase(rawValue: Store.shared.string(key: "\(self.title)_base", defaultValue: "byte")) ?? .byte
    }
    
    public init(title: String, config: NSDictionary?, preview: Bool = false) {
        let widgetTitle: String = title
        if config != nil {
            if let symbols = config!["Symbols"] as? NSDictionary {
                if let i = symbols["Input"] as? String { self.symbols.input = i }
                if let o = symbols["Output"] as? String { self.symbols.output = o }
            }
            if let icon = config!["Icon"] as? String { self.icon = icon }
            if let words = config!["Words"] as? NSDictionary {
                if let i = words["Input"] as? String { self.words.input = i }
                if let o = words["Output"] as? String { self.words.output = o }
            }
        }
        
        super.init(.speed, title: widgetTitle, frame: CGRect(
            x: 0,
            y: Constants.Widget.margin.y,
            width: width,
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        self.canDrawConcurrently = true
        
        if !preview {
            self.valueState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_value", defaultValue: self.valueState)
            self.icon = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_icon", defaultValue: self.icon)
            self.unitsState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_units", defaultValue: self.unitsState)
            self.monochromeState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_monochrome", defaultValue: self.monochromeState)
            self.valueColorState = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_valueColor", defaultValue: self.valueColorState)
            if self.valueColorState == "0" {
                self.valueColorState = "none"
            }
            self.inputColorState = SColor.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_downloadColor", defaultValue: self.inputColorState.key))
            self.outputColorState = SColor.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_uploadColor", defaultValue: self.outputColorState.key))
            self.valueAlignmentState = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_valueAlignment", defaultValue: self.valueAlignmentState)
            self.modeState = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_mode", defaultValue: self.modeState)
            self.iconAlignmentState = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_iconAlignment", defaultValue: self.iconAlignmentState)
            self.iconColorState = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_iconColor", defaultValue: self.iconColorState)
            self.displayValueState = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_displayValue", defaultValue: self.displayValueState)
        }
        
        if preview {
            self.inputValue = 8947141
            self.outputValue = 478678
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        var width: CGFloat = 0
        switch self.modeState {
        case "oneRow":
            width = self.drawOneRow()
        case "twoRows":
            width = self.drawTwoRows()
        default:
            width = 0
        }
        
        self.setWidth(width)
    }
    
    // MARK: - one row
    
    private func drawOneRow() -> CGFloat {
        var width: CGFloat = Constants.Widget.margin.x
        
        if self.displayValueState.first == "i" {
            width = self.drawRowItem(
                initWidth: width,
                symbol: self.symbols.input,
                iconColor: self.inputColor(self.iconColorState),
                value: self.inputValue,
                valueColor: self.inputColor(self.valueColorState)
            )
        } else {
            width = self.drawRowItem(
                initWidth: width,
                symbol: self.symbols.output,
                iconColor: self.outputColor(self.iconColorState),
                value: self.outputValue,
                valueColor: self.outputColor(self.valueColorState)
            )
        }
        
        if self.displayValueState.count > 1 {
            width += Constants.Widget.spacing*3
            if self.displayValueState.last == "i" {
                width = self.drawRowItem(
                    initWidth: width,
                    symbol: self.symbols.input,
                    iconColor: self.inputColor(self.iconColorState),
                    value: self.inputValue,
                    valueColor: self.inputColor(self.valueColorState)
                )
            } else {
                width = self.drawRowItem(
                    initWidth: width,
                    symbol: self.symbols.output,
                    iconColor: self.outputColor(self.iconColorState),
                    value: self.outputValue,
                    valueColor: self.outputColor(self.valueColorState)
                )
            }
        }
        
        return width + Constants.Widget.margin.x
    }
    
    private func drawRowItem(initWidth: CGFloat, symbol: String, iconColor: NSColor, value: Int64, valueColor: NSColor) -> CGFloat {
        var width = initWidth
        
        if self.iconAlignmentState == "left" {
            switch self.icon {
            case "dots":
                width += self.drawDot(CGPoint(x: width, y: 0), color: iconColor)
            case "arrows":
                width += self.drawArrow(CGPoint(x: width, y: 0), symbol: symbol, color: iconColor)
            case "chars":
                width += self.drawChar(CGPoint(x: width, y: 0), symbol: symbol, color: iconColor)
            default: break
            }
            width += self.valueState && self.icon != "none" ? 2 : 0
        }
        
        if self.valueState {
            width += self.drawValue(value, offset: CGPoint(x: width, y: 0), color: valueColor)
        }
        
        if self.iconAlignmentState == "right" {
            if self.valueState {
                width += 2
            }
            switch self.icon {
            case "dots":
                width += self.drawDot(CGPoint(x: width, y: 0), color: iconColor)
            case "arrows":
                width += self.drawArrow(CGPoint(x: width, y: 0), symbol: symbol, color: iconColor)
            case "chars":
                width += self.drawChar(CGPoint(x: width, y: 0), symbol: symbol, color: iconColor)
            default: break
            }
        }
        
        return width
    }
    
    private func drawValue(_ value: Int64, offset: CGPoint, color: NSColor) -> CGFloat {
        let rowWidth: CGFloat = self.unitsState ? 58 : 32
        let height: CGFloat = self.frame.height
        let style = NSMutableParagraphStyle()
        style.alignment = self.valueAlignment
        let size: CGFloat = 10
        
        let inputStringAttributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 11, weight: .regular),
            NSAttributedString.Key.foregroundColor: color,
            NSAttributedString.Key.paragraphStyle: style
        ]
        
        let rect = CGRect(x: offset.x, y: (height-size)/2 + offset.y + 1, width: rowWidth - (Constants.Widget.margin.x*2), height: size)
        let value = NSAttributedString.init(
            string: Units(bytes: value).getReadableSpeed(base: base, omitUnits: !self.unitsState),
            attributes: inputStringAttributes
        )
        value.draw(with: rect)
        
        return rowWidth
    }
    
    private func drawDot(_ offset: CGPoint, color: NSColor) -> CGFloat {
        var size: CGFloat = 8
        var height: CGFloat = self.frame.height
        
        if self.modeState == "twoRows" {
            size = 6
            height /= 2
        }
        
        var circle = NSBezierPath()
        circle = NSBezierPath(ovalIn: CGRect(x: offset.x, y: (height-size)/2 + offset.y, width: size, height: size))
        color.set()
        circle.fill()
        
        return size
    }
    
    private func drawArrow(_ offset: CGPoint, symbol: String, color: NSColor) -> CGFloat {
        let height = self.frame.height
        let size = height * 0.8
        let scaleFactor = NSScreen.main?.backingScaleFactor ?? 1
        let lineWidth: CGFloat = 1
        let arrowSize: CGFloat = 3 + (scaleFactor/2)
        let x = arrowSize + (lineWidth / 2)
        let y = (height - size)/2
        
        var start: CGPoint = CGPoint(x: offset.x + x, y: y)
        var end: CGPoint = CGPoint(x: offset.x + x, y: size + y)
        if symbol == "D" || symbol == "R" {
            start = CGPoint(x: offset.x + x, y: size + y)
            end = CGPoint(x: offset.x + x, y: y)
        } else if symbol == "U" || symbol == "W" {
            start = CGPoint(x: offset.x + x, y: y)
            end = CGPoint(x: offset.x + x, y: size + y)
        }
        
        let arrow = NSBezierPath()
        arrow.addArrow(
            start: start,
            end: end,
            pointerLineLength: arrowSize,
            arrowAngle: CGFloat(Double.pi / 5)
        )
        
        color.set()
        arrow.lineWidth = lineWidth
        arrow.stroke()
        arrow.close()
        
        return arrowSize
    }
    
    private func drawChar(_ offset: CGPoint, symbol: String, color: NSColor) -> CGFloat {
        let rowHeight: CGFloat = self.frame.height
        let height: CGFloat = 10
        let inputAttributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 12, weight: .regular),
            NSAttributedString.Key.foregroundColor: color,
            NSAttributedString.Key.paragraphStyle: NSMutableParagraphStyle()
        ]
        let rect = CGRect(x: offset.x, y: offset.y + ((rowHeight-height)/2) + 1, width: 10, height: height)
        let str = NSAttributedString.init(string: symbol, attributes: inputAttributes)
        str.draw(with: rect)
        
        return 10
    }
    
    // MARK: - two rows
    
    private func drawTwoRows() -> CGFloat {
        var width: CGFloat = 7
        var x: CGFloat = 7
        
        if self.iconAlignmentState == "right" {
            x = 0
        }
        if self.icon == "none" {
            x = 0
            width = 0
        }
        
        if self.valueState {
            let rowWidth: CGFloat = self.unitsState ? 48 : 30
            let rowHeight: CGFloat = self.frame.height / 2
            let style = NSMutableParagraphStyle()
            style.alignment = self.valueAlignment
            
            let inputStringAttributes = [
                NSAttributedString.Key.font: NSFont.systemFont(ofSize: 9, weight: .light),
                NSAttributedString.Key.foregroundColor: self.inputColor(self.valueColorState),
                NSAttributedString.Key.paragraphStyle: style
            ]
            let outputStringAttributes = [
                NSAttributedString.Key.font: NSFont.systemFont(ofSize: 9, weight: .light),
                NSAttributedString.Key.foregroundColor: self.outputColor(self.valueColorState),
                NSAttributedString.Key.paragraphStyle: style
            ]
            
            let inputY: CGFloat = self.displayValueState == "io" ? rowHeight + 1 : 1
            let outputY: CGFloat = self.displayValueState == "io" ? 1 : rowHeight + 1
            
            var rect = CGRect(x: Constants.Widget.margin.x + x, y: inputY, width: rowWidth - (Constants.Widget.margin.x*2), height: rowHeight)
            let input = NSAttributedString.init(
                string: Units(bytes: self.inputValue).getReadableSpeed(base: base, omitUnits: !self.unitsState),
                attributes: inputStringAttributes
            )
            input.draw(with: rect)
            
            rect = CGRect(x: Constants.Widget.margin.x + x, y: outputY, width: rowWidth - (Constants.Widget.margin.x*2), height: rowHeight)
            let output = NSAttributedString.init(
                string: Units(bytes: self.outputValue).getReadableSpeed(base: base, omitUnits: !self.unitsState),
                attributes: outputStringAttributes
            )
            output.draw(with: rect)
            
            width += rowWidth
        }
        
        switch self.icon {
        case "dots":
            self.drawDots(width)
        case "arrows":
            self.drawArrows(width)
        case "chars":
            self.drawChars(width)
        default: break
        }
        
        return width
    }
    
    private func drawDots(_ width: CGFloat) {
        let rowHeight: CGFloat = self.frame.height / 2
        let size: CGFloat = 6
        let y: CGFloat = (rowHeight-size)/2
        let x: CGFloat = self.iconAlignmentState == "left" ? Constants.Widget.margin.x : Constants.Widget.margin.x+(width-6)
        let inputY: CGFloat = self.displayValueState == "io" ? 10.5 : y-0.2
        let outputdY: CGFloat = self.displayValueState == "io" ? y-0.2 : 10.5
        
        var inputCircle = NSBezierPath()
        inputCircle = NSBezierPath(ovalIn: CGRect(x: x, y: inputY, width: size, height: size))
        self.inputColor(self.iconColorState).set()
        inputCircle.fill()
        
        var outputCircle = NSBezierPath()
        outputCircle = NSBezierPath(ovalIn: CGRect(x: x, y: outputdY, width: size, height: size))
        self.outputColor(self.iconColorState).set()
        outputCircle.fill()
    }
    
    private func drawArrows(_ width: CGFloat) {
        let arrowAngle = CGFloat(Double.pi / 5)
        let half = self.frame.size.height / 2
        let scaleFactor = NSScreen.main?.backingScaleFactor ?? 1
        let lineWidth: CGFloat = 1
        let arrowSize: CGFloat = 3 + (scaleFactor/2)
        var x = Constants.Widget.margin.x + arrowSize + (lineWidth / 2)
        if self.iconAlignmentState == "right" {
            x += (width-7)
        }
        
        let inputYStart: CGFloat = self.displayValueState == "io" ? self.frame.size.height : half - Constants.Widget.spacing/2
        let inputYEnd: CGFloat = self.displayValueState == "io" ? (half + Constants.Widget.spacing/2)+1 : 1
        
        let outputYStart: CGFloat = self.displayValueState == "io" ? 0 : half + Constants.Widget.spacing/2
        let uploadYEnd: CGFloat = self.displayValueState == "io" ? (half - Constants.Widget.spacing/2)-1 : self.frame.size.height-1
        
        let inputArrow = NSBezierPath()
        inputArrow.addArrow(
            start: CGPoint(x: x, y: inputYStart),
            end: CGPoint(x: x, y: inputYEnd),
            pointerLineLength: arrowSize,
            arrowAngle: arrowAngle
        )
        
        self.inputColor(self.iconColorState).set()
        inputArrow.lineWidth = lineWidth
        inputArrow.stroke()
        inputArrow.close()
        
        let outputArrow = NSBezierPath()
        outputArrow.addArrow(
            start: CGPoint(x: x, y: outputYStart),
            end: CGPoint(x: x, y: uploadYEnd),
            pointerLineLength: arrowSize,
            arrowAngle: arrowAngle
        )
        
        self.outputColor(self.iconColorState).set()
        outputArrow.lineWidth = lineWidth
        outputArrow.stroke()
        outputArrow.close()
    }
    
    private func drawChars(_ width: CGFloat) {
        let rowHeight: CGFloat = self.frame.height / 2
        let inputY: CGFloat = self.displayValueState == "io" ? rowHeight+1 : 1
        let outputY: CGFloat = self.displayValueState == "io" ? 1 : rowHeight+1
        let x: CGFloat = self.iconAlignmentState == "left" ? Constants.Widget.margin.x : Constants.Widget.margin.x+(width-6)
        
        let inputAttributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 9, weight: .regular),
            NSAttributedString.Key.foregroundColor: self.inputColor(self.iconColorState),
            NSAttributedString.Key.paragraphStyle: NSMutableParagraphStyle()
        ]
        var rect = CGRect(x: x, y: inputY, width: 8, height: rowHeight)
        var str = NSAttributedString.init(string: self.symbols.input, attributes: inputAttributes)
        str.draw(with: rect)
        
        let outputAttributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: 9, weight: .regular),
            NSAttributedString.Key.foregroundColor: self.outputColor(self.iconColorState),
            NSAttributedString.Key.paragraphStyle: NSMutableParagraphStyle()
        ]
        rect = CGRect(x: x, y: outputY, width: 8, height: rowHeight)
        str = NSAttributedString.init(string: self.symbols.output, attributes: outputAttributes)
        str.draw(with: rect)
    }
    
    // MARK: - settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        let valueAlignment = selectView(
            action: #selector(self.toggleValueAlignment),
            items: Alignments,
            selected: self.valueAlignmentState
        )
        valueAlignment.isEnabled = self.valueState
        self.valueAlignmentView = valueAlignment
        
        let iconAlignment = selectView(
            action: #selector(self.toggleIconAlignment),
            items: Alignments.filter({ $0.key != "center" }),
            selected: self.iconAlignmentState
        )
        iconAlignment.isEnabled = self.icon != "none"
        self.iconAlignmentView = iconAlignment
        
        let iconColor = selectView(
            action: #selector(self.toggleIconColor),
            items: SpeedPictogramColor.filter({ $0.key != "none" }),
            selected: self.iconColorState
        )
        iconColor.isEnabled = self.icon != "none"
        self.iconColorView = iconColor
        
        let valueColor = selectView(
            action: #selector(self.toggleValueColor),
            items: SpeedPictogramColor,
            selected: self.valueColorState
        )
        valueColor.isEnabled = self.valueState
        self.valueColorView = valueColor
        
        let displayMode = selectView(
            action: #selector(self.changeDisplayMode),
            items: SensorsWidgetMode.filter({ $0.key == "oneRow" || $0.key == "twoRows"}),
            selected: self.modeState
        )
        displayMode.isEnabled = self.displayValueState.count > 1
        self.displayModeView = displayMode
        
        let sensorWidgetValue = SensorsWidgetValue.map { v in
            var value = v.value.replacingOccurrences(of: "input", with: localizedString(self.words.input), options: .literal, range: nil)
            value = value.replacingOccurrences(of: "output", with: localizedString(self.words.output), options: .literal, range: nil)
            return KeyValue_t(key: v.key, value: value)
        }
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Value"), component: selectView(
                action: #selector(self.changeDisplayValue),
                items: sensorWidgetValue,
                selected: self.displayValueState
            )),
            PreferencesRow(localizedString("Display mode"), component: displayMode)
        ]))
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Pictogram"), component: selectView(
                action: #selector(self.toggleIcon),
                items: SpeedPictogram,
                selected: self.icon
            )),
            PreferencesRow(localizedString("Colorize"), component: iconColor),
            PreferencesRow(localizedString("Alignment"), component: iconAlignment)
        ]))
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Value"), component: switchView(
                action: #selector(self.toggleValue),
                state: self.valueState
            )),
            PreferencesRow(localizedString("Colorize value"), component: valueColor),
            PreferencesRow(localizedString("Alignment"), component: valueAlignment),
            PreferencesRow(localizedString("Units"), component: switchView(
                action: #selector(self.toggleUnits),
                state: self.unitsState
            ))
        ]))
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Monochrome accent"), component: switchView(
                action: #selector(self.toggleMonochrome),
                state: self.monochromeState
            )),
            PreferencesRow(localizedString("Color of download"), component: selectView(
                action: #selector(self.toggleInputColor),
                items: SColor.allColors,
                selected: self.inputColorState.key
            )),
            PreferencesRow(localizedString("Color of upload"), component: selectView(
                action: #selector(self.toggleOutputColor),
                items: SColor.allColors,
                selected: self.outputColorState.key
            ))
        ]))
        
        return view
    }
    
    @objc private func changeDisplayValue(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        self.displayValueState = key
        
        if key.count == 1 {
            if self.modeState != "oneRow" {
                self.modeState = "oneRow"
                Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_mode", value: self.modeState)
            }
            self.displayModeView?.selectItem(at: 0)
        }
        self.displayModeView?.isEnabled = key.count > 1
        
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_displayValue", value: key)
        self.display()
    }
    
    @objc private func changeDisplayMode(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        self.modeState = key
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_mode", value: key)
        self.display()
    }
    
    @objc private func toggleValue(_ sender: NSControl) {
        self.valueState = controlState(sender)
        
        self.valueColorView?.isEnabled = self.valueState
        self.valueAlignmentView?.isEnabled = self.valueState
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_value", value: self.valueState)
        self.display()
    }
    
    @objc private func toggleUnits(_ sender: NSControl) {
        self.unitsState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_units", value: self.unitsState)
        self.display()
    }
    
    @objc private func toggleIcon(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        self.icon = key
        self.iconColorView?.isEnabled = self.icon != "none"
        self.iconAlignmentView?.isEnabled = self.icon != "none"
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_icon", value: key)
        self.display()
    }
    
    @objc private func toggleMonochrome(_ sender: NSControl) {
        self.monochromeState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_monochrome", value: self.monochromeState)
        self.display()
    }
    
    @objc private func toggleValueColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newColor = SpeedPictogramColor.first(where: { $0.key == key }) {
            self.valueColorState = newColor.key
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_valueColor", value: key)
        self.display()
    }
    
    @objc private func toggleOutputColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String,
              let newValue = SColor.allColors.first(where: { $0.key == key }) else {
            return
        }
        self.outputColorState = newValue
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_uploadColor", value: key)
    }
    @objc private func toggleInputColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String,
              let newValue = SColor.allColors.first(where: { $0.key == key }) else {
            return
        }
        self.inputColorState = newValue
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_downloadColor", value: key)
    }
    
    public func setValue(input: Int64, output: Int64) {
        var updated: Bool = false
        
        if self.inputValue != input {
            self.inputValue = abs(input)
            updated = true
        }
        if self.outputValue != output {
            self.outputValue = abs(output)
            updated = true
        }
        
        if updated {
            DispatchQueue.main.async(execute: {
                self.display()
            })
        }
    }
    
    @objc private func toggleValueAlignment(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newAlignment = Alignments.first(where: { $0.key == key }) {
            self.valueAlignmentState = newAlignment.key
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_valueAlignment", value: key)
        self.display()
    }
    
    @objc private func toggleIconAlignment(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newAlignment = Alignments.first(where: { $0.key == key }) {
            self.iconAlignmentState = newAlignment.key
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_iconAlignment", value: key)
        self.display()
    }
    
    @objc private func toggleIconColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newColor = SpeedPictogramColor.first(where: { $0.key == key }) {
            self.iconColorState = newColor.key
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_iconColor", value: key)
        self.display()
    }
}


================================================
FILE: Kit/Widgets/Stack.swift
================================================
//
//  Sensors.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 17/06/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public struct Stack_t: KeyValue_p {
    public var key: String
    public var value: String
    
    var index: Int {
        get { Store.shared.int(key: "stack_\(self.key)_index", defaultValue: -1) }
        set { Store.shared.set(key: "stack_\(self.key)_index", value: newValue) }
    }
    
    public init(key: String, value: String) {
        self.key = key
        self.value = value
    }
}

public class StackWidget: WidgetWrapper {
    private var modeState: StackMode = .auto
    private var fixedSizeState: Bool = false
    private var monospacedFontState: Bool = false
    private var alignmentState: String = "left"
    
    private var values: [Stack_t] = []
    
    private var oneRowWidth: CGFloat = 45
    private var twoRowWidth: CGFloat = 32
    
    private let orderTableView: OrderTableView
    
    private var alignment: NSTextAlignment {
        if let alignmentPair = Alignments.first(where: { $0.key == self.alignmentState }) {
            return alignmentPair.additional as? NSTextAlignment ?? .left
        }
        return .left
    }
    
    public init(title: String, config: NSDictionary?, preview: Bool = false) {
        if let config, preview {
            if let previewConfig = config["Preview"] as? NSDictionary {
                if let value = previewConfig["Values"] as? String {
                    for (i, value) in value.split(separator: ",").enumerated() {
                        self.values.append(Stack_t(key: "\(i)", value: String(value)))
                    }
                }
            }
        }
        
        self.orderTableView = OrderTableView(&self.values)
        
        super.init(.stack, title: title, frame: CGRect(
            x: 0,
            y: Constants.Widget.margin.y,
            width: Constants.Widget.width,
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        if !preview {
            self.modeState = StackMode(rawValue: Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_mode", defaultValue: self.modeState.rawValue)) ?? .auto
            self.fixedSizeState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_size", defaultValue: self.fixedSizeState)
            self.monospacedFontState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_monospacedFont", defaultValue: self.monospacedFontState)
            self.alignmentState = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_alignment", defaultValue: self.alignmentState)
        }
        
        self.orderTableView.reorderCallback = { [weak self] in
            self?.display()
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        var values: [Stack_t] = []
        var mode: StackMode = .auto
        self.queue.sync {
            values = self.values
            mode = self.modeState
        }
        
        guard !values.isEmpty else {
            self.setWidth(0)
            return
        }
        
        let num: Int = Int(round(Double(values.count) / 2))
        var totalWidth: CGFloat = Constants.Widget.spacing  // opening space
        var x: CGFloat = Constants.Widget.spacing
        
        var i = 0
        while i < values.count {
            switch mode {
            case .auto, .twoRows:
                let firstElement: Stack_t = values[i]
                let secondElement: Stack_t? = values.indices.contains(i+1) ? values[i+1] : nil
                
                var width: CGFloat = 0
                if mode == .auto && secondElement == nil {
                    width += self.drawOneRow(x, firstElement)
                } else {
                    width += self.drawTwoRows(x, firstElement, secondElement)
                }
                
                x += width
                totalWidth += width
                
                if num != 1 && (i/2) != num {
                    x += Constants.Widget.spacing
                    totalWidth += Constants.Widget.spacing
                }
                
                i += 1
            case .oneRow:
                let width = self.drawOneRow(x, values[i])
                
                x += width
                totalWidth += width
                
                // add margins between columns
                if values.count != 1 && i != values.count {
                    x += Constants.Widget.spacing
                    totalWidth += Constants.Widget.spacing
                }
            }
            
            i += 1
        }
        totalWidth += Constants.Widget.spacing // closing space
        
        guard abs(self.frame.width - totalWidth) > 2 else { return }
        self.setWidth(totalWidth)
    }
    
    private func drawOneRow(_ x: CGFloat, _ element: Stack_t) -> CGFloat {
        var monospacedFontState: Bool = false
        var fixedSizeState: Bool = false
        var alignment: NSTextAlignment = .left
        self.queue.sync {
            monospacedFontState = self.monospacedFontState
            fixedSizeState = self.fixedSizeState
            alignment = self.alignment
        }
        
        var font: NSFont = NSFont.systemFont(ofSize: 13, weight: .regular)
        if monospacedFontState {
            font = NSFont.monospacedDigitSystemFont(ofSize: 13, weight: .regular)
        }
        
        let style = NSMutableParagraphStyle()
        style.alignment = alignment
        
        var width: CGFloat = self.oneRowWidth
        if !fixedSizeState {
            width = element.value.widthOfString(usingFont: font).rounded(.up) + 2
        }
        
        let rect = CGRect(x: x, y: (Constants.Widget.height-13)/2, width: width, height: 13)
        let str = NSAttributedString.init(string: element.value, attributes: [
            NSAttributedString.Key.font: font,
            NSAttributedString.Key.foregroundColor: NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: style
        ])
        str.draw(with: rect)
        
        return width
    }
    
    private func drawTwoRows(_ x: CGFloat, _ topElement: Stack_t, _ bottomElement: Stack_t?) -> CGFloat {
        let rowHeight: CGFloat = self.frame.height / 2
        var monospacedFontState: Bool = false
        var fixedSizeState: Bool = false
        var alignment: NSTextAlignment = .left
        self.queue.sync {
            monospacedFontState = self.monospacedFontState
            fixedSizeState = self.fixedSizeState
            alignment = self.alignment
        }
        
        var font: NSFont
        if monospacedFontState {
            font = NSFont.monospacedDigitSystemFont(ofSize: 10, weight: .light)
        } else {
            font = NSFont.systemFont(ofSize: 10, weight: .light)
        }
        let style = NSMutableParagraphStyle()
        style.alignment = alignment
        
        let attributes = [
            NSAttributedString.Key.font: font,
            NSAttributedString.Key.foregroundColor: NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: style
        ]
        
        var width: CGFloat = self.twoRowWidth
        if !fixedSizeState {
            let firstRowWidth = topElement.value.widthOfString(usingFont: font)
            let secondRowWidth = bottomElement?.value.widthOfString(usingFont: font) ?? 0
            width = max(20, max(firstRowWidth, secondRowWidth)).rounded(.up) + 2
        }
        
        var rect = CGRect(x: x, y: rowHeight+1, width: width, height: rowHeight)
        var str = NSAttributedString.init(string: topElement.value, attributes: attributes)
        str.draw(with: rect)
        
        if bottomElement != nil {
            rect = CGRect(x: x, y: 1, width: width, height: rowHeight)
            str = NSAttributedString.init(string: bottomElement!.value, attributes: attributes)
            str.draw(with: rect)
        }
        
        return width
    }
    
    public func setValues(_ values: [Stack_t]) {
        DispatchQueue.main.async(execute: {
            var tableNeedsToBeUpdated: Bool = false
            
            values.forEach { (p: Stack_t) in
                if let idx = self.values.firstIndex(where: { $0.key == p.key }) {
                    self.values[idx].value = p.value
                    return
                }
                tableNeedsToBeUpdated = true
                self.values.append(p)
            }
            
            let diff = self.values.filter({ v in values.contains(where: { $0.key == v.key }) })
            if diff.count != self.values.count {
                tableNeedsToBeUpdated = true
            }
            self.values = diff.sorted(by: { $0.index < $1.index })
            
            if tableNeedsToBeUpdated {
                self.orderTableView.update()
            }
            self.display()
        })
    }
    
    // MARK: - Settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        var rows = [
            PreferencesRow(localizedString("Display mode"), component: selectView(
                action: #selector(self.changeDisplayMode),
                items: SensorsWidgetMode,
                selected: self.modeState.rawValue
            )),
            PreferencesRow(localizedString("Monospaced font"), component: switchView(
                action: #selector(self.toggleMonospacedFont),
                state: self.monospacedFontState
            )),
            PreferencesRow(localizedString("Alignment"), component: selectView(
                action: #selector(self.toggleAlignment),
                items: Alignments,
                selected: self.alignmentState
            ))
        ]
        if self.title != "Clock" {
            rows.append(PreferencesRow(localizedString("Static width"), component: switchView(
                action: #selector(self.toggleSize),
                state: self.fixedSizeState
            )))
        }
        view.addArrangedSubview(PreferencesSection(rows))
        
        view.addArrangedSubview(self.orderTableView)
        
        return view
    }
    
    @objc private func changeDisplayMode(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        self.modeState = StackMode(rawValue: key) ?? .auto
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_mode", value: key)
        self.display()
    }
    
    @objc private func toggleSize(_ sender: NSControl) {
        self.fixedSizeState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_size", value: self.fixedSizeState)
        self.display()
    }
    
    @objc private func toggleMonospacedFont(_ sender: NSControl) {
        self.monospacedFontState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_monospacedFont", value: self.monospacedFontState)
        self.display()
    }
    
    @objc private func toggleAlignment(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newAlignment = Alignments.first(where: { $0.key == key }) {
            self.alignmentState = newAlignment.key
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_alignment", value: key)
        self.display()
    }
}

private class OrderTableView: NSView, NSTableViewDelegate, NSTableViewDataSource {
    private let scrollView = NSScrollView()
    private let tableView = NSTableView()
    private var dragDropType = NSPasteboard.PasteboardType(rawValue: "\(Bundle.main.bundleIdentifier!).sensors-row")
    
    fileprivate var reorderCallback: () -> Void = {}
    private let list: UnsafeMutablePointer<[Stack_t]>
    
    init(_ list: UnsafeMutablePointer<[Stack_t]>) {
        self.list = list
        
        super.init(frame: NSRect.zero)
        
        self.wantsLayer = true
        self.layer?.cornerRadius = 3
        
        self.scrollView.translatesAutoresizingMaskIntoConstraints = false
        self.scrollView.documentView = self.tableView
        self.scrollView.hasHorizontalScroller = false
        self.scrollView.hasVerticalScroller = true
        self.scrollView.autohidesScrollers = true
        self.scrollView.backgroundColor = NSColor.clear
        self.scrollView.drawsBackground = true
        
        self.tableView.frame = self.scrollView.bounds
        self.tableView.delegate = self
        self.tableView.dataSource = self
        self.tableView.headerView = nil
        self.tableView.backgroundColor = NSColor.clear
        self.tableView.columnAutoresizingStyle = .firstColumnOnlyAutoresizingStyle
        self.tableView.registerForDraggedTypes([dragDropType])
        self.tableView.gridColor = .gridColor
        self.tableView.gridStyleMask = [.solidVerticalGridLineMask, .solidHorizontalGridLineMask]
        self.tableView.style = .plain
        
        self.tableView.addTableColumn(NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "name")))
        
        self.addSubview(self.scrollView)
        
        NSLayoutConstraint.activate([
            self.scrollView.leftAnchor.constraint(equalTo: self.leftAnchor),
            self.scrollView.rightAnchor.constraint(equalTo: self.rightAnchor),
            self.scrollView.topAnchor.constraint(equalTo: self.topAnchor),
            self.scrollView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
            
            self.heightAnchor.constraint(equalToConstant: 120)
        ])
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    fileprivate func update() {
        self.tableView.reloadData()
    }
    
    func numberOfRows(in tableView: NSTableView) -> Int {
        return list.pointee.count
    }
    
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        if !self.list.pointee.indices.contains(row) { return nil }
        let item = self.list.pointee[row]
        
        let text: NSTextField = NSTextField()
        text.drawsBackground = false
        text.isBordered = false
        text.isEditable = false
        text.isSelectable = false
        text.translatesAutoresizingMaskIntoConstraints = false
        text.identifier = NSUserInterfaceItemIdentifier(item.key)
        
        switch tableColumn?.identifier.rawValue {
        case "name": text.stringValue = item.key
        default: break
        }
        
        text.sizeToFit()
        
        let cell = NSTableCellView()
        cell.addSubview(text)
        
        NSLayoutConstraint.activate([
            text.widthAnchor.constraint(equalTo: cell.widthAnchor),
            text.centerYAnchor.constraint(equalTo: cell.centerYAnchor)
        ])
        
        return cell
    }
    
    func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
        let item = NSPasteboardItem()
        item.setString(String(row), forType: self.dragDropType)
        return item
    }
    
    func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
        if dropOperation == .above {
            return .move
        }
        return []
    }
    
    func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
        var oldIndexes = [Int]()
        info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) { dragItem, _, _ in
            if let str = (dragItem.item as! NSPasteboardItem).string(forType: self.dragDropType), let index = Int(str) {
                oldIndexes.append(index)
            }
        }
        
        var oldIndexOffset = 0
        var newIndexOffset = 0
        
        tableView.beginUpdates()
        for oldIndex in oldIndexes {
            if oldIndex < row {
                let currentIdx = oldIndex + oldIndexOffset
                let newIdx = row - 1
                
                self.list.pointee[currentIdx].index = newIdx
                self.list.pointee[newIdx].index = currentIdx
                
                oldIndexOffset -= 1
            } else {
                let currentIdx = oldIndex
                let newIdx = row + newIndexOffset
                
                self.list.pointee[currentIdx].index = newIdx
                self.list.pointee[newIdx].index = currentIdx
                
                newIndexOffset += 1
            }
            self.list.pointee = self.list.pointee.sorted(by: { $0.index < $1.index })
            self.reorderCallback()
            tableView.reloadData()
        }
        tableView.endUpdates()
        
        return true
    }
}


================================================
FILE: Kit/Widgets/State.swift
================================================
//
//  State.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 18/09/2022.
//  Using Swift 5.0.
//  Running on macOS 12.6.
//
//  Copyright © 2022 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class StateWidget: WidgetWrapper {
    private var activeColorState: SColor = .secondGreen
    private var nonactiveColorState: SColor = .secondRed
    
    private var value: Bool = false
    
    private var colors: [SColor] = SColor.allColors
    
    public init(title: String, config: NSDictionary?, preview: Bool = false) {
        if config != nil {
            var configuration = config!
            
            if preview {
                if let previewConfig = config!["Preview"] as? NSDictionary {
                    configuration = previewConfig
                    if let value = configuration["Value"] as? Bool {
                        self.value = value
                    }
                }
            }
        }
        
        super.init(.state, title: title, frame: CGRect(
            x: 0,
            y: Constants.Widget.margin.y,
            width: 8 + (2*Constants.Widget.margin.x),
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        self.canDrawConcurrently = true
        
        if !preview {
            self.activeColorState = SColor.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_activeColor", defaultValue: self.activeColorState.key))
            self.nonactiveColorState = SColor.fromString(Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_nonactiveColor", defaultValue: self.nonactiveColorState.key))
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        let circle = NSBezierPath(ovalIn: CGRect(x: Constants.Widget.margin.x, y: (self.frame.height - 8)/2, width: 8, height: 8))
        let color = self.value ? self.activeColorState : self.nonactiveColorState
        (color.additional as? NSColor)?.set()
        circle.fill()
    }
    
    public func setValue(_ value: Bool) {
        guard self.value != value else { return }
        self.value = value
        DispatchQueue.main.async(execute: {
            self.display()
        })
    }
    
    // MARK: - Settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Active state color"), component: selectView(
                action: #selector(self.toggleActiveColor),
                items: self.colors,
                selected: self.activeColorState.key
            )),
            PreferencesRow(localizedString("Nonactive state color"), component: selectView(
                action: #selector(self.toggleNonactiveColor),
                items: self.colors,
                selected: self.nonactiveColorState.key
            ))
        ]))
        
        return view
    }
    
    @objc private func toggleActiveColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newColor = SColor.allCases.first(where: { $0.key == key }) {
            self.activeColorState = newColor
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_activeColor", value: key)
        self.display()
    }
    
    @objc private func toggleNonactiveColor(_ sender: NSMenuItem) {
        guard let key = sender.representedObject as? String else { return }
        if let newColor = SColor.allCases.first(where: { $0.key == key }) {
            self.nonactiveColorState = newColor
        }
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_nonactiveColor", value: key)
        self.display()
    }
}


================================================
FILE: Kit/Widgets/Tachometer.swift
================================================
//
//  Tachometer.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 11/10/2021.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2021 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public class Tachometer: WidgetWrapper {
    private var labelState: Bool = false
    private var monochromeState: Bool = false
    
    private var chart: TachometerGraphView = TachometerGraphView(
        frame: NSRect(
            x: Constants.Widget.margin.x,
            y: Constants.Widget.margin.y,
            width: Constants.Widget.height,
            height: Constants.Widget.height
        ), segments: []
    )
    private var labelView: NSView? = nil
    
    private let size: CGFloat = Constants.Widget.height - (Constants.Widget.margin.y*2) + (Constants.Widget.margin.x*2)
    
    public init(title: String, preview: Bool = false) {
        let widgetTitle: String = title
        
        super.init(.tachometer, title: widgetTitle, frame: CGRect(
            x: Constants.Widget.margin.x,
            y: Constants.Widget.margin.y,
            width: self.size,
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        self.canDrawConcurrently = true
        
        if preview {
            self.chart.setSegments([
                circle_segment(value: 0.20, color: NSColor.systemRed),
                circle_segment(value: 0.57, color: NSColor.systemBlue)
            ])
        } else {
            self.labelState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_label", defaultValue: self.labelState)
            self.monochromeState = Store.shared.bool(key: "\(self.title)_\(self.type.rawValue)_monochrome", defaultValue: self.monochromeState)
        }
        
        self.draw()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func draw() {
        let x: CGFloat = self.labelState ? 8 + Constants.Widget.spacing : 0
        
        self.labelView = WidgetLabelView(self.title, height: self.frame.height)
        self.labelView!.isHidden = !self.labelState
        
        self.addSubview(self.labelView!)
        self.addSubview(self.chart)
        
        self.chart.setFrame(NSRect(x: x, y: 0, width: self.frame.size.height, height: self.frame.size.height))
        
        self.setFrameSize(NSSize(width: self.size + x, height: self.frame.size.height))
        self.setWidth(self.size + x)
    }
    
    public func setValue(_ list: [circle_segment]) {
        var segments = list
        
        if self.monochromeState {
            for i in 0..<segments.count {
                segments[i].color = segments[i].color.grayscaled()
            }
        }
        
        DispatchQueue.main.async(execute: {
            self.chart.setSegments(segments)
        })
    }
    
    // MARK: - Settings
    
    public override func settings() -> NSView {
        let view = SettingsContainerView()
        
        view.addArrangedSubview(PreferencesSection([
            PreferencesRow(localizedString("Label"), component: switchView(
                action: #selector(self.toggleLabel),
                state: self.labelState
            )),
            PreferencesRow(localizedString("Monochrome accent"), component: switchView(
                action: #selector(self.toggleMonochrome),
                state: self.monochromeState
            ))
        ]))
        
        return view
    }
    
    @objc private func toggleLabel(_ sender: NSControl) {
        self.labelState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_label", value: self.labelState)
        
        let x = self.labelState ? 6 + Constants.Widget.spacing : 0
        self.labelView!.isHidden = !self.labelState
        self.chart.setFrameOrigin(NSPoint(x: x, y: 0))
        self.setWidth(self.labelState ? self.size+x : self.size)
    }
    
    @objc private func toggleMonochrome(_ sender: NSControl) {
        self.monochromeState = controlState(sender)
        Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_monochrome", value: self.monochromeState)
    }
}


================================================
FILE: Kit/Widgets/Text.swift
================================================
//
//  Text.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 08/09/2024
//  Using Swift 5.0
//  Running on macOS 14.6
//
//  Copyright © 2024 Serhiy Mytrovtsiy. All rights reserved.
//  

import Cocoa

public class TextWidget: WidgetWrapper {
    private var value: String = ""
    
    public init(title: String, config: NSDictionary?, preview: Bool = false) {
        super.init(.text, title: title, frame: CGRect(
            x: 0,
            y: Constants.Widget.margin.y,
            width: 30 + (2*Constants.Widget.margin.x),
            height: Constants.Widget.height - (2*Constants.Widget.margin.y)
        ))
        
        if preview {
            self.value = "Text"
        }
        
        self.canDrawConcurrently = true
    }
    
    required public init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        var value: String = ""
        self.queue.sync {
            value = self.value
        }
        
        if value.isEmpty {
            self.setWidth(0)
            return
        }
        
        let valueSize: CGFloat = 12
        let style = NSMutableParagraphStyle()
        style.alignment = .center
        let stringAttributes = [
            NSAttributedString.Key.font: NSFont.systemFont(ofSize: valueSize, weight: .regular),
            NSAttributedString.Key.foregroundColor: NSColor.textColor,
            NSAttributedString.Key.paragraphStyle: style
        ]
        let attributedString = NSAttributedString(string: value, attributes: stringAttributes)
        let size = attributedString.boundingRect(
            with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude),
            options: [.usesLineFragmentOrigin, .usesFontLeading]
        )
        let width = (size.width+Constants.Widget.margin.x*2).roundedUpToNearestTen()
        let origin: CGPoint = CGPoint(x: Constants.Widget.margin.x, y: ((Constants.Widget.height-valueSize-1)/2))
        let rect = CGRect(x: origin.x, y: origin.y, width: width - (Constants.Widget.margin.x*2), height: valueSize)
        attributedString.draw(with: rect)
        
        self.setWidth(width)
    }
    
    public func setValue(_ newValue: String) {
        guard self.value != newValue else { return }
        self.value = newValue
        DispatchQueue.main.async(execute: {
            self.display()
        })
    }
    
    static public func parseText(_ raw: String) -> [KeyValue_t] {
        var pairs: [KeyValue_t] = []
        do {
            let regex = try NSRegularExpression(pattern: "(\\$[a-zA-Z0-9_]+)(?:\\.([a-zA-Z0-9_]+))?")
            let matches = regex.matches(in: raw, range: NSRange(raw.startIndex..., in: raw))
            for match in matches {
                if let keyRange = Range(match.range(at: 1), in: raw) {
                    let key = String(raw[keyRange])
                    let value: String?
                    if match.range(at: 2).location != NSNotFound, let valueRange = Range(match.range(at: 2), in: raw) {
                        value = String(raw[valueRange])
                    } else {
                        value = nil
                    }
                    pairs.append(KeyValue_t(key: key, value: value ?? ""))
                }
            }
        } catch {
            print("Error creating regex: \(error.localizedDescription)")
        }
        return pairs
    }
}


================================================
FILE: Kit/constants.swift
================================================
//
//  constants.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 15/04/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa

public struct Popup_c_s {
    public let width: CGFloat = 264
    public let height: CGFloat = 300
    public let margins: CGFloat = 8
    public let spacing: CGFloat = 2
    public let headerHeight: CGFloat = 42
    public let separatorHeight: CGFloat = 30
    public let portalHeight: CGFloat = 120
}

public struct Settings_c_s {
    public let width: CGFloat = 540
    public let height: CGFloat = 480
    public let margin: CGFloat = 10
    public let row: CGFloat = 30
}

public struct Widget_c_s {
    public let width: CGFloat = 32
    public var height: CGFloat {
        get {
            let systemHeight = NSApplication.shared.mainMenu?.menuBarHeight
            return (systemHeight == 0 ? 22 : systemHeight) ?? 22
        }
    }
    public var margin: CGPoint {
        get { CGPoint(x: 0, y: 2) }
    }
    public let spacing: CGFloat = 2
}

public struct Constants {
    public static let Popup: Popup_c_s = Popup_c_s()
    public static let Settings: Settings_c_s = Settings_c_s()
    public static let Widget: Widget_c_s = Widget_c_s()
    
    public static let defaultProcessIcon = NSWorkspace.shared.icon(forFile: "/bin/bash")
}

public enum ModuleType: Int {
    case CPU
    case RAM
    case GPU
    case disk
    case sensors
    case network
    case battery
    case bluetooth
    case clock
    
    case combined
    
    public var stringValue: String {
        switch self {
        case .CPU: return "CPU"
        case .RAM: return "RAM"
        case .GPU: return "GPU"
        case .disk: return "Disk"
        case .sensors: return "Sensors"
        case .network: return "Network"
        case .battery: return "Battery"
        case .bluetooth: return "Bluetooth"
        case .clock: return "Clock"
        case .combined: return ""
        }
    }
}


================================================
FILE: Kit/extensions.swift
================================================
//
//  extensions.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 10/04/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//

import Cocoa
import Carbon

extension String: @retroactive LocalizedError {
    public var errorDescription: String? { return self }
    
    public var nilIfEmpty: String? { self.isEmpty ? nil : self }
    
    public var digits: String {
        return components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
    }
    
    public func widthOfString(usingFont font: NSFont) -> CGFloat {
        let fontAttributes = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttributes)
        return size.width
    }
    
    public func condenseWhitespace() -> String {
        let components = self.components(separatedBy: .whitespacesAndNewlines)
        return components.filter { !$0.isEmpty }.joined(separator: " ")
    }
    
    public func findAndCrop(pattern: String) -> (cropped: String, remain: String) {
        do {
            let regex = try NSRegularExpression(pattern: pattern)
            let range = NSRange(self.startIndex..., in: self)
            
            if let match = regex.firstMatch(in: self, options: [], range: range) {
                if let range = Range(match.range, in: self) {
                    let cropped = String(self[range]).trimmingCharacters(in: .whitespaces)
                    let remaining = self.replacingOccurrences(of: cropped, with: "", options: .regularExpression).trimmingCharacters(in: .whitespaces)
                    return (cropped, remaining)
                }
            }
        } catch {
            print("Error creating regex: \(error.localizedDescription)")
        }
        
        return ("", self)
    }
    
    public func find(pattern: String) -> String {
        do {
            let regex = try NSRegularExpression(pattern: pattern)
            let stringRange = NSRange(location: 0, length: self.utf16.count)
            
            if let searchRange = regex.firstMatch(in: self, options: [], range: stringRange) {
                let start = self.index(self.startIndex, offsetBy: searchRange.range.lowerBound)
                let end = self.index(self.startIndex, offsetBy: searchRange.range.upperBound)
                let value  = String(self[start..<end]).trimmingCharacters(in: .whitespaces)
                return value.trimmingCharacters(in: .whitespaces)
            }
        } catch {}
        
        return ""
    }
    
    public var trimmed: String {
        var buf = [UInt8]()
        var trimming = true
        for c in self.utf8 {
            if trimming && c < 33 { continue }
            trimming = false
            buf.append(c)
        }
        
        while let last = buf.last, last < 33 {
            buf.removeLast()
        }
        
        buf.append(0)
        return String(cString: buf)
    }
    
    public func matches(_ regex: String) -> Bool {
        return self.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
    }
    
    public func removedRegexMatches(pattern: String, replaceWith: String = "") -> String {
        do {
            let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options.caseInsensitive)
            let range = NSRange(location: 0, length: self.count)
            return regex.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: replaceWith)
        } catch {
            return self
        }
    }
    
    func removingWhitespaces() -> String {
        return components(separatedBy: .whitespaces).joined()
    }
}

public extension DispatchSource.MemoryPressureEvent {
    func pressureColor() -> NSColor {
        switch self {
        case .normal:
            return NSColor.systemGreen
        case .warning:
            return NSColor.systemYellow
        case .critical:
            return NSColor.systemRed
        default:
            return .controlAccentColor
        }
    }
}

public extension Double {
    func roundTo(decimalPlaces: Int) -> String {
        return NSString(format: "%.\(decimalPlaces)f" as NSString, self) as String
    }
    
    func rounded(toPlaces places: Int) -> Double {
        let divisor = pow(10.0, Double(places))
        return (self * divisor).rounded() / divisor
    }
    
    func usageColor(zones: colorZones = (0.6, 0.8), reversed: Bool = false) -> NSColor {
        let firstColor: NSColor = NSColor.systemBlue
        let secondColor: NSColor = NSColor.orange
        let thirdColor: NSColor = NSColor.red
        
        if reversed {
            switch self {
            case 0...zones.orange:
                return thirdColor
            case zones.orange...zones.red:
                return secondColor
            default:
                return firstColor
            }
        } else {
            switch self {
            case 0...zones.orange:
                return firstColor
            case zones.orange...zones.red:
                return secondColor
            default:
                return thirdColor
            }
        }
    }
    
    func batteryColor(color: Bool = false, lowPowerMode: Bool? = nil) -> NSColor {
        if let mode = lowPowerMode, mode {
            return NSColor.systemOrange
        }
        
        switch self {
        case 0.2...0.4:
            if !color {
                return NSColor.textColor
            }
            return NSColor.systemOrange
        case 0.4...1:
            if self == 1 {
                return NSColor.textColor
            }
            if !color {
                return NSColor.textColor
            }
            return NSColor.systemGreen
        default:
            return NSColor.systemRed
        }
    }
    
    func secondsToHoursMinutesSeconds() -> (Int, Int) {
        let mins = (self.truncatingRemainder(dividingBy: 3600)) / 60
        return (Int(self / 3600), Int(mins))
    }
    
    func printSecondsToHoursMinutesSeconds(short: Bool = false) -> String {
        let (h, m) = self.secondsToHoursMinutesSeconds()
        
        if self == 0 || h < 0 || m < 0 {
            return "n/a"
        }
        
        let minutes = m > 9 ? "\(m)" : "0\(m)"
        
        if short {
            return "\(h):\(minutes)"
        }
        
        if h == 0 {
            return "\(minutes)min"
        } else if m == 0 {
            return "\(h)h"
        }
        
        return "\(h)h \(minutes)min"
    }
    
    func power(_ unit: String) -> Double {
        switch unit {
        case "mJ":
            return self / 1e3
        case "uJ":
            return self / 1e6
        case "nJ":
            return self / 1e9
        default:
            return 0
        }
    }
}

public extension NSView {
    var isDarkMode: Bool {
        switch effectiveAppearance.name {
        case .darkAqua, .vibrantDark, .accessibilityHighContrastDarkAqua, .accessibilityHighContrastVibrantDark:
            return true
        default:
            return false
        }
    }
    
    func toggleSettingRow(title: String, action: Selector, state: Bool) -> NSView {
        let view: NSStackView = NSStackView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.heightAnchor.constraint(equalToConstant: Constants.Settings.row).isActive = true
        view.orientation = .horizontal
        view.alignment = .centerY
        view.distribution = .fill
        view.spacing = 0
        
        let titleField: NSTextField = LabelField(frame: NSRect(x: 0, y: 0, width: 0, height: 0), title)
        titleField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
        titleField.textColor = .textColor
        
        let state: NSControl.StateValue = state ? .on : .off
        var toggle: NSControl = NSControl()
        if #available(OSX 10.15, *) {
            let switchButton = NSSwitch()
            switchButton.state = state
            switchButton.action = action
            switchButton.target = self
            
            toggle = switchButton
        } else {
            let button: NSButton = NSButton()
            button.setButtonType(.switch)
            button.state = state
            button.title = ""
            button.action = action
            button.isBordered = false
            button.isTransparent = false
            button.target = self
            button.wantsLayer = true
            
            toggle = button
        }
        
        view.addArrangedSubview(titleField)
        view.addArrangedSubview(NSView())
        view.addArrangedSubview(toggle)
        
        return view
    }
    
    func selectSettingsRow(title: String, action: Selector, items: [KeyValue_p], selected: String) -> NSView {
        let view = NSStackView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.heightAnchor.constraint(equalToConstant: Constants.Settings.row).isActive = true
        view.orientation = .horizontal
        view.alignment = .centerY
        view.distribution = .fill
        view.spacing = 0
        
        let titleField: NSTextField = LabelField(frame: NSRect(x: 0, y: 0, width: 0, height: 0), title)
        titleField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
        titleField.textColor = .textColor
        
        let select: NSPopUpButton = selectView(action: action, items: items, selected: selected)
        select.sizeToFit()
        
        view.addArrangedSubview(titleField)
        view.addArrangedSubview(NSView())
        view.addArrangedSubview(select)
        
        return view
    }
    
    func selectView(action: Selector, items: [KeyValue_p], selected: String) -> NSPopUpButton {
        let select: NSPopUpButton = NSPopUpButton(frame: NSRect(x: 0, y: 4, width: 50, height: 28))
        select.target = self
        select.action = action
        
        let menu = NSMenu()
        items.forEach { (item) in
            if item.key.contains("separator") {
                menu.addItem(NSMenuItem.separator())
            } else {
                let interfaceMenu = NSMenuItem(title: localizedString(item.value), action: nil, keyEquivalent: "")
                interfaceMenu.representedObject = item.key
                menu.addItem(interfaceMenu)
                if selected == item.key {
                    interfaceMenu.state = .on
                }
            }
        }
        select.menu = menu
        
        return select
    }
    
    func switchView(action: Selector, state: Bool) -> NSSwitch {
        let s = NSSwitch()
        s.heightAnchor.constraint(equalToConstant: 25).isActive = true
        s.controlSize = .mini
        s.state = state ? .on : .off
        s.action = action
        s.target = self
        return s
    }
    
    func buttonView(_ action: Selector, text: String) -> NSButton {
        let button = NSButton()
        button.title = text
        button.contentTintColor = .labelColor
        button.action = action
        button.target = self
        return button
    }
    
    func buttonIconView(_ action: Selector, icon: NSImage, height: CGFloat = 22) -> NSButton {
        let button = NSButton()
        button.heightAnchor.constraint(equalToConstant: height).isActive = true
        button.bezelStyle = .regularSquare
        button.translatesAutoresizingMaskIntoConstraints = false
        button.imageScaling = .scaleNone
        button.image = icon
        button.contentTintColor = .labelColor
        button.isBordered = false
        button.action = action
        button.target = self
        button.focusRingType = .none
        return button
    }
    
    func textView(_ value: String, alignment: NSTextAlignment = .left) -> NSTextField {
        let field: NSTextField = TextView()
        field.font = NSFont.systemFont(ofSize: 13, weight: .regular)
        field.stringValue = value
        field.isSelectable = true
        field.alignment = alignment
        return field
    }
    
    func sliderView(action: Selector, value: Int, initialValue: String, min: Double = 1, max: Double = 100, valueWidth: CGFloat = 40) -> NSView {
        let view: NSStackView = NSStackView()
        view.orientation = .horizontal
        view.widthAnchor.constraint(equalToConstant: 195).isActive = true
        
        let valueField: NSTextField = LabelField(initialValue)
        valueField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
        valueField.textColor = .textColor
        valueField.alignment = .center
        valueField.widthAnchor.constraint(equalToConstant: valueWidth).isActive = true
        
        let slider = NSSlider()
        slider.controlSize = .small
        slider.minValue = min
        slider.maxValue = max
        slider.intValue = Int32(value)
        slider.target = self
        slider.isContinuous = true
        slider.action = action
        slider.sizeToFit()
        
        view.addArrangedSubview(slider)
        view.addArrangedSubview(valueField)
        
        return view
    }
}

public class NSButtonWithPadding: NSButton {
    public var horizontalPadding: CGFloat = 0
    public var verticalPadding: CGFloat = 0
    
    public override var intrinsicContentSize: NSSize {
        var size = super.intrinsicContentSize
        size.width += self.horizontalPadding
        size.height += self.verticalPadding
        return size
    }
}

public class TextView: NSTextField {
    public override init(frame: NSRect = .zero) {
        super.init(frame: frame)
        
        self.isEditable = false
        self.isSelectable = false
        self.isBezeled = false
        self.wantsLayer = true
        self.textColor = .labelColor
        self.backgroundColor = .clear
        self.canDrawSubviewsIntoLayer = true
        self.alignment = .natural
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

public extension OperatingSystemVersion {
    func getFullVersion(separator: String = ".") -> String {
        return "\(majorVersion)\(separator)\(minorVersion)\(separator)\(patchVersion)"
    }
}

extension URL {
    func checkFileExist() -> Bool {
        return FileManager.default.fileExists(atPath: self.path)
    }
}

public extension NSColor {
    func grayscaled() -> NSColor {
        guard let space = CGColorSpace(name: CGColorSpace.extendedGray),
              let cg = self.cgColor.converted(to: space, intent: .perceptual, options: nil),
              let color = NSColor.init(cgColor: cg) else {
            return self
        }
        return color
    }
}

public class FlippedStackView: NSStackView {
    public override var isFlipped: Bool { return true }
}

open class ScrollableStackView: NSView {
    public var stackView: NSStackView = FlippedStackView()
    
    private let clipView: NSClipView = NSClipView()
    private let scrollView: NSScrollView = NSScrollView()
    
    public var scrollWidth: CGFloat? {
        self.scrollView.verticalScroller?.frame.size.width
    }
    
    public init(frame: NSRect = NSRect.zero, orientation: NSUserInterfaceLayoutOrientation = .vertical) {
        super.init(frame: frame)
        
        self.clipView.drawsBackground = false
        
        self.stackView.orientation = orientation
        self.stackView.translatesAutoresizingMaskIntoConstraints = false
        
        self.scrollView.translatesAutoresizingMaskIntoConstraints = false
        if orientation == .vertical {
            self.scrollView.hasVerticalScroller = true
            self.scrollView.hasHorizontalScroller = false
            self.scrollView.autohidesScrollers = true
            self.scrollView.horizontalScrollElasticity = .none
        } else {
            self.scrollView.hasVerticalScroller = false
            self.scrollView.hasHorizontalScroller = true
            self.scrollView.autohidesScrollers = true
            self.scrollView.verticalScrollElasticity = .none
        }
        self.scrollView.drawsBackground = false
        self.scrollView.contentView = self.clipView
        self.scrollView.documentView = self.stackView
        
        self.addSubview(self.scrollView)
        
        NSLayoutConstraint.activate([
            self.scrollView.leftAnchor.constraint(equalTo: self.leftAnchor),
            self.scrollView.rightAnchor.constraint(equalTo: self.rightAnchor),
            self.scrollView.topAnchor.constraint(equalTo: self.topAnchor),
            self.scrollView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
            
            self.stackView.leftAnchor.constraint(equalTo: self.clipView.leftAnchor),
            self.stackView.topAnchor.constraint(equalTo: self.clipView.topAnchor)
        ])
        
        if orientation == .vertical {
            self.stackView.rightAnchor.constraint(equalTo: self.clipView.rightAnchor).isActive = true
        } else {
            self.stackView.bottomAnchor.constraint(equalTo: self.clipView.bottomAnchor).isActive = true
        }
    }
    
    required public init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

// https://stackoverflow.com/a/54492165
extension NSTextView {
    override open func performKeyEquivalent(with event: NSEvent) -> Bool {
        let commandKey = NSEvent.ModifierFlags.command.rawValue
        let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue
        if event.type == NSEvent.EventType.keyDown {
            if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
                switch event.charactersIgnoringModifiers! {
                case "x":
                    if NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self) { return true }
                case "c":
                    if NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self) { return true }
                case "v":
                    if NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self) { return true }
                case "z":
                    if NSApp.sendAction(Selector(("undo:")), to: nil, from: self) { return true }
                case "a":
                    if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self) { return true }
                default:
                    break
                }
            } else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
                if event.charactersIgnoringModifiers == "Z" {
                    if NSApp.sendAction(Selector(("redo:")), to: nil, from: self) { return true }
                }
            }
        }
        return super.performKeyEquivalent(with: event)
    }
}

public extension Data {
    var socketAddress: sockaddr {
        return withUnsafeBytes { $0.load(as: sockaddr.self) }
    }
}

public extension Date {
    func convertToTimeZone(_ timeZone: TimeZone) -> Date {
        return addingTimeInterval(TimeInterval(timeZone.secondsFromGMT(for: self) - TimeZone.current.secondsFromGMT(for: self)))
    }
    
    func currentTimeSeconds() -> Int {
        return Int(self.timeIntervalSince1970)
    }
}

public extension TimeZone {
    init(from: String) {
        if let tz = TimeZone(identifier: from) {
            self = tz
            return
        }
        
        if from == "local" {
            self = TimeZone.current
            return
        }
        
        let arr = from.split(separator: ":")
        guard !arr.isEmpty else {
            self = TimeZone.current
            return
        }
        
        var secondsFromGMT = 0
        if arr.indices.contains(0), let h = Int(arr[0]) {
            secondsFromGMT += h*3600
        }
        if arr.indices.contains(1), let m = Int(arr[1]) {
            if secondsFromGMT < 0 {
                secondsFromGMT -= m*60
            } else {
                secondsFromGMT += m*60
            }
        }
        
        if let tz = TimeZone(secondsFromGMT: secondsFromGMT) {
            self = tz
        } else {
            self = TimeZone.current
        }
    }
}

extension CGFloat {
    func roundedUpToNearestTen() -> CGFloat {
        return ceil(self / 10) * 10
    }
}

public class KeyboardShartcutView: NSStackView {
    private let callback: (_ value: [UInt16]) -> Void
    
    private var startIcon: NSImage { iconFromSymbol(name: "record.circle", scale: .large) }
    private var stopIcon: NSImage { iconFromSymbol(name: "stop.circle.fill", scale: .large) }
    
    private var valueField: NSTextField? = nil
    private var startButton: NSButton? = nil
    private var stopButton: NSButton? = nil
    
    private var recording: Bool = false
    private var keyCodes: [UInt16] = []
    private var value: [UInt16] = []
    private var interaction: Bool = false
    
    public init(callback: @escaping (_ value: [UInt16]) -> Void, value: [UInt16]) {
        self.callback = callback
        self.value = value
        
        super.init(frame: NSRect.zero)
        self.orientation = .horizontal
        
        let stringValue = value.isEmpty ? localizedString("Disabled") : self.parseValue(value)
        let valueField: NSTextField = LabelField(stringValue)
        valueField.font = NSFont.systemFont(ofSize: 13, weight: .regular)
        valueField.textColor = .textColor
        valueField.alignment = .center
        
        let startButton = buttonIconView(#selector(self.startListening), icon: self.startIcon, height: 15)
        let stopButton = buttonIconView(#selector(self.stopListening), icon: self.stopIcon, height: 15)
        
        self.addArrangedSubview(valueField)
        self.addArrangedSubview(startButton)
        
        self.valueField = valueField
        self.startButton = startButton
        self.stopButton = stopButton
        
        NSEvent.addLocalMonitorForEvents(matching: [.keyDown, .flagsChanged]) { [weak self] event in
            self?.handleKeyEvent(event)
            return event
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc private func startListening() {
        guard AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary) else { return }
        if let btn = self.stopButton {
            self.startButton?.removeFromSuperview()
            self.addArrangedSubview(btn)
        }
        self.valueField?.stringValue = localizedString("Listening...")
        self.keyCodes = []
        self.recording = true
    }
    
    @objc private func stopListening() {
        if let btn = self.startButton {
            self.stopButton?.removeFromSuperview()
            self.addArrangedSubview(btn)
        }
        
        if self.keyCodes.isEmpty && !self.interaction {
            self.value = []
            self.valueField?.stringValue = localizedString("Disabled")
        }
        
        self.recording = false
        self.interaction = false
        self.callback(self.value)
    }
    
    private func handleKeyEvent(_ event: NSEvent) {
        guard self.recording else { return }
        self.interaction = true
        
        if event.type == .flagsChanged {
            self.keyCodes = []
            if event.modifierFlags.contains(.control) { self.keyCodes.append(59) }
            if event.modifierFlags.contains(.shift) { self.keyCodes.append(60) }
            if event.modifierFlags.contains(.command) { self.keyCodes.append(55) }
            if event.modifierFlags.contains(.option) { self.keyCodes.append(58) }
        } else if event.type == .keyDown {
            self.keyCodes.append(event.keyCode)
            self.value = self.keyCodes
        }
        
        let list = self.keyCodes.isEmpty ? self.value : self.keyCodes
        self.valueField?.stringValue = self.parseValue(list)
    }
    
    private func parseValue(_ list: [UInt16]) -> String {
        return list.compactMap { self.keyName(virtualKeyCode: $0) }.joined(separator: " + ")
    }
    
    private func keyName(virtualKeyCode: UInt16) -> String? {
        if virtualKeyCode == 59 {
            return "Control"
        } else if virtualKeyCode == 60 {
            return "Shift"
        } else if virtualKeyCode == 55 {
            return "Command"
        } else if virtualKeyCode == 58 {
            return "Option"
        }
        
        let maxNameLength = 4
        var nameBuffer = [UniChar](repeating: 0, count: maxNameLength)
        var nameLength = 0
        
        let modifierKeys = UInt32(alphaLock >> 8) & 0xFF // Caps Lock
        var deadKeys: UInt32 = 0
        let keyboardType = UInt32(LMGetKbdType())
        
        let source = TISCopyCurrentKeyboardLayoutInputSource().takeRetainedValue()
        guard let ptr = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) else {
            NSLog("Could not get keyboard layout data")
            return nil
        }
        let layoutData = Unmanaged<CFData>.fromOpaque(ptr).takeUnretainedValue() as Data
        let osStatus = layoutData.withUnsafeBytes {
            UCKeyTranslate($0.bindMemory(to: UCKeyboardLayout.self).baseAddress, virtualKeyCode, UInt16(kUCKeyActionDown),
                           modifierKeys, keyboardType, UInt32(kUCKeyTranslateNoDeadKeysMask),
                           &deadKeys, maxNameLength, &nameLength, &nameBuffer)
        }
        guard osStatus == noErr else {
            NSLog("Code: 0x%04X  Status: %+i", virtualKeyCode, osStatus)
            return nil
        }
        
        return  String(utf16CodeUnits: nameBuffer, count: nameLength)
    }
}


================================================
FILE: Kit/helpers.swift
================================================
//
//  helpers.swift
//  Kit
//
//  Created by Serhiy Mytrovtsiy on 29/09/2020.
//  Using Swift 5.0.
//  Running on macOS 10.15.
//
//  Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//
// swiftlint:disable file_length

import Cocoa
import ServiceManagement
import UserNotifications
import WebKit
import Metal

public struct LaunchAtLogin {
    private static let id = "\(Bundle.main.bundleIdentifier!).LaunchAtLogin"
    
    public static var isEnabled: Bool {
        get {
            if #available(macOS 13, *) {
                return isEnabledNext
            } else {
                return isEnabledLegacy
            }
        }
        set {
            if #available(macOS 13, *) {
                isEnabledNext = newValue
            } else {
                isEnabledLegacy = newValue
            }
        }
    }
    
    private static var isEnabledLegacy: Bool {
        get {
            guard let jobs = (LaunchAtLogin.self as DeprecationWarningWorkaround.Type).jobsDict else {
                return false
            }
            let job = jobs.first { $0["Label"] as! String == id }
            return job?["OnDemand"] as? Bool ?? false
        }
        set {
            SMLoginItemSetEnabled(id as CFString, newValue)
        }
    }
    
    @available(macOS 13, *)
    private static var isEnabledNext: Bool {
        get { SMAppService.mainApp.status == .enabled }
        set {
            do {
                if newValue {
                    if SMAppService.mainApp.status == .enabled {
                        try? SMAppService.mainApp.unregister()
                    }
                    try SMAppService.mainApp.register()
                } else {
                    try SMAppService.mainApp.unregister()
                }
            } catch {
                print("failed to \(newValue ? "enable" : "disable") launch at login: \(error.localizedDescription)")
            }
        }
    }
    
    public static func migrate() {
        guard #available(macOS 13, *), !Store.shared.exist(key: "LaunchAtLoginNext") else {
            return
        }
        
        Store.shared.set(key: "LaunchAtLoginNext", value: true)
        isEnabledNext = isEnabledLegacy
        isEnabledLegacy = false
        try? SMAppService.loginItem(identifier: id).unregister()
    }
}

private protocol DeprecationWarningWorkaround {
    static var jobsDict: [[String: AnyObject]]? { get }
}

extension LaunchAtLogin: DeprecationWarningWorkaround {
    @available(*, deprecated)
    static var jobsDict: [[String: AnyObject]]? {
        SMCopyAllJobDictionaries(kSMDomainUserLaunchd)?.takeRetainedValue() as? [[String: AnyObject]]
    }
}

public protocol KeyValue_p {
    var key: String { get }
    var value: String { get }
}

public struct KeyValue_t: KeyValue_p, Codable {
    public let key: String
    public let value: String
    public let additional: Any?
    
    private enum CodingKeys: String, CodingKey {
        case key, value
    }
    
    public init(key: String, value: String, additional: Any? = nil) {
        self.key = key
        self.value = value
        self.additional = additional
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.key = try container.decode(String.self, forKey: .key)
        self.value = try container.decode(String.self, forKey: .value)
        self.additional = nil
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(key, forKey: .key)
        try container.encode(value, forKey: .value)
    }
}

public struct Units {
    public let bytes: Int64
    
    public init(bytes: Int64) {
        self.bytes = bytes
    }
    
    public var kilobytes: Double {
        return Double(bytes) / 1_000
    }
    public var megabytes: Double {
        return kilobytes / 1_000
    }
    public var gigabytes: Double {
        return megabytes / 1_000
    }
    public var terabytes: Double {
        return gigabytes / 1_000
    }
    
    public func getReadableTuple(base: DataSizeBase = .byte) -> (String, String) {
        let stringBase = base == .byte ? "B" : "b"
        let multiplier: Double = base == .byte ? 1 : 8
        
        switch bytes {
        case 0..<1_000:
            return ("0", "K\(stringBase)/s")
        case 1_000..<(1_000 * 1_000):
            return (String(format: "%.0f", kilobytes*multiplier), "K\(stringBase)/s")
        case 1_000..<(1_000 * 1_000 * 100):
            return (String(format: "%.1f", megabytes*multiplier), "M\(stringBase)/s")
        case (1_000 * 1_000 * 100)..<(1_000 * 1_000 * 1_000):
            return (String(format: "%.0f", megabytes*multiplier), "M\(stringBase)/s")
        case (1_000 * 1_000 * 1_000)...Int64.max:
            return (String(format: "%.1f", gigabytes*multiplier), "G\(stringBase)/s")
        default:
            return (String(format: "%.0f", kilobytes*multiplier), "K\(stringBase)B/s")
        }
    }
    
    public func getReadableSpeed(base: DataSizeBase = .byte, omitUnits: Bool = false) -> String {
        let stringBase = base == .byte ? "B" : "b"
        let multiplier: Double = base == .byte ? 1 : 8
        
        switch bytes*Int64(multiplier) {
        case 0..<1_000:
            let unit = omitUnits ? "" : " K\(stringBase)/s"
            return "0\(unit)"
        case 1_000..<(1_000 * 1_000):
            let unit = omitUnits ? "" : " K\(stringBase)/s"
            return String(format: "%.0f\(unit)", kilobytes*multiplier)
        case 1_000..<(1_000 * 1_000 * 100):
            let unit = omitUnits ? "" : " M\(stringBase)/s"
            return String(format: "%.1f\(unit)", megabytes*multiplier)
        case (1_000 * 1_000 * 100)..<(1_000 * 1_000 * 1_000):
            let unit = omitUnits ? "" : " M\(stringBase)/s"
            return String(format: "%.0f\(unit)", megabytes*multiplier)
        case (1_000 * 1_000 * 1_000)...Int64.max:
            let unit = omitUnits ? "" : " G\(stringBase)/s"
            return String(format: "%.1f\(unit)", gigabytes*multiplier)
        default:
            let unit = omitUnits ? "" : " K\(stringBase)/s"
            return String(format: "%.0f\(unit)", kilobytes*multiplier)
        }
    }
    
    public func getReadableMemory(style: ByteCountFormatter.CountStyle = .file) -> String {
        let formatter: ByteCountFormatter = ByteCountFormatter()
        formatter.countStyle = style
        formatter.includesUnit = true
        formatter.isAdaptive = true
        
        var value = formatter.string(fromByteCount: Int64(self.bytes))
        if let idx = value.lastIndex(of: ",") {
            value.replaceSubrange(idx...idx, with: ".")
        }
        
        return value
    }
    
    public func toUnit(_ unit: SizeUnit) -> Double {
        switch unit {
        case .KB: return self.kilobytes
        case .MB: return self.megabytes
        case .GB: return self.gigabytes
        case .TB: return self.terabytes
        default: return Double(self.bytes)
        }
    }
}

public struct DiskSize {
    public let value: Int64
    
    public init(_ size: Int64) {
        self.value = size
    }
    
    public var kilobytes: Double {
        return Double(value) / 1_000
    }
    public var megabytes: Double {
        return kilobytes / 1_000
    }
    public var gigabytes: Double {
        return megabytes / 1_000
    }
    public var terabytes: Double {
        return gigabytes / 1_000
    }
   
Download .txt
gitextract_0i6vpw9i/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.md
│   └── workflows/
│       ├── build.yaml
│       ├── i18n.yaml
│       └── linter.yaml
├── .gitignore
├── .swiftlint.yml
├── Kit/
│   ├── Supporting Files/
│   │   ├── Assets.xcassets/
│   │   │   ├── Contents.json
│   │   │   ├── calendar.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── cancel.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── chart.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── close.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── refresh.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── settings.imageset/
│   │   │   │   └── Contents.json
│   │   │   └── tune.imageset/
│   │   │       └── Contents.json
│   │   ├── Info.plist
│   │   └── Kit.h
│   ├── Widgets/
│   │   ├── BarChart.swift
│   │   ├── Battery.swift
│   │   ├── Label.swift
│   │   ├── LineChart.swift
│   │   ├── Memory.swift
│   │   ├── Mini.swift
│   │   ├── NetworkChart.swift
│   │   ├── PieChart.swift
│   │   ├── Speed.swift
│   │   ├── Stack.swift
│   │   ├── State.swift
│   │   ├── Tachometer.swift
│   │   └── Text.swift
│   ├── constants.swift
│   ├── extensions.swift
│   ├── helpers.swift
│   ├── lldb/
│   │   ├── LICENSE.txt
│   │   ├── include/
│   │   │   ├── c.h
│   │   │   ├── cache.h
│   │   │   ├── comparator.h
│   │   │   ├── db.h
│   │   │   ├── dumpfile.h
│   │   │   ├── env.h
│   │   │   ├── export.h
│   │   │   ├── filter_policy.h
│   │   │   ├── iterator.h
│   │   │   ├── options.h
│   │   │   ├── slice.h
│   │   │   ├── status.h
│   │   │   ├── table.h
│   │   │   ├── table_builder.h
│   │   │   └── write_batch.h
│   │   ├── libleveldb.a
│   │   ├── lldb.h
│   │   └── lldb.m
│   ├── module/
│   │   ├── module.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── reader.swift
│   │   ├── widget.swift
│   │   └── window.swift
│   ├── plugins/
│   │   ├── Charts.swift
│   │   ├── DB.swift
│   │   ├── Logger.swift
│   │   ├── Reachability.swift
│   │   ├── Remote.swift
│   │   ├── Repeater.swift
│   │   ├── Store.swift
│   │   ├── SystemKit.swift
│   │   └── Updater.swift
│   ├── process.swift
│   ├── scripts/
│   │   ├── SMJobBlessUtil.py
│   │   ├── changelog.py
│   │   ├── i18n.py
│   │   ├── uninstall.sh
│   │   └── updater.sh
│   └── types.swift
├── LICENSE
├── LaunchAtLogin/
│   ├── Info.plist
│   ├── LaunchAtLogin.entitlements
│   └── main.swift
├── Makefile
├── Modules/
│   ├── Battery/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── readers.swift
│   │   └── settings.swift
│   ├── Bluetooth/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── readers.swift
│   │   └── settings.swift
│   ├── CPU/
│   │   ├── Info.plist
│   │   ├── bridge.h
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── readers.swift
│   │   ├── settings.swift
│   │   └── widget.swift
│   ├── Clock/
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── reader.swift
│   │   └── settings.swift
│   ├── Disk/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── header.h
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── readers.swift
│   │   ├── settings.swift
│   │   └── widget.swift
│   ├── GPU/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── reader.swift
│   │   ├── settings.swift
│   │   └── widget.swift
│   ├── Net/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── readers.swift
│   │   ├── settings.swift
│   │   └── widget.swift
│   ├── RAM/
│   │   ├── Info.plist
│   │   ├── config.plist
│   │   ├── main.swift
│   │   ├── notifications.swift
│   │   ├── popup.swift
│   │   ├── portal.swift
│   │   ├── readers.swift
│   │   ├── settings.swift
│   │   └── widget.swift
│   └── Sensors/
│       ├── Info.plist
│       ├── bridge.h
│       ├── config.plist
│       ├── main.swift
│       ├── notifications.swift
│       ├── popup.swift
│       ├── portal.swift
│       ├── reader.m
│       ├── readers.swift
│       ├── settings.swift
│       └── values.swift
├── README.md
├── SMC/
│   ├── Helper/
│   │   ├── Info.plist
│   │   ├── Launchd.plist
│   │   ├── main.swift
│   │   └── protocol.swift
│   ├── Makefile
│   ├── main.swift
│   └── smc.swift
├── Stats/
│   ├── AppDelegate.swift
│   ├── Supporting Files/
│   │   ├── Assets.xcassets/
│   │   │   ├── AppIcon.appiconset/
│   │   │   │   └── Contents.json
│   │   │   ├── Contents.json
│   │   │   ├── ac_unit.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── apps.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── bug.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── devices/
│   │   │   │   ├── Contents.json
│   │   │   │   ├── imac.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── imacPro.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macMini.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macMini2020.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macMini2024.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macPro.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macPro2019.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macStudio.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macbookAir.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macbookAir4thGen.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macbookNeo.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── macbookPro.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   └── macbookPro5thGen.imageset/
│   │   │   │       └── Contents.json
│   │   │   ├── donate.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── high-battery.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── low-battery.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── pause.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── power.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── record.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── resume.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── settings.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── stop.imageset/
│   │   │   │   └── Contents.json
│   │   │   └── support/
│   │   │       ├── Contents.json
│   │   │       ├── github.imageset/
│   │   │       │   └── Contents.json
│   │   │       ├── ko-fi.imageset/
│   │   │       │   └── Contents.json
│   │   │       ├── patreon.imageset/
│   │   │       │   └── Contents.json
│   │   │       └── paypal.imageset/
│   │   │           └── Contents.json
│   │   ├── Info.plist
│   │   ├── Stats/
│   │   │   └── en.xcloc/
│   │   │       ├── Localized Contents/
│   │   │       │   └── en.xliff
│   │   │       ├── Source Contents/
│   │   │       │   ├── LaunchAtLogin/
│   │   │       │   │   └── en.lproj/
│   │   │       │   │       └── InfoPlist.strings
│   │   │       │   ├── ModuleKit/
│   │   │       │   │   └── Supporting Files/
│   │   │       │   │       └── en.lproj/
│   │   │       │   │           └── InfoPlist.strings
│   │   │       │   ├── Modules/
│   │   │       │   │   ├── Battery/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   ├── CPU/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   ├── Disk/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   ├── GPU/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   ├── Memory/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   ├── Net/
│   │   │       │   │   │   └── en.lproj/
│   │   │       │   │   │       └── InfoPlist.strings
│   │   │       │   │   └── Sensors/
│   │   │       │   │       └── en.lproj/
│   │   │       │   │           └── InfoPlist.strings
│   │   │       │   ├── Stats/
│   │   │       │   │   └── Supporting Files/
│   │   │       │   │       └── en.lproj/
│   │   │       │   │           ├── InfoPlist.strings
│   │   │       │   │           └── Localizable.strings
│   │   │       │   └── StatsKit/
│   │   │       │       └── en.lproj/
│   │   │       │           └── InfoPlist.strings
│   │   │       └── contents.json
│   │   ├── Stats.entitlements
│   │   ├── ar.lproj/
│   │   │   └── Localizable.strings
│   │   ├── bg.lproj/
│   │   │   └── Localizable.strings
│   │   ├── ca.lproj/
│   │   │   └── Localizable.strings
│   │   ├── cs.lproj/
│   │   │   └── Localizable.strings
│   │   ├── da.lproj/
│   │   │   └── Localizable.strings
│   │   ├── de.lproj/
│   │   │   └── Localizable.strings
│   │   ├── el.lproj/
│   │   │   └── Localizable.strings
│   │   ├── en-AU.lproj/
│   │   │   └── Localizable.strings
│   │   ├── en-GB.lproj/
│   │   │   └── Localizable.strings
│   │   ├── en.lproj/
│   │   │   └── Localizable.strings
│   │   ├── es.lproj/
│   │   │   └── Localizable.strings
│   │   ├── et.lproj/
│   │   │   └── Localizable.strings
│   │   ├── fa.lproj/
│   │   │   └── Localizable.strings
│   │   ├── fi.lproj/
│   │   │   └── Localizable.strings
│   │   ├── fr.lproj/
│   │   │   └── Localizable.strings
│   │   ├── he.lproj/
│   │   │   └── Localizable.strings
│   │   ├── hi.lproj/
│   │   │   └── Localizable.strings
│   │   ├── hr.lproj/
│   │   │   └── Localizable.strings
│   │   ├── hu.lproj/
│   │   │   └── Localizable.strings
│   │   ├── id.lproj/
│   │   │   └── Localizable.strings
│   │   ├── it.lproj/
│   │   │   └── Localizable.strings
│   │   ├── ja.lproj/
│   │   │   └── Localizable.strings
│   │   ├── ko.lproj/
│   │   │   └── Localizable.strings
│   │   ├── menus.psd
│   │   ├── nb.lproj/
│   │   │   └── Localizable.strings
│   │   ├── nl.lproj/
│   │   │   └── Localizable.strings
│   │   ├── pl.lproj/
│   │   │   └── Localizable.strings
│   │   ├── popups.psd
│   │   ├── pt-BR.lproj/
│   │   │   └── Localizable.strings
│   │   ├── pt-PT.lproj/
│   │   │   └── Localizable.strings
│   │   ├── ro.lproj/
│   │   │   └── Localizable.strings
│   │   ├── ru.lproj/
│   │   │   └── Localizable.strings
│   │   ├── sk.lproj/
│   │   │   └── Localizable.strings
│   │   ├── sl.lproj/
│   │   │   └── Localizable.strings
│   │   ├── sv.lproj/
│   │   │   └── Localizable.strings
│   │   ├── th.lproj/
│   │   │   └── Localizable.strings
│   │   ├── tr.lproj/
│   │   │   └── Localizable.strings
│   │   ├── uk.lproj/
│   │   │   └── Localizable.strings
│   │   ├── vi.lproj/
│   │   │   └── Localizable.strings
│   │   ├── zh-Hans.lproj/
│   │   │   └── Localizable.strings
│   │   └── zh-Hant.lproj/
│   │       └── Localizable.strings
│   ├── Views/
│   │   ├── AppSettings.swift
│   │   ├── CombinedView.swift
│   │   ├── Dashboard.swift
│   │   ├── Settings.swift
│   │   ├── Setup.swift
│   │   ├── Support.swift
│   │   └── Update.swift
│   └── helpers.swift
├── Stats.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── IDEWorkspaceChecks.plist
│   └── xcshareddata/
│       └── xcschemes/
│           ├── SMC.xcscheme
│           ├── Stats.xcscheme
│           └── WidgetsExtension.xcscheme
├── Tests/
│   ├── Info.plist
│   └── RAM.swift
├── Widgets/
│   ├── Supporting Files/
│   │   ├── Assets.xcassets/
│   │   │   ├── AccentColor.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── AppIcon.appiconset/
│   │   │   │   └── Contents.json
│   │   │   ├── Contents.json
│   │   │   └── WidgetBackground.colorset/
│   │   │       └── Contents.json
│   │   ├── Info.plist
│   │   └── Widgets.entitlements
│   ├── UnitedWidget.swift
│   └── widgets.swift
└── exportOptions.plist
Download .txt
SYMBOL INDEX (73 symbols across 20 files)

FILE: Kit/lldb/include/c.h
  type leveldb_t (line 55) | typedef struct leveldb_t leveldb_t;
  type leveldb_cache_t (line 56) | typedef struct leveldb_cache_t leveldb_cache_t;
  type leveldb_comparator_t (line 57) | typedef struct leveldb_comparator_t leveldb_comparator_t;
  type leveldb_env_t (line 58) | typedef struct leveldb_env_t leveldb_env_t;
  type leveldb_filelock_t (line 59) | typedef struct leveldb_filelock_t leveldb_filelock_t;
  type leveldb_filterpolicy_t (line 60) | typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t;
  type leveldb_iterator_t (line 61) | typedef struct leveldb_iterator_t leveldb_iterator_t;
  type leveldb_logger_t (line 62) | typedef struct leveldb_logger_t leveldb_logger_t;
  type leveldb_options_t (line 63) | typedef struct leveldb_options_t leveldb_options_t;
  type leveldb_randomfile_t (line 64) | typedef struct leveldb_randomfile_t leveldb_randomfile_t;
  type leveldb_readoptions_t (line 65) | typedef struct leveldb_readoptions_t leveldb_readoptions_t;
  type leveldb_seqfile_t (line 66) | typedef struct leveldb_seqfile_t leveldb_seqfile_t;
  type leveldb_snapshot_t (line 67) | typedef struct leveldb_snapshot_t leveldb_snapshot_t;
  type leveldb_writablefile_t (line 68) | typedef struct leveldb_writablefile_t leveldb_writablefile_t;
  type leveldb_writebatch_t (line 69) | typedef struct leveldb_writebatch_t leveldb_writebatch_t;
  type leveldb_writeoptions_t (line 70) | typedef struct leveldb_writeoptions_t leveldb_writeoptions_t;

FILE: Kit/lldb/include/cache.h
  function namespace (line 26) | namespace leveldb {

FILE: Kit/lldb/include/comparator.h
  function namespace (line 12) | namespace leveldb {

FILE: Kit/lldb/include/db.h
  function namespace (line 15) | namespace leveldb {

FILE: Kit/lldb/include/dumpfile.h
  function namespace (line 14) | namespace leveldb {

FILE: Kit/lldb/include/env.h
  function namespace (line 42) | namespace leveldb {

FILE: Kit/lldb/include/filter_policy.h
  function namespace (line 23) | namespace leveldb {

FILE: Kit/lldb/include/iterator.h
  type CleanupNode (line 86) | struct CleanupNode {

FILE: Kit/lldb/include/options.h
  function namespace (line 12) | namespace leveldb {

FILE: Kit/lldb/include/slice.h
  function namespace (line 25) | namespace leveldb {
  function compare (line 100) | inline int Slice::compare(const Slice& b) const {

FILE: Kit/lldb/include/status.h
  function namespace (line 22) | namespace leveldb {

FILE: Kit/lldb/include/table.h
  function namespace (line 13) | namespace leveldb {

FILE: Kit/lldb/include/table_builder.h
  function namespace (line 22) | namespace leveldb {

FILE: Kit/lldb/include/write_batch.h
  function namespace (line 29) | namespace leveldb {

FILE: Kit/scripts/SMJobBlessUtil.py
  class UsageException (line 62) | class UsageException (Exception):
  class CheckException (line 69) | class CheckException (Exception):
    method __init__ (line 74) | def __init__(self, message, path=None):
  function checkCodeSignature (line 78) | def checkCodeSignature(programPath, programType):
  function readDesignatedRequirement (line 100) | def readDesignatedRequirement(programPath, programType):
  function readInfoPlistFromPath (line 120) | def readInfoPlistFromPath(infoPath):
  function readPlistFromToolSection (line 131) | def readPlistFromToolSection(toolPath, segmentName, sectionName):
  function checkStep1 (line 196) | def checkStep1(appPath):
  function checkStep2 (line 230) | def checkStep2(appPath, toolPathList):
  function checkStep3 (line 272) | def checkStep3(appPath, toolPathList):
  function checkStep4 (line 304) | def checkStep4(appPath, toolPathList):
  function checkStep5 (line 317) | def checkStep5(appPath):
  function check (line 321) | def check(appPath):
  function setreq (line 336) | def setreq(appPath, appInfoPlistPath, toolInfoPlistPaths):
  function main (line 421) | def main():

FILE: Kit/scripts/changelog.py
  function last_release_tag (line 5) | def last_release_tag():
  function git_hash (line 11) | def git_hash(tag):
  class Changelog (line 17) | class Changelog:
    method generate (line 22) | def generate(self):
    method commits (line 43) | def commits(self, first_commit):

FILE: Kit/scripts/i18n.py
  function dictionary (line 14) | def dictionary(lines):
  class i18n (line 28) | class i18n:
    method __init__ (line 31) | def __init__(self):
    method en_file (line 36) | def en_file(self):
    method check (line 43) | def check(self):
    method fix (line 63) | def fix(self):
    method _normalize_lang_code (line 84) | def _normalize_lang_code(self, code):
    method _extract_translation (line 90) | def _extract_translation(self, raw, fallback):
    method _lang_name_from_code (line 149) | def _lang_name_from_code(self, code):
    method _script_hint (line 164) | def _script_hint(self, lang_code):
    method _ollama_translate (line 181) | def _ollama_translate(self, text, target_lang, model="translategemma:4...
    method _line_authors (line 211) | def _line_authors(self, file_path):
    method _my_git_author (line 220) | def _my_git_author(self):
    method _strings_escape (line 230) | def _strings_escape(self, value):
    method translate (line 235) | def translate(self, model="translategemma:4b", accept=False):

FILE: Modules/CPU/bridge.h
  type IOReportSubscriptionRef (line 17) | struct IOReportSubscriptionRef

FILE: Modules/Disk/header.h
  type nvme_smart_log (line 15) | struct nvme_smart_log {
  type IONVMeSMARTInterface (line 39) | typedef struct IONVMeSMARTInterface {

FILE: Modules/Sensors/bridge.h
  type __IOHIDEvent (line 17) | struct __IOHIDEvent
  type __IOHIDServiceClient (line 18) | struct __IOHIDServiceClient
  type IOReportSubscriptionRef (line 19) | struct IOReportSubscriptionRef
  type IOHIDFloat (line 21) | typedef double IOHIDFloat;
  type IOHIDFloat (line 23) | typedef float IOHIDFloat;
Condensed preview — 283 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,771K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 122,
    "preview": "github: [exelban]\npatreon: exelban\nko_fi: exelban\ncustom: [\"https://www.paypal.com/donate?hosted_button_id=3DS5JHDBATMTC"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 289,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
  },
  {
    "path": ".github/workflows/build.yaml",
    "chars": 380,
    "preview": "name: build\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n\nconcurrency:\n  group"
  },
  {
    "path": ".github/workflows/i18n.yaml",
    "chars": 357,
    "preview": "name: i18n check\n\non:\n  push:\n    paths:\n      - '.github/workflows/i18n.yaml'\n      - '**/*.strings'\n  pull_request:\n  "
  },
  {
    "path": ".github/workflows/linter.yaml",
    "chars": 378,
    "preview": "name: Linter\n\non:\n  push:\n    paths:\n      - '.github/workflows/linter.yaml'\n      - '.swiftlint.yml'\n      - '**/*.swif"
  },
  {
    "path": ".gitignore",
    "chars": 129,
    "preview": ".DS_Store\n\nPods\nCarthage\nbuild\nxcuserdata\n\nStats.dmg\nStats.app\ncreate-dmg\ndSYMs.zip\nStats.dmg.zip\nSMC/smc\n\nCartfile.reso"
  },
  {
    "path": ".swiftlint.yml",
    "chars": 1108,
    "preview": "disabled_rules:\n  - force_cast # todo\n  - type_name # todo\n  - cyclomatic_complexity # todo\n  - trailing_whitespace\n  - "
  },
  {
    "path": "Kit/Supporting Files/Assets.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Kit/Supporting Files/Assets.xcassets/calendar.imageset/Contents.json",
    "chars": 532,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_calendar_month_black_24pt_1x.png\",\n      \"idiom\" : \"universal\",\n    "
  },
  {
    "path": "Kit/Supporting Files/Assets.xcassets/cancel.imageset/Contents.json",
    "chars": 502,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"outline_close_white_12pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" "
  },
  {
    "path": "Kit/Supporting Files/Assets.xcassets/chart.imageset/Contents.json",
    "chars": 553,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_insert_chart_outlined_white_24pt_1x.png\",\n      \"idiom\" : \"universal"
  },
  {
    "path": "Kit/Supporting Files/Assets.xcassets/close.imageset/Contents.json",
    "chars": 508,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_cancel_white_24pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"scale"
  },
  {
    "path": "Kit/Supporting Files/Assets.xcassets/refresh.imageset/Contents.json",
    "chars": 508,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"outline_refresh_black_18pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"scale"
  },
  {
    "path": "Kit/Supporting Files/Assets.xcassets/settings.imageset/Contents.json",
    "chars": 514,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_settings_black_24pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"sca"
  },
  {
    "path": "Kit/Supporting Files/Assets.xcassets/tune.imageset/Contents.json",
    "chars": 499,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"outline_tune_black_18pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" :"
  },
  {
    "path": "Kit/Supporting Files/Info.plist",
    "chars": 864,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Kit/Supporting Files/Kit.h",
    "chars": 412,
    "preview": "//\n//  Kit.h\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 05/02/2024\n//  Using Swift 5.0\n//  Running on macOS 14.3\n//\n"
  },
  {
    "path": "Kit/Widgets/BarChart.swift",
    "chars": 12279,
    "preview": "//\n//  BarChart.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 26/04/2020.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Kit/Widgets/Battery.swift",
    "chars": 27095,
    "preview": "//\n//  Battery.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 06/06/2020.\n//  Using Swift 5.0.\n//  Running on macO"
  },
  {
    "path": "Kit/Widgets/Label.swift",
    "chars": 1864,
    "preview": "//\n//  Label.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 30/03/2021.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Kit/Widgets/LineChart.swift",
    "chars": 14874,
    "preview": "//\n//  Chart.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 18/04/2020.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Kit/Widgets/Memory.swift",
    "chars": 7472,
    "preview": "//\n//  Memory.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 30/06/2020.\n//  Using Swift 5.0.\n//  Running on macOS"
  },
  {
    "path": "Kit/Widgets/Mini.swift",
    "chars": 9142,
    "preview": "//\n//  Mini.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 10/04/2020.\n//  Using Swift 5.0.\n//  Running on macOS 1"
  },
  {
    "path": "Kit/Widgets/NetworkChart.swift",
    "chars": 16567,
    "preview": "//\n//  NetworkChart.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 19/01/2021.\n//  Using Swift 5.0.\n//  Running on"
  },
  {
    "path": "Kit/Widgets/PieChart.swift",
    "chars": 4979,
    "preview": "//\n//  PieChart.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 30/11/2020.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Kit/Widgets/Speed.swift",
    "chars": 29094,
    "preview": "//\n//  Speed.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 24/05/2020.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Kit/Widgets/Stack.swift",
    "chars": 17296,
    "preview": "//\n//  Sensors.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 17/06/2020.\n//  Using Swift 5.0.\n//  Running on macO"
  },
  {
    "path": "Kit/Widgets/State.swift",
    "chars": 3927,
    "preview": "//\n//  State.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 18/09/2022.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Kit/Widgets/Tachometer.swift",
    "chars": 4192,
    "preview": "//\n//  Tachometer.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 11/10/2021.\n//  Using Swift 5.0.\n//  Running on m"
  },
  {
    "path": "Kit/Widgets/Text.swift",
    "chars": 3512,
    "preview": "//\n//  Text.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 08/09/2024\n//  Using Swift 5.0\n//  Running on macOS 14."
  },
  {
    "path": "Kit/constants.swift",
    "chars": 2002,
    "preview": "//\n//  constants.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 15/04/2020.\n//  Using Swift 5.0.\n//  Running on ma"
  },
  {
    "path": "Kit/extensions.swift",
    "chars": 25790,
    "preview": "//\n//  extensions.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 10/04/2020.\n//  Using Swift 5.0.\n//  Running on m"
  },
  {
    "path": "Kit/helpers.swift",
    "chars": 64390,
    "preview": "//\n//  helpers.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 29/09/2020.\n//  Using Swift 5.0.\n//  Running on macO"
  },
  {
    "path": "Kit/lldb/LICENSE.txt",
    "chars": 1484,
    "preview": "Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or"
  },
  {
    "path": "Kit/lldb/include/c.h",
    "chars": 12037,
    "preview": "/* Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n  Use of this source code is governed by a BSD-style lic"
  },
  {
    "path": "Kit/lldb/include/cache.h",
    "chars": 3993,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/comparator.h",
    "chars": 2417,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/db.h",
    "chars": 6779,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/dumpfile.h",
    "chars": 927,
    "preview": "// Copyright (c) 2014 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/env.h",
    "chars": 16024,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/export.h",
    "chars": 911,
    "preview": "// Copyright (c) 2017 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/filter_policy.h",
    "chars": 3013,
    "preview": "// Copyright (c) 2012 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/iterator.h",
    "chars": 3977,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/options.h",
    "chars": 7515,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/slice.h",
    "chars": 3245,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/status.h",
    "chars": 3945,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/table.h",
    "chars": 2998,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/table_builder.h",
    "chars": 3417,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/include/write_batch.h",
    "chars": 2519,
    "preview": "// Copyright (c) 2011 The LevelDB Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style li"
  },
  {
    "path": "Kit/lldb/lldb.h",
    "chars": 588,
    "preview": "//\n//  lldb.h\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 03/02/2024\n//  Using Swift 5.0\n//  Running on macOS 14.3\n//"
  },
  {
    "path": "Kit/lldb/lldb.m",
    "chars": 3977,
    "preview": "//\n//  lldb.m\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 03/02/2024\n//  Using Swift 5.0\n//  Running on macOS 14.3\n//"
  },
  {
    "path": "Kit/module/module.swift",
    "chars": 13858,
    "preview": "//\n//  module.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 09/04/2020.\n//  Copyright © 2020 Serhiy Mytrovtsiy. A"
  },
  {
    "path": "Kit/module/notifications.swift",
    "chars": 3122,
    "preview": "//\n//  notifications.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 04/12/2023\n//  Using Swift 5.0\n//  Running on "
  },
  {
    "path": "Kit/module/popup.swift",
    "chars": 16302,
    "preview": "//\n//  popup.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 11/04/2020.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Kit/module/portal.swift",
    "chars": 3506,
    "preview": "//\n//  portal.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 17/02/2023\n//  Using Swift 5.0\n//  Running on macOS 1"
  },
  {
    "path": "Kit/module/reader.swift",
    "chars": 7867,
    "preview": "//\n//  reader.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 10/04/2020.\n//  Using Swift 5.0.\n//  Running on macOS"
  },
  {
    "path": "Kit/module/widget.swift",
    "chars": 21541,
    "preview": "//\n//  widget.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 10/04/2020.\n//  Using Swift 5.0.\n//  Running on macOS"
  },
  {
    "path": "Kit/module/window.swift",
    "chars": 25910,
    "preview": "//\n//  settings.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 13/04/2020.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Kit/plugins/Charts.swift",
    "chars": 40474,
    "preview": "//\n//  Chart.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 17/04/2020.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Kit/plugins/DB.swift",
    "chars": 3680,
    "preview": "//\n//  DB.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 03/02/2024\n//  Using Swift 5.0\n//  Running on macOS 14.3\n"
  },
  {
    "path": "Kit/plugins/Logger.swift",
    "chars": 5447,
    "preview": "//\n//  Logger.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 24/06/2021.\n//  Using Swift 5.0.\n//  Running on macOS"
  },
  {
    "path": "Kit/plugins/Reachability.swift",
    "chars": 3927,
    "preview": "//\n//  Reachability.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 15/10/2021.\n//  Using Swift 5.0.\n//  Running on"
  },
  {
    "path": "Kit/plugins/Remote.swift",
    "chars": 40697,
    "preview": "//\n//  Remote.swift\n//  Stats\n//\n//  Created by Serhiy Mytrovtsiy on 16/03/2025\n//  Using Swift 6.0\n//  Running on macOS"
  },
  {
    "path": "Kit/plugins/Repeater.swift",
    "chars": 1689,
    "preview": "//\n//  Repeater.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 27/06/2022.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Kit/plugins/Store.swift",
    "chars": 4147,
    "preview": "//\n//  store.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 10/04/2020.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Kit/plugins/SystemKit.swift",
    "chars": 44182,
    "preview": "//\n//  SystemKit.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 13/04/2020.\n//  Using Swift 5.0.\n//  Running on ma"
  },
  {
    "path": "Kit/plugins/Updater.swift",
    "chars": 10138,
    "preview": "//\n//  Updater.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 14/04/2020.\n//  Using Swift 5.0.\n//  Running on macO"
  },
  {
    "path": "Kit/process.swift",
    "chars": 9633,
    "preview": "//\n//  process.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 05/01/2024\n//  Using Swift 5.0\n//  Running on macOS "
  },
  {
    "path": "Kit/scripts/SMJobBlessUtil.py",
    "chars": 19400,
    "preview": "#! /usr/bin/python3\n#\n#   File:       SMJobBlessUtil.py\n#\n#   Contains:   Tool for checking and correcting apps that use"
  },
  {
    "path": "Kit/scripts/changelog.py",
    "chars": 2018,
    "preview": "import subprocess\nimport re\n\n\ndef last_release_tag():\n    cmd = \"git describe --abbrev=0 --tags\"\n    output = subprocess"
  },
  {
    "path": "Kit/scripts/i18n.py",
    "chars": 13188,
    "preview": "import os\nimport sys\nimport json\nimport urllib.request\nimport subprocess\nimport unicodedata\n\ntry:\n    import langcodes\ne"
  },
  {
    "path": "Kit/scripts/uninstall.sh",
    "chars": 271,
    "preview": "#! /bin/sh\n\nsudo launchctl unload /Library/LaunchDaemons/eu.exelban.Stats.SMC.Helper.plist\nsudo rm /Library/LaunchDaemon"
  },
  {
    "path": "Kit/scripts/updater.sh",
    "chars": 1000,
    "preview": "#!/bin/bash\n\nDMG_PATH=\"$HOME/Downloads/Stats.dmg\"\nMOUNT_PATH=\"/tmp/Stats\"\nAPPLICATION_PATH=\"/Applications/\"\n\nSTEP=\"\"\n\nwh"
  },
  {
    "path": "Kit/types.swift",
    "chars": 18858,
    "preview": "//\n//  types.swift\n//  Kit\n//\n//  Created by Serhiy Mytrovtsiy on 10/04/2021.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "LICENSE",
    "chars": 1074,
    "preview": "MIT License\n\nCopyright (c) 2019 Serhiy Mytrovtsiy\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "LaunchAtLogin/Info.plist",
    "chars": 1025,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "LaunchAtLogin/LaunchAtLogin.entitlements",
    "chars": 240,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "LaunchAtLogin/main.swift",
    "chars": 782,
    "preview": "//\n//  main.swift\n//  LaunchAtLogin\n//\n//  Created by Serhiy Mytrovtsiy on 08/04/2020.\n//  Copyright © 2020 Serhiy Mytro"
  },
  {
    "path": "Makefile",
    "chars": 3719,
    "preview": "APP = Stats\nBUNDLE_ID = eu.exelban.$(APP)\n\nBUILD_PATH = $(PWD)/build\nAPP_PATH = \"$(BUILD_PATH)/$(APP).app\"\nZIP_PATH = \"$"
  },
  {
    "path": "Modules/Battery/Info.plist",
    "chars": 864,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Battery/config.plist",
    "chars": 1662,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Battery/main.swift",
    "chars": 4577,
    "preview": "//\n//  main.swift\n//  Battery\n//\n//  Created by Serhiy Mytrovtsiy on 06/06/2020.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Modules/Battery/notifications.swift",
    "chars": 4105,
    "preview": "//\n//  notifications.swift\n//  Battery\n//\n//  Created by Serhiy Mytrovtsiy on 17/12/2023\n//  Using Swift 5.0\n//  Running"
  },
  {
    "path": "Modules/Battery/popup.swift",
    "chars": 19216,
    "preview": "//\n//  popup.swift\n//  Battery\n//\n//  Created by Serhiy Mytrovtsiy on 06/06/2020.\n//  Using Swift 5.0.\n//  Running on ma"
  },
  {
    "path": "Modules/Battery/portal.swift",
    "chars": 4314,
    "preview": "//\n//  portal.swift\n//  Battery\n//\n//  Created by Serhiy Mytrovtsiy on 16/03/2023\n//  Using Swift 5.0\n//  Running on mac"
  },
  {
    "path": "Modules/Battery/readers.swift",
    "chars": 8731,
    "preview": "//\n//  readers.swift\n//  Battery\n//\n//  Created by Serhiy Mytrovtsiy on 06/06/2020.\n//  Using Swift 5.0.\n//  Running on "
  },
  {
    "path": "Modules/Battery/settings.swift",
    "chars": 2629,
    "preview": "//\n//  settings.swift\n//  Battery\n//\n//  Created by Serhiy Mytrovtsiy on 15/07/2020.\n//  Using Swift 5.0.\n//  Running on"
  },
  {
    "path": "Modules/Bluetooth/Info.plist",
    "chars": 864,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Bluetooth/config.plist",
    "chars": 909,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Bluetooth/main.swift",
    "chars": 4423,
    "preview": "//\n//  main.swift\n//  Bluetooth\n//\n//  Created by Serhiy Mytrovtsiy on 08/06/2021.\n//  Using Swift 5.0.\n//  Running on m"
  },
  {
    "path": "Modules/Bluetooth/notifications.swift",
    "chars": 2962,
    "preview": "//\n//  notifications.swift\n//  Bluetooth\n//\n//  Created by Serhiy Mytrovtsiy on 24/06/2024\n//  Using Swift 5.0\n//  Runni"
  },
  {
    "path": "Modules/Bluetooth/popup.swift",
    "chars": 5184,
    "preview": "//\n//  popup.swift\n//  Bluetooth\n//\n//  Created by Serhiy Mytrovtsiy on 22/06/2021.\n//  Using Swift 5.0.\n//  Running on "
  },
  {
    "path": "Modules/Bluetooth/readers.swift",
    "chars": 19738,
    "preview": "//\n//  readers.swift\n//  Bluetooth\n//\n//  Created by Serhiy Mytrovtsiy on 08/06/2021.\n//  Using Swift 5.0.\n//  Running o"
  },
  {
    "path": "Modules/Bluetooth/settings.swift",
    "chars": 2516,
    "preview": "//\n//  settings.swift\n//  Bluetooth\n//\n//  Created by Serhiy Mytrovtsiy on 07/07/2021.\n//  Using Swift 5.0.\n//  Running "
  },
  {
    "path": "Modules/CPU/Info.plist",
    "chars": 912,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/CPU/bridge.h",
    "chars": 1316,
    "preview": "//\n//  bridge.h\n//  Stats\n//\n//  Created by Serhiy Mytrovtsiy on 17/12/2024\n//  Using Swift 6.0\n//  Running on macOS 15."
  },
  {
    "path": "Modules/CPU/config.plist",
    "chars": 1744,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/CPU/main.swift",
    "chars": 9097,
    "preview": "//\n//  main.swift\n//  CPU\n//\n//  Created by Serhiy Mytrovtsiy on 09/04/2020.\n//  Copyright © 2020 Serhiy Mytrovtsiy. All"
  },
  {
    "path": "Modules/CPU/notifications.swift",
    "chars": 10618,
    "preview": "//\n//  notifications.swift\n//  CPU\n//\n//  Created by Serhiy Mytrovtsiy on 04/12/2023\n//  Using Swift 5.0\n//  Running on "
  },
  {
    "path": "Modules/CPU/popup.swift",
    "chars": 31782,
    "preview": "//\n//  popup.swift\n//  CPU\n//\n//  Created by Serhiy Mytrovtsiy on 15/04/2020.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Modules/CPU/portal.swift",
    "chars": 7945,
    "preview": "//\n//  portal.swift\n//  CPU\n//\n//  Created by Serhiy Mytrovtsiy on 17/02/2023\n//  Using Swift 5.0\n//  Running on macOS 1"
  },
  {
    "path": "Modules/CPU/readers.swift",
    "chars": 23354,
    "preview": "//\n//  readers.swift\n//  CPU\n//\n//  Created by Serhiy Mytrovtsiy on 10/04/2020.\n//  Using Swift 5.0.\n//  Running on macO"
  },
  {
    "path": "Modules/CPU/settings.swift",
    "chars": 8848,
    "preview": "//\n//  Settings.swift\n//  CPU\n//\n//  Created by Serhiy Mytrovtsiy on 18/04/2020.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Modules/CPU/widget.swift",
    "chars": 5201,
    "preview": "//\n//  widget.swift\n//  CPU\n//\n//  Created by Serhiy Mytrovtsiy on 01/07/2024\n//  Using Swift 5.0\n//  Running on macOS 1"
  },
  {
    "path": "Modules/Clock/config.plist",
    "chars": 827,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Clock/main.swift",
    "chars": 5711,
    "preview": "//\n//  main.swift\n//  Clock\n//\n//  Created by Serhiy Mytrovtsiy on 23/03/2023\n//  Using Swift 5.0\n//  Running on macOS 1"
  },
  {
    "path": "Modules/Clock/popup.swift",
    "chars": 32663,
    "preview": "//\n//  popup.swift\n//  Clock\n//\n//  Created by Serhiy Mytrovtsiy on 24/03/2023\n//  Using Swift 5.0\n//  Running on macOS "
  },
  {
    "path": "Modules/Clock/portal.swift",
    "chars": 3184,
    "preview": "//\n//  portal.swift\n//  Clock\n//\n//  Created by Serhiy Mytrovtsiy on 28/12/2023\n//  Using Swift 5.0\n//  Running on macOS"
  },
  {
    "path": "Modules/Clock/reader.swift",
    "chars": 4492,
    "preview": "//\n//  reader.swift\n//  Stats\n//\n//  Created by Serhiy Mytrovtsiy on 05/03/2026\n//  Using Swift 6.0\n//  Running on macOS"
  },
  {
    "path": "Modules/Clock/settings.swift",
    "chars": 11292,
    "preview": "//\n//  settings.swift\n//  Clock\n//\n//  Created by Serhiy Mytrovtsiy on 24/03/2023\n//  Using Swift 5.0\n//  Running on mac"
  },
  {
    "path": "Modules/Disk/Info.plist",
    "chars": 864,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Disk/config.plist",
    "chars": 2752,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Disk/header.h",
    "chars": 1127,
    "preview": "//\n//  Header.h\n//  Disk\n//\n//  Created by Serhiy Mytrovtsiy on 25/03/2023\n//  Using Swift 5.0\n//  Running on macOS 13.2"
  },
  {
    "path": "Modules/Disk/main.swift",
    "chars": 12413,
    "preview": "//\n//  main.swift\n//  Disk\n//\n//  Created by Serhiy Mytrovtsiy on 07/05/2020.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Modules/Disk/notifications.swift",
    "chars": 2614,
    "preview": "//\n//  notifications.swift\n//  Disk\n//\n//  Created by Serhiy Mytrovtsiy on 05/12/2023\n//  Using Swift 5.0\n//  Running on"
  },
  {
    "path": "Modules/Disk/popup.swift",
    "chars": 35308,
    "preview": "//\n//  popup.swift\n//  Disk\n//\n//  Created by Serhiy Mytrovtsiy on 11/05/2020.\n//  Using Swift 5.0.\n//  Running on macOS"
  },
  {
    "path": "Modules/Disk/portal.swift",
    "chars": 4519,
    "preview": "//\n//  portal.swift\n//  Disk\n//\n//  Created by Serhiy Mytrovtsiy on 20/02/2023\n//  Using Swift 5.0\n//  Running on macOS "
  },
  {
    "path": "Modules/Disk/readers.swift",
    "chars": 21325,
    "preview": "//\n//  readers.swift\n//  Disk\n//\n//  Created by Serhiy Mytrovtsiy on 07/05/2020.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Modules/Disk/settings.swift",
    "chars": 8738,
    "preview": "//\n//  settings.swift\n//  Disk\n//\n//  Created by Serhiy Mytrovtsiy on 12/05/2020.\n//  Using Swift 5.0.\n//  Running on ma"
  },
  {
    "path": "Modules/Disk/widget.swift",
    "chars": 4642,
    "preview": "//\n//  widget.swift\n//  Disk\n//\n//  Created by Serhiy Mytrovtsiy on 16/07/2024\n//  Using Swift 5.0\n//  Running on macOS "
  },
  {
    "path": "Modules/GPU/Info.plist",
    "chars": 864,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/GPU/config.plist",
    "chars": 1581,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/GPU/main.swift",
    "chars": 6695,
    "preview": "//\n//  main.swift\n//  GPU\n//\n//  Created by Serhiy Mytrovtsiy on 17/08/2020.\n//  Using Swift 5.0.\n//  Running on macOS 1"
  },
  {
    "path": "Modules/GPU/notifications.swift",
    "chars": 2487,
    "preview": "//\n//  notifications.swift\n//  GPU\n//\n//  Created by Serhiy Mytrovtsiy on 05/12/2023\n//  Using Swift 5.0\n//  Running on "
  },
  {
    "path": "Modules/GPU/popup.swift",
    "chars": 18376,
    "preview": "//\n//  popup.swift\n//  GPU\n//\n//  Created by Serhiy Mytrovtsiy on 17/08/2020.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Modules/GPU/portal.swift",
    "chars": 3288,
    "preview": "//\n//  portal.swift\n//  GPU\n//\n//  Created by Serhiy Mytrovtsiy on 18/02/2023\n//  Using Swift 5.0\n//  Running on macOS 1"
  },
  {
    "path": "Modules/GPU/reader.swift",
    "chars": 7853,
    "preview": "//\n//  reader.swift\n//  GPU\n//\n//  Created by Serhiy Mytrovtsiy on 17/08/2020.\n//  Using Swift 5.0.\n//  Running on macOS"
  },
  {
    "path": "Modules/GPU/settings.swift",
    "chars": 4705,
    "preview": "//\n//  settings.swift\n//  GPU\n//\n//  Created by Serhiy Mytrovtsiy on 17/08/2020.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Modules/GPU/widget.swift",
    "chars": 4738,
    "preview": "//\n//  widget.swift\n//  GPU\n//\n//  Created by Serhiy Mytrovtsiy on 17/07/2024\n//  Using Swift 5.0\n//  Running on macOS 1"
  },
  {
    "path": "Modules/Net/Info.plist",
    "chars": 864,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Net/config.plist",
    "chars": 1636,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Net/main.swift",
    "chars": 15920,
    "preview": "//\n//  main.swift\n//  Net\n//\n//  Created by Serhiy Mytrovtsiy on 24/05/2020.\n//  Using Swift 5.0.\n//  Running on macOS 1"
  },
  {
    "path": "Modules/Net/notifications.swift",
    "chars": 9426,
    "preview": "//\n//  notifications.swift\n//  Net\n//\n//  Created by Serhiy Mytrovtsiy on 25/01/2025\n//  Using Swift 6.0\n//  Running on "
  },
  {
    "path": "Modules/Net/popup.swift",
    "chars": 45104,
    "preview": "//\n//  popup.swift\n//  Net\n//\n//  Created by Serhiy Mytrovtsiy on 24/05/2020.\n//  Using Swift 5.0.\n//  Running on macOS "
  },
  {
    "path": "Modules/Net/portal.swift",
    "chars": 6040,
    "preview": "//\n//  portal.swift\n//  Net\n//\n//  Created by Serhiy Mytrovtsiy on 18/02/2023\n//  Using Swift 5.0\n//  Running on macOS 1"
  },
  {
    "path": "Modules/Net/readers.swift",
    "chars": 40371,
    "preview": "//\n//  readers.swift\n//  Net\n//\n//  Created by Serhiy Mytrovtsiy on 24/05/2020.\n//  Using Swift 5.0.\n//  Running on macO"
  },
  {
    "path": "Modules/Net/settings.swift",
    "chars": 20438,
    "preview": "//\n//  settings.swift\n//  Net\n//\n//  Created by Serhiy Mytrovtsiy on 06/07/2020.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Modules/Net/widget.swift",
    "chars": 5558,
    "preview": "//\n//  widget.swift\n//  Net\n//\n//  Created by Serhiy Mytrovtsiy on 30/07/2024\n//  Using Swift 5.0\n//  Running on macOS 1"
  },
  {
    "path": "Modules/RAM/Info.plist",
    "chars": 864,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/RAM/config.plist",
    "chars": 2019,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/RAM/main.swift",
    "chars": 10088,
    "preview": "//\n//  main.swift\n//  Memory\n//\n//  Created by Serhiy Mytrovtsiy on 12/04/2020.\n//  Using Swift 5.0.\n//  Running on macO"
  },
  {
    "path": "Modules/RAM/notifications.swift",
    "chars": 9385,
    "preview": "//\n//  notifications.swift\n//  RAM\n//\n//  Created by Serhiy Mytrovtsiy on 05/12/2023\n//  Using Swift 5.0\n//  Running on "
  },
  {
    "path": "Modules/RAM/popup.swift",
    "chars": 24920,
    "preview": "//\n//  popup.swift\n//  Memory\n//\n//  Created by Serhiy Mytrovtsiy on 18/04/2020.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Modules/RAM/portal.swift",
    "chars": 5537,
    "preview": "//\n//  portal.swift\n//  RAM\n//\n//  Created by Serhiy Mytrovtsiy on 17/02/2023\n//  Using Swift 5.0\n//  Running on macOS 1"
  },
  {
    "path": "Modules/RAM/readers.swift",
    "chars": 10259,
    "preview": "//\n//  readers.swift\n//  Memory\n//\n//  Created by Serhiy Mytrovtsiy on 12/04/2020.\n//  Using Swift 5.0.\n//  Running on m"
  },
  {
    "path": "Modules/RAM/settings.swift",
    "chars": 8494,
    "preview": "//\n//  settings.swift\n//  Memory\n//\n//  Created by Serhiy Mytrovtsiy on 11/07/2020.\n//  Using Swift 5.0.\n//  Running on "
  },
  {
    "path": "Modules/RAM/widget.swift",
    "chars": 5379,
    "preview": "//\n//  widget.swift\n//  RAM\n//\n//  Created by Serhiy Mytrovtsiy on 03/07/2024\n//  Using Swift 5.0\n//  Running on macOS 1"
  },
  {
    "path": "Modules/Sensors/Info.plist",
    "chars": 864,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Sensors/bridge.h",
    "chars": 2319,
    "preview": "//\n//  bridge.h\n//  Stats\n//\n//  Created by Serhiy Mytrovtsiy on 30/03/2021.\n//  Using Swift 5.0.\n//  Running on macOS 1"
  },
  {
    "path": "Modules/Sensors/config.plist",
    "chars": 1586,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Modules/Sensors/main.swift",
    "chars": 6233,
    "preview": "//\n//  main.swift\n//  Sensors\n//\n//  Created by Serhiy Mytrovtsiy on 17/06/2020.\n//  Using Swift 5.0.\n//  Running on mac"
  },
  {
    "path": "Modules/Sensors/notifications.swift",
    "chars": 3539,
    "preview": "//\n//  notifications.swift\n//  Sensors\n//\n//  Created by Serhiy Mytrovtsiy on 05/12/2023\n//  Using Swift 5.0\n//  Running"
  },
  {
    "path": "Modules/Sensors/popup.swift",
    "chars": 45836,
    "preview": "//\n//  popup.swift\n//  Sensors\n//\n//  Created by Serhiy Mytrovtsiy on 22/06/2020.\n//  Using Swift 5.0.\n//  Running on ma"
  },
  {
    "path": "Modules/Sensors/portal.swift",
    "chars": 3157,
    "preview": "//\n//  portal.swift\n//  Sensors\n//\n//  Created by Serhiy Mytrovtsiy on 14/01/2024\n//  Using Swift 5.0\n//  Running on mac"
  },
  {
    "path": "Modules/Sensors/reader.m",
    "chars": 1484,
    "preview": "//\n//  reader.m\n//  Sensors\n//\n//  Created by Serhiy Mytrovtsiy on 06/05/2021.\n//  Using Swift 5.0.\n//  Running on macOS"
  },
  {
    "path": "Modules/Sensors/readers.swift",
    "chars": 25000,
    "preview": "//\n//  readers.swift\n//  Sensors\n//\n//  Created by Serhiy Mytrovtsiy on 17/06/2020.\n//  Using Swift 5.0.\n//  Running on "
  },
  {
    "path": "Modules/Sensors/settings.swift",
    "chars": 8908,
    "preview": "//\n//  settings.swift\n//  Sensors\n//\n//  Created by Serhiy Mytrovtsiy on 23/06/2020.\n//  Using Swift 5.0.\n//  Running on"
  },
  {
    "path": "Modules/Sensors/values.swift",
    "chars": 33490,
    "preview": "//\n//  values.swift\n//  Sensors\n//\n//  Created by Serhiy Mytrovtsiy on 17/06/2020.\n//  Using Swift 5.0.\n//  Running on m"
  },
  {
    "path": "README.md",
    "chars": 7583,
    "preview": "# Stats\n\n<a href=\"https://github.com/exelban/stats/releases\"><p align=\"center\"><img src=\"https://github.com/exelban/stat"
  },
  {
    "path": "SMC/Helper/Info.plist",
    "chars": 889,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SMC/Helper/Launchd.plist",
    "chars": 345,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SMC/Helper/main.swift",
    "chars": 10843,
    "preview": "//\n//  main.swift\n//  Helper\n//\n//  Created by Serhiy Mytrovtsiy on 17/11/2022\n//  Using Swift 5.0\n//  Running on macOS "
  },
  {
    "path": "SMC/Helper/protocol.swift",
    "chars": 696,
    "preview": "//\n//  protocol.swift\n//  Helper\n//\n//  Created by Serhiy Mytrovtsiy on 17/11/2022\n//  Using Swift 5.0\n//  Running on ma"
  },
  {
    "path": "SMC/Makefile",
    "chars": 337,
    "preview": ".PHONY: build\n.SILENT: build\n\nbuild:\n\trm -rf ./build\n\n\txcodebuild \\\n\t\t-project ../Stats.xcodeproj \\\n  \t\t-scheme SMC \\\n  "
  },
  {
    "path": "SMC/main.swift",
    "chars": 5200,
    "preview": "//\n//  main.swift\n//  SMC\n//\n//  Created by Serhiy Mytrovtsiy on 25/05/2021.\n//  Using Swift 5.0.\n//  Running on macOS 1"
  },
  {
    "path": "SMC/smc.swift",
    "chars": 25580,
    "preview": "//\n//  smc.swift\n//  SMC\n//\n//  Created by Serhiy Mytrovtsiy on 25/05/2021.\n//  Using Swift 5.0.\n//  Running on macOS 10"
  },
  {
    "path": "Stats/AppDelegate.swift",
    "chars": 4211,
    "preview": "//\n//  AppDelegate.swift\n//  Stats\n//\n//  Created by Serhiy Mytrovtsiy on 28.05.2019.\n//  Copyright © 2019 Serhiy Mytrov"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1299,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon_16x16.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/ac_unit.imageset/Contents.json",
    "chars": 508,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"outline_ac_unit_black_18pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"scale"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/apps.imageset/Contents.json",
    "chars": 502,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_apps_white_24pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/bug.imageset/Contents.json",
    "chars": 520,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_bug_report_white_24pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"s"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/imac.imageset/Contents.json",
    "chars": 302,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"imac.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n   "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/imacPro.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"imacPro.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macMini.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macMini.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macMini2020.imageset/Contents.json",
    "chars": 309,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macMini2020.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n  "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macMini2024.imageset/Contents.json",
    "chars": 309,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macMini2024.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n  "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macPro.imageset/Contents.json",
    "chars": 304,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macPro.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macPro2019.imageset/Contents.json",
    "chars": 310,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"mac pro 2019.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macStudio.imageset/Contents.json",
    "chars": 307,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macStudio.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macbookAir.imageset/Contents.json",
    "chars": 308,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macbookAir.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n   "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macbookAir4thGen.imageset/Contents.json",
    "chars": 308,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macbookAir.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n   "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macbookNeo.imageset/Contents.json",
    "chars": 308,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macbookNeo.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n   "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macbookPro.imageset/Contents.json",
    "chars": 308,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macbookPro.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n   "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/devices/macbookPro5thGen.imageset/Contents.json",
    "chars": 308,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macbookPro.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n   "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/donate.imageset/Contents.json",
    "chars": 448,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"donate@1x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/high-battery.imageset/Contents.json",
    "chars": 310,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"high-battery.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/low-battery.imageset/Contents.json",
    "chars": 309,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"low-battery.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n  "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/pause.imageset/Contents.json",
    "chars": 502,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"outline_pause_white_24pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/power.imageset/Contents.json",
    "chars": 544,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_power_settings_new_white_24pt_1x.png\",\n      \"idiom\" : \"universal\",\n"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/record.imageset/Contents.json",
    "chars": 550,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_radio_button_checked_black_20pt_1x.png\",\n      \"idiom\" : \"universal\""
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/resume.imageset/Contents.json",
    "chars": 520,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_play_arrow_white_24pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"s"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/settings.imageset/Contents.json",
    "chars": 514,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_settings_white_24pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \"sca"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/stop.imageset/Contents.json",
    "chars": 523,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"baseline_stop_circle_black_20pt_1x.png\",\n      \"idiom\" : \"universal\",\n      \""
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/support/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/support/github.imageset/Contents.json",
    "chars": 373,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"github.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/support/ko-fi.imageset/Contents.json",
    "chars": 303,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"ko-fi.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n  "
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/support/patreon.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"patreon.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n"
  },
  {
    "path": "Stats/Supporting Files/Assets.xcassets/support/paypal.imageset/Contents.json",
    "chars": 304,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"paypal.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n "
  }
]

// ... and 83 more files (download for full content)

About this extraction

This page contains the full source code of the exelban/stats GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 283 files (17.7 MB), approximately 660.7k tokens, and a symbol index with 73 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!