Repository: Abdenasser/neohtop
Branch: main
Commit: dc22a9f475b9
Files: 95
Total size: 276.9 KB
Directory structure:
gitextract_7hzxi6z1/
├── .github/
│ ├── CODEOWNERS
│ ├── CONTRIBUTING.md
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── build-check.yml
│ ├── format-check.yml
│ ├── linux-aarch64-nightly.yml
│ ├── linux-x86_64-nightly.yml
│ ├── macos-nightly.yml
│ ├── test-release.yml
│ └── windows-nightly.yml
├── .gitignore
├── .husky/
│ └── pre-commit
├── .prettierrc
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── LICENSE
├── README.md
├── docs/
│ ├── index.html
│ ├── main.js
│ └── styles.css
├── jsconfig.json
├── package.json
├── src/
│ ├── App.svelte
│ ├── app.css
│ ├── app.html
│ ├── lib/
│ │ ├── components/
│ │ │ ├── AppInfo.svelte
│ │ │ ├── ThemeSwitcher.svelte
│ │ │ ├── TitleBar.svelte
│ │ │ ├── index.ts
│ │ │ ├── modals/
│ │ │ │ ├── KillProcessModal.svelte
│ │ │ │ ├── Modal.svelte
│ │ │ │ ├── ProcessDetailsModal.svelte
│ │ │ │ └── index.ts
│ │ │ ├── process/
│ │ │ │ ├── ActionButtons.svelte
│ │ │ │ ├── ProcessIcon.svelte
│ │ │ │ ├── ProcessRow.svelte
│ │ │ │ ├── ProcessTable.svelte
│ │ │ │ ├── TableHeader.svelte
│ │ │ │ └── index.ts
│ │ │ ├── stats/
│ │ │ │ ├── CpuPanel.svelte
│ │ │ │ ├── MemoryPanel.svelte
│ │ │ │ ├── NetworkPanel.svelte
│ │ │ │ ├── PanelHeader.svelte
│ │ │ │ ├── ProgressBar.svelte
│ │ │ │ ├── StatItem.svelte
│ │ │ │ ├── StatPanel.svelte
│ │ │ │ ├── StatsBar.svelte
│ │ │ │ ├── StoragePanel.svelte
│ │ │ │ ├── SystemPanel.svelte
│ │ │ │ └── index.ts
│ │ │ └── toolbar/
│ │ │ ├── ColumnToggle.svelte
│ │ │ ├── FilterToggle.svelte
│ │ │ ├── PaginationControls.svelte
│ │ │ ├── RefreshControls.svelte
│ │ │ ├── SearchBox.svelte
│ │ │ ├── StatusFilter.svelte
│ │ │ ├── ToolBar.svelte
│ │ │ └── index.ts
│ │ ├── constants/
│ │ │ └── index.ts
│ │ ├── definitions/
│ │ │ ├── columns.ts
│ │ │ ├── index.ts
│ │ │ ├── settings.ts
│ │ │ └── themes.ts
│ │ ├── stores/
│ │ │ ├── index.ts
│ │ │ ├── overlay.ts
│ │ │ ├── processes.ts
│ │ │ ├── settings.ts
│ │ │ └── theme.ts
│ │ ├── types/
│ │ │ └── index.ts
│ │ └── utils/
│ │ └── index.ts
│ └── routes/
│ ├── +layout.js
│ ├── +layout.svelte
│ └── +page.svelte
├── src-tauri/
│ ├── .cargo/
│ │ └── config.toml
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── build.rs
│ ├── capabilities/
│ │ └── default.json
│ ├── icons/
│ │ └── icon.icns
│ ├── src/
│ │ ├── commands.rs
│ │ ├── main.rs
│ │ ├── monitoring/
│ │ │ ├── mod.rs
│ │ │ ├── process_monitor.rs
│ │ │ ├── system_monitor.rs
│ │ │ └── types.rs
│ │ ├── state.rs
│ │ └── ui/
│ │ ├── mod.rs
│ │ └── window.rs
│ └── tauri.conf.json
├── svelte.config.js
└── vite.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/CODEOWNERS
================================================
* @Abdenasser
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing to NeoHtop
Thank you for considering contributing to NeoHtop! We welcome contributions from the community.
## How to Contribute
1. Fork the repository.
2. Create a new branch (`git checkout -b feature/YourFeature`).
3. Make your changes.
4. Commit your changes (`git commit -m 'Add some feature'`).
5. Push to the branch (`git push origin feature/YourFeature`).
6. Open a pull request.
## Code of Conduct
Please note that this project is released with a [Contributor Code of Conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/). By participating in this project you agree to abide by its terms.
## Reporting Bugs
Please use the [bug report template](./ISSUE_TEMPLATE/bug_report.md) to report any bugs you find.
## Requesting Features
Please use the [feature request template](./ISSUE_TEMPLATE/feature_request.md) to suggest new features.
================================================
FILE: .github/FUNDING.yml
================================================
github: abdenasser
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. macOS]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Support
url: https://github.com/Abdenasser/neohtop/discussions
about: Please use discussions for questions and support.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/pull_request_template.md
================================================
## Description
Please include a summary of the changes and the related issue. Please also include relevant motivation and context.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration.
- [ ] Test A
- [ ] Test B
## Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules
================================================
FILE: .github/workflows/build-check.yml
================================================
name: Build Check
on:
pull_request:
branches: [main]
paths:
- "src-tauri/**"
- ".github/workflows/**"
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 1
CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10
RUST_BACKTRACE: 1
RUSTC_WRAPPER: sccache
CARGO_BUILD_JOBS: 2
jobs:
build:
name: Build Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
cache: "npm"
- name: Cache Linux Dependencies
id: cache-apt
uses: actions/cache@v3
with:
path: |
/var/cache/apt/archives/*.deb
/var/lib/apt/lists/*
key: ${{ runner.os }}-apt-${{ hashFiles('**/package.json', '**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-apt-
- name: Add Ubuntu Jammy repo for WebKitGTK 4.0
run: |
echo "deb http://archive.ubuntu.com/ubuntu jammy main universe" | sudo tee -a /etc/apt/sources.list
sudo apt update
- name: Install Linux Dependencies
run: |
sudo rm -rf /var/cache/apt/archives/lock
sudo rm -rf /var/cache/apt/archives/partial
sudo rm -rf /var/lib/apt/lists/lock
sudo rm -rf /var/lib/apt/lists/partial
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
build-essential \
pkg-config \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
libglib2.0-dev \
libjavascriptcoregtk-4.0-dev \
libsoup-3.0-dev \
libwebkit2gtk-4.1-dev
- name: Remove Jammy repo
run: |
sudo sed -i '/jammy main universe/d' /etc/apt/sources.list
sudo apt update
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: cargo
target: x86_64-unknown-linux-gnu
- name: Install sccache
run: |
SCCACHE_VERSION=v0.7.7
curl -L "https://github.com/mozilla/sccache/releases/download/${SCCACHE_VERSION}/sccache-${SCCACHE_VERSION}-x86_64-unknown-linux-musl.tar.gz" | tar xz
sudo mv sccache-*/sccache /usr/local/bin/sccache
echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
- uses: Swatinem/rust-cache@v2
with:
workspaces: "./src-tauri -> target"
shared-key: "build"
- name: Install Dependencies
run: npm ci
- name: Build Application
run: |
npm run tauri build -- \
--target x86_64-unknown-linux-gnu \
--bundles deb \
--ci
================================================
FILE: .github/workflows/format-check.yml
================================================
name: Format Check
on:
pull_request:
branches: [ main ]
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Install dependencies
run: npm ci
- name: Check formatting
run: npm run format:check
================================================
FILE: .github/workflows/linux-aarch64-nightly.yml
================================================
name: Linux (aarch64) Nightly Build
on:
workflow_dispatch:
inputs:
release_upload_url:
description: "Release upload URL"
required: true
env:
CARGO_TERM_COLOR: always
PKG_CONFIG_ALLOW_CROSS: 1
PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
PKG_CONFIG: /usr/bin/aarch64-linux-gnu-pkg-config
jobs:
build:
name: Build Linux aarch64 Packages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
cache: "npm"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-unknown-linux-gnu
- name: Configure ARM64 repositories
run: |
sudo dpkg --add-architecture arm64
# Remove all existing sources
sudo rm -rf /etc/apt/sources.list.d/*
sudo truncate -s 0 /etc/apt/sources.list
# Add only ports.ubuntu.com repository
sudo tee /etc/apt/sources.list << EOF
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy main restricted universe multiverse
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted universe multiverse
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main restricted universe multiverse
deb [arch=amd64] http://azure.archive.ubuntu.com/ubuntu jammy main restricted universe multiverse
deb [arch=amd64] http://azure.archive.ubuntu.com/ubuntu jammy-updates main restricted universe multiverse
deb [arch=amd64] http://azure.archive.ubuntu.com/ubuntu jammy-security main restricted universe multiverse
EOF
sudo apt-get update
# Install required packages including cross-compilation tools
sudo apt-get install -y \
build-essential \
pkg-config \
crossbuild-essential-arm64 \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu \
libgtk-3-dev:arm64 \
libayatana-appindicator3-dev:arm64 \
librsvg2-dev:arm64 \
libglib2.0-dev:arm64 \
libjavascriptcoregtk-4.0-dev:arm64 \
libsoup-3.0-dev:arm64 \
libwebkit2gtk-4.1-dev:arm64 \
libssl-dev:arm64 \
libssl-dev \
openssl:arm64
# Configure pkg-config for cross-compilation
echo "PKG_CONFIG=/usr/bin/aarch64-linux-gnu-pkg-config" >> $GITHUB_ENV
echo "PKG_CONFIG_ALLOW_CROSS=1" >> $GITHUB_ENV
- name: Install Dependencies
run: npm install
- name: Setup cross-compilation environment
run: |
sudo apt-get install -y \
crossbuild-essential-arm64 \
pkg-config \
libssl-dev:arm64 \
libssl-dev \
openssl:arm64 \
file \
desktop-file-utils \
libfuse2 \
qemu-user-static
# Setup pkg-config
sudo tee /usr/bin/aarch64-linux-gnu-pkg-config << 'EOF'
#!/bin/sh
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
exec pkg-config "$@"
EOF
sudo chmod +x /usr/bin/aarch64-linux-gnu-pkg-config
# Create .cargo/config
mkdir -p .cargo
cat > .cargo/config << EOF
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
ar = "aarch64-linux-gnu-ar"
EOF
# Download and setup appimagetool for ARM64
wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-aarch64.AppImage
chmod +x appimagetool-aarch64.AppImage
sudo mv appimagetool-aarch64.AppImage /usr/local/bin/appimagetool
# Set environment variables
echo "PKG_CONFIG=/usr/bin/aarch64-linux-gnu-pkg-config" >> $GITHUB_ENV
echo "PKG_CONFIG_ALLOW_CROSS=1" >> $GITHUB_ENV
echo "OPENSSL_DIR=/usr" >> $GITHUB_ENV
echo "OPENSSL_INCLUDE_DIR=/usr/include/aarch64-linux-gnu" >> $GITHUB_ENV
echo "OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu" >> $GITHUB_ENV
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
echo "APPIMAGE_EXTRACT_AND_RUN=1" >> $GITHUB_ENV
- name: Build Frontend
run: npm run build
- name: Build AppImage
run: |
echo "Building AppImage for aarch64..."
npm run tauri build -- --target aarch64-unknown-linux-gnu --bundles appimage
cd src-tauri/target/aarch64-unknown-linux-gnu/release/bundle/appimage/
for f in *.AppImage; do
echo "AARCH64_APPIMAGE_PATH=src-tauri/target/aarch64-unknown-linux-gnu/release/bundle/appimage/$f" >> $GITHUB_ENV
done
- name: Build Debian Package
run: |
echo "Building Debian package for aarch64..."
npm run tauri build -- --target aarch64-unknown-linux-gnu --bundles deb
cd src-tauri/target/aarch64-unknown-linux-gnu/release/bundle/deb/
for f in *.deb; do
echo "AARCH64_DEB_PATH=src-tauri/target/aarch64-unknown-linux-gnu/release/bundle/deb/$f" >> $GITHUB_ENV
done
- name: Build RPM Package
run: |
echo "Building RPM package for aarch64..."
npm run tauri build -- --target aarch64-unknown-linux-gnu --bundles rpm
cd src-tauri/target/aarch64-unknown-linux-gnu/release/bundle/rpm/
for f in *.rpm; do
echo "AARCH64_RPM_PATH=src-tauri/target/aarch64-unknown-linux-gnu/release/bundle/rpm/$f" >> $GITHUB_ENV
done
- name: Get version from package.json
id: version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
- name: Upload AppImage to Release
if: github.event.inputs.release_upload_url != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
with:
upload_url: ${{ github.event.inputs.release_upload_url }}
asset_path: ${{ env.AARCH64_APPIMAGE_PATH }}
asset_name: NeoHtop_${{ steps.version.outputs.version }}_aarch64.AppImage
asset_content_type: application/x-executable
- name: Upload Debian Package to Release
if: github.event.inputs.release_upload_url != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
with:
upload_url: ${{ github.event.inputs.release_upload_url }}
asset_path: ${{ env.AARCH64_DEB_PATH }}
asset_name: NeoHtop_${{ steps.version.outputs.version }}_aarch64.deb
asset_content_type: application/vnd.debian.binary-package
- name: Upload RPM Package to Release
if: github.event.inputs.release_upload_url != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
with:
upload_url: ${{ github.event.inputs.release_upload_url }}
asset_path: ${{ env.AARCH64_RPM_PATH }}
asset_name: NeoHtop_${{ steps.version.outputs.version }}_aarch64.rpm
asset_content_type: application/x-rpm
================================================
FILE: .github/workflows/linux-x86_64-nightly.yml
================================================
name: Linux (x86_64) Nightly Build
on:
workflow_dispatch:
inputs:
release_upload_url:
description: "Release upload URL"
required: true
env:
CARGO_TERM_COLOR: always
jobs:
build:
name: Build Linux Packages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
cache: "npm"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Add Ubuntu Jammy repo for WebKitGTK 4.0
run: |
echo "deb http://archive.ubuntu.com/ubuntu jammy main universe" | sudo tee -a /etc/apt/sources.list
sudo apt update
- name: Install Linux Dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
pkg-config \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
libglib2.0-dev \
libjavascriptcoregtk-4.0-dev \
libsoup-3.0-dev \
libwebkit2gtk-4.1-dev
- name: Remove Jammy repo
run: |
sudo sed -i '/jammy main universe/d' /etc/apt/sources.list
sudo apt update
- name: Install Dependencies
run: |
npm install
- name: Build Frontend
run: npm run build
- name: Build AppImage (x86_64)
run: |
echo "Building AppImage for x86_64..."
npm run tauri build -- --target x86_64-unknown-linux-gnu --bundles appimage
cd src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/appimage/
for f in *.AppImage; do
echo "APPIMAGE_PATH=src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/appimage/$f" >> $GITHUB_ENV
done
- name: Build Debian Package (x86_64)
run: |
echo "Building Debian package for x86_64..."
npm run tauri build -- --target x86_64-unknown-linux-gnu --bundles deb
cd src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/deb/
for f in *.deb; do
echo "DEB_PATH=src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/deb/$f" >> $GITHUB_ENV
done
- name: Build RPM Package (x86_64)
run: |
echo "Building RPM package for x86_64..."
npm run tauri build -- --target x86_64-unknown-linux-gnu --bundles rpm
cd src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/rpm/
for f in *.rpm; do
echo "RPM_PATH=src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/rpm/$f" >> $GITHUB_ENV
done
- name: Get version from package.json
id: version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
- name: Upload AppImage to Release
if: github.event.inputs.release_upload_url != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
with:
upload_url: ${{ github.event.inputs.release_upload_url }}
asset_path: ${{ env.APPIMAGE_PATH }}
asset_name: NeoHtop_${{ steps.version.outputs.version }}_x86_64.AppImage
asset_content_type: application/x-executable
- name: Upload Debian Package to Release
if: github.event.inputs.release_upload_url != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
with:
upload_url: ${{ github.event.inputs.release_upload_url }}
asset_path: ${{ env.DEB_PATH }}
asset_name: NeoHtop_${{ steps.version.outputs.version }}_x86_64.deb
asset_content_type: application/vnd.debian.binary-package
- name: Upload RPM Package to Release
if: github.event.inputs.release_upload_url != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
with:
upload_url: ${{ github.event.inputs.release_upload_url }}
asset_path: ${{ env.RPM_PATH }}
asset_name: NeoHtop_${{ steps.version.outputs.version }}_x86_64.rpm
asset_content_type: application/x-rpm
================================================
FILE: .github/workflows/macos-nightly.yml
================================================
name: MacOS (Intel/Apple Silicon) Nightly Build
on:
workflow_dispatch:
inputs:
release_upload_url:
description: 'Release upload URL'
required: true
env:
CARGO_TERM_COLOR: always
jobs:
build:
name: Build MacOS Apps
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
cache: 'npm'
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install Dependencies
run: |
rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-darwin
npm install
- name: Set up keychain
run: |
security create-keychain -p "" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "" build.keychain
echo "$MACOS_CERTIFICATE" | base64 --decode > /tmp/certificate.p12
security import /tmp/certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
- name: Build Frontend
run: npm run build
- name: Build for Intel Mac
run: |
echo "Building for Intel Mac..."
npm run tauri build -- --target x86_64-apple-darwin --bundles dmg --config "{\"bundle\":{\"macOS\":{\"signingIdentity\": \"Developer ID Application: Abdenasser Elidrissi (785JV74B9Y)\"}}}"
# Rename the Intel build and store the filename
cd src-tauri/target/x86_64-apple-darwin/release/bundle/dmg/
for f in *.dmg; do
mv "$f" "intel-$f"
echo "INTEL_DMG_PATH=src-tauri/target/x86_64-apple-darwin/release/bundle/dmg/intel-$f" >> $GITHUB_ENV
done
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- name: Build for Apple Silicon
run: |
echo "Building for aarch64..."
npm run tauri build -- --target aarch64-apple-darwin --bundles dmg --config "{\"bundle\":{\"macOS\":{\"signingIdentity\": \"Developer ID Application: Abdenasser Elidrissi (785JV74B9Y)\"}}}"
# Rename the Apple Silicon build and store the filename
cd src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/
for f in *.dmg; do
mv "$f" "silicon-$f"
echo "SILICON_DMG_PATH=src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/silicon-$f" >> $GITHUB_ENV
done
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- name: Get version from package.json
id: version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
- name: Upload Intel Build to Release
if: github.event.inputs.release_upload_url != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
with:
upload_url: ${{ github.event.inputs.release_upload_url }}
asset_path: ${{ env.INTEL_DMG_PATH }}
asset_name: intel-NeoHtop_${{ steps.version.outputs.version }}_x64.dmg
asset_content_type: application/x-apple-diskimage
- name: Upload Silicon Build to Release
if: github.event.inputs.release_upload_url != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
with:
upload_url: ${{ github.event.inputs.release_upload_url }}
asset_path: ${{ env.SILICON_DMG_PATH }}
asset_name: silicon-NeoHtop_${{ steps.version.outputs.version }}_aarch64.dmg
asset_content_type: application/x-apple-diskimage
================================================
FILE: .github/workflows/test-release.yml
================================================
name: Test Release Build
on:
workflow_dispatch: # Manual trigger
jobs:
create-draft:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get version from package.json
id: version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
- name: Create Draft Release
id: create_release
uses: softprops/action-gh-release@v1
with:
name: "NeoHtop v${{ steps.version.outputs.version }}"
tag_name: "v${{ steps.version.outputs.version }}"
draft: true
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
- name: Trigger MacOS Build
uses: benc-uk/workflow-dispatch@v1
with:
workflow: macos-nightly.yml
token: ${{ secrets.PAT_TOKEN }}
inputs: '{"release_upload_url": "${{ steps.create_release.outputs.upload_url }}"}'
- name: Trigger Windows Build
uses: benc-uk/workflow-dispatch@v1
with:
workflow: windows-nightly.yml
token: ${{ secrets.PAT_TOKEN }}
inputs: '{"release_upload_url": "${{ steps.create_release.outputs.upload_url }}"}'
- name: Trigger Linux x86_64 Build
uses: benc-uk/workflow-dispatch@v1
with:
workflow: linux-x86_64-nightly.yml
token: ${{ secrets.PAT_TOKEN }}
inputs: '{"release_upload_url": "${{ steps.create_release.outputs.upload_url }}"}'
================================================
FILE: .github/workflows/windows-nightly.yml
================================================
name: Windows (x86_64) Nightly Build
on:
workflow_dispatch:
inputs:
release_upload_url:
description: 'Release upload URL'
required: true
env:
CARGO_TERM_COLOR: always
jobs:
build:
name: Build Windows Executable
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
cache: 'npm'
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install WebView2
run: |
$WebView2InstallPath = "$env:TEMP\MicrosoftEdgeWebview2Setup.exe"
Invoke-WebRequest "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile $WebView2InstallPath
Start-Process -FilePath $WebView2InstallPath -Args "/silent /install" -Wait
- name: Install Dependencies
run: |
npm install
- name: Build Frontend
run: npm run build
- name: Build Windows Executable
shell: bash # Force using bash shell for consistent environment variable setting
run: |
echo "Building Windows executable..."
npm run tauri build
echo "WIN_EXE_PATH=src-tauri/target/release/NeoHtop.exe" >> $GITHUB_ENV
- name: Get version from package.json
id: version
shell: bash # Force using bash shell
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
- name: Upload to Release
if: github.event.inputs.release_upload_url != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
with:
upload_url: ${{ github.event.inputs.release_upload_url }}
asset_path: ${{ env.WIN_EXE_PATH }}
asset_name: NeoHtop_${{ steps.version.outputs.version }}_x64.exe
asset_content_type: application/vnd.microsoft.portable-executable
================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
## Jetbrains
.idea/
.run/
================================================
FILE: .husky/pre-commit
================================================
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm exec lint-staged
================================================
FILE: .prettierrc
================================================
{
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"svelte.svelte-vscode",
"tauri-apps.tauri-vscode",
"rust-lang.rust-analyzer"
]
}
================================================
FILE: .vscode/settings.json
================================================
{
"svelte.enable-ts-plugin": true
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 Abdenasser
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
NeoHtop
A modern, cross-platform system monitor built on top of Svelte, Rust, and Tauri.
[](https://github.com/Abdenasser/neohtop/blob/main/LICENSE)
[](https://github.com/Abdenasser/neohtop/stargazers)
[](https://github.com/Abdenasser/neohtop/issues)
[](https://github.com/Abdenasser/neohtop/releases)
[](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution)
If you find this project helpful, consider buying me a coffee:
Or sponsor me on GitHub:
## Table of Contents
- [Why NeoHtop?](#why-neohtop)
- [Features](#features)
- [Tech Stack](#tech-stack)
- [Getting Started](#getting-started)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Running with Sudo](#running-with-sudo)
- [Development](#development)
- [Setup](#setup)
- [Code Formatting](#code-formatting)
- [Pull Requests](#pull-requests)
- [Contributing](#contributing)
- [License](#license)
## Why NeoHtop?
[Read about the back story and motivation behind NeoHtop](https://www.abdenasser.com/2024/11/06/oh-boy-neohtop/)
## Features
- 🚀 Real-time process monitoring
- 💻 CPU and Memory usage tracking
- 🎨 Beautiful, modern UI with dark/light themes
- 🔍 Advanced process search and filtering
- 📌 Pin important processes
- 🛠 Process management (kill processes)
- 🎯 Sort by any column
- 🔄 Auto-refresh system stats
### Search Functionality
Search for processes by name, command, or PID. Use commas to search for multiple terms simultaneously. Regular expressions are supported for advanced filtering.
Examples:
- `arm, x86`: Returns processes with "arm" or "x86" in the name or command
- `d$`: Lists daemons (processes ending with 'd')
- `^(\w+\.)+\w+$`: Shows processes with reverse domain name notation (e.g., com.docker.vmnetd)
## Tech Stack
- **Frontend**: SvelteKit, TypeScript
- **Backend**: Rust, Tauri
- **Styling**: CSS Variables for theming
- **Icons**: FontAwesome
## Getting Started
### Prerequisites
- Node.js (v16 or later)
- Rust (latest stable)
- Xcode Command Line Tools (for macOS)
### Installation
#### Manual
Download the latest release from the [releases page](https://github.com/Abdenasser/neohtop/releases).
#### Package Managers
Members of the community have kindly published unofficial packages for various platforms and package managers.
Please note, these packages are community-maintained and not officially released, reviewed, or endorsed by NeoHtop.
We only provide official builds through the GitHub Releases page.
Since these external packages are managed by third parties, we cannot guarantee their security, integrity, or update frequency.
Please use them at your own discretion.
##### macOS
Using [Homebrew](https://brew.sh/).
```bash
brew install --cask neohtop
```
##### Arch Linux (AUR)
Using the [AUR](https://aur.archlinux.org/) and [an AUR helper](https://wiki.archlinux.org/title/AUR_helpers).
```bash
yay -S neohtop
```
or
```bash
paru -S neohtop
```
##### Fedora Linux
Install the [Terra repository](https://terra.fyralabs.com/).
```bash
dnf install neohtop
```
##### Windows
Install the [Scoop repository](https://scoop.sh/), then make sure you have the Scoop extras bucket added:
```bash
scoop bucket add extras
```
Then install with:
```bash
scoop install extras/neohtop
```
##### Solus
```bash
eopkg install neohtop
```
### Running with Sudo
Some processes require monitoring with sudo privileges. To monitor these processes, launch NeoHtop with sudo:
- macOS: `sudo /Applications/NeoHtop.app/Contents/MacOS/NeoHtop`
- Linux: `pkexec /path/to/neohtop` (recommended)
## Development
### Setup
```bash
# Install dependencies
npm install
# Run in development mode
npm run tauri dev
# Build for production
npm run tauri build
```
### Code Formatting
We use Prettier for web code and `cargo fmt` for Rust code.
```bash
# Format all files
npm run format
# Check formatting without making changes
npm run format:check
```
### Pull Requests
Before submitting a PR, ensure:
1. All code is formatted (`npm run format`)
2. The format check passes (`npm run format:check`)
3. Your commits follow the project's commit message conventions
## Contributing
We welcome contributions! Please see our [contributing guidelines](./.github/CONTRIBUTING.md) for more information.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
================================================
FILE: docs/index.html
================================================
NeoHtop - Blazing-fast system monitoring for your desktop (built with Rust, Tauri & Svelte)
Monitor Your System With Style
A beautiful, lightning-fast cross-platform system monitor.
Choose and download the appropriate version for your Mac
2
Open DMG
Double-click the downloaded .dmg file
3
Install
Drag NeoHtop to your Applications folder
4
First Launch
Right-click and choose Open to bypass Gatekeeper
What People Are Saying
Neohtop is absolutely beautiful to look at. It's a modern system monitor inspired by
htop, providing real-time insights into CPU, memory, disk, and network usage. A work of art! pic.twitter.com/TAzCIEb8uF
🚀 Introducing NeoHtop, the ultimate process monitoring tool for all your system needs!
🖥️ Say goodbye to clunky, outdated monitors and hello to a modern, native solution with a clean and
user-friendly interface. 🤩 With NeoHtop, you can easily keep track of your system's… pic.twitter.com/qq8cXI8Qq1
NeoHtop offers a modern interface with additional features like process pinning, smart
search, and themes while maintaining high performance through its Rust backend. It's designed to be more
user-friendly and efficient than traditional system monitors.https://t.co/c4niSeqw0ppic.twitter.com/hnqrfSiSFG
NeoHtop offers a modern interface with additional features like process pinning, smart search, and themes
while maintaining high performance through its Rust backend. It's designed to be more user-friendly and
efficient than traditional system monitors.
No, NeoHtop is built with Rust and optimized for minimal resource usage, typically using less than 1% CPU
and minimal memory. It's designed to be lightweight while monitoring your system.
NeoHtop supports macOS 10.15 (Catalina) and newer versions. It's optimized for both Intel and Apple
Silicon Macs, with native support for both architectures.
Yes, NeoHtop offers various customization options including:
Dark/Light theme switching
Customizable columns and metrics
Adjustable refresh rates
Process grouping options
Yes, NeoHtop is completely open source and available on GitHub. You can contribute to the project, report
issues, or suggest new features through our GitHub repository.
================================================
FILE: src/lib/components/index.ts
================================================
export * from "./toolbar";
export * from "./process";
export * from "./stats";
export * from "./modals";
export { default as AppInfo } from "./AppInfo.svelte";
export { default as TitleBar } from "./TitleBar.svelte";
export { default as ThemeSwitcher } from "./ThemeSwitcher.svelte";
================================================
FILE: src/lib/components/modals/KillProcessModal.svelte
================================================
{#if process}
{#each columns.filter((col) => col.visible) as column}
onToggleSort(column.id)}>
{column.label}
{getSortIndicator(column.id)}
{/each}
Actions
================================================
FILE: src/lib/components/process/index.ts
================================================
export { default as ProcessTable } from "./ProcessTable.svelte";
export { default as ProcessRow } from "./ProcessRow.svelte";
export { default as TableHeader } from "./TableHeader.svelte";
export { default as ActionButtons } from "./ActionButtons.svelte";
export { default as ProcessIcon } from "./ProcessIcon.svelte";
================================================
FILE: src/lib/components/stats/CpuPanel.svelte
================================================
================================================
FILE: src/lib/components/stats/index.ts
================================================
export { default as StatsBar } from "./StatsBar.svelte";
export { default as CpuPanel } from "./CpuPanel.svelte";
export { default as MemoryPanel } from "./MemoryPanel.svelte";
export { default as StoragePanel } from "./StoragePanel.svelte";
export { default as SystemPanel } from "./SystemPanel.svelte";
export { default as NetworkPanel } from "./NetworkPanel.svelte";
export { default as PanelHeader } from "./PanelHeader.svelte";
export { default as ProgressBar } from "./ProgressBar.svelte";
export { default as StatItem } from "./StatItem.svelte";
================================================
FILE: src/lib/components/toolbar/ColumnToggle.svelte
================================================
{/if}
================================================
FILE: src-tauri/.cargo/config.toml
================================================
[target.x86_64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
================================================
FILE: src-tauri/.gitignore
================================================
# Generated by Cargo
# will have compiled files and executables
/target/
# Generated by Tauri
# will have schema files for capabilities auto-completion
/gen/schemas
================================================
FILE: src-tauri/Cargo.toml
================================================
[package]
name = "neohtop"
version = "1.2.0"
description = "A cross-platform system monitor"
authors = ["you"]
edition = "2021"
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "2.1.1", features = ["macos-private-api"] }
sysinfo = { version = "0.35.2", features = ["disk", "multithread", "network", "system"] }
tauri-plugin-shell = "2"
tauri-plugin-os = "2"
window-vibrancy = "0.5.2"
[features]
default = [ "custom-protocol" ]
custom-protocol = [ "tauri/custom-protocol" ]
[profile.release]
panic = "abort" # Strip expensive panic clean-up logic
codegen-units = 1 # Compile crates one after another so the compiler can optimize better
lto = "fat" # More aggressive link-time optimization
opt-level = 3 # Optimize for maximum performance
strip = true # Remove debug symbols
incremental = false # Disable incremental compilation
================================================
FILE: src-tauri/build.rs
================================================
fn main() {
tauri_build::build()
}
================================================
FILE: src-tauri/capabilities/default.json
================================================
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": [
"main"
],
"permissions": [
"core:default",
"core:app:allow-version",
"core:window:allow-start-dragging",
"core:window:allow-maximize",
"core:window:allow-minimize",
"core:window:allow-close",
"shell:default"
]
}
================================================
FILE: src-tauri/src/commands.rs
================================================
//! Tauri command handlers
//!
//! This module contains the command handlers that are exposed to the frontend
//! through Tauri's IPC mechanism. These commands provide the interface between
//! the frontend and the system monitoring functionality.
use crate::monitoring::{ProcessInfo, ProcessMonitor, SystemStats};
use crate::state::AppState;
use tauri::State;
/// Retrieves the current list of processes and system statistics
///
/// # Arguments
///
/// * `state` - The application state containing system monitoring components
///
/// # Returns
///
/// A tuple containing:
/// * A vector of process information
/// * Current system statistics
///
/// # Errors
///
/// Returns an error string if:
/// * Failed to acquire locks on system state
/// * Failed to collect process information
#[tauri::command]
pub async fn get_processes(
state: State<'_, AppState>,
) -> Result<(Vec, SystemStats), String> {
let mut sys = state.sys.lock().map_err(|e| e.to_string())?;
let mut disks = state.disks.lock().map_err(|e| e.to_string())?;
let mut networks = state.networks.lock().map_err(|e| e.to_string())?;
sys.refresh_all();
disks.refresh(true);
networks.refresh(true);
let mut process_monitor = state.process_monitor.lock().map_err(|e| e.to_string())?;
let mut system_monitor = state.system_monitor.lock().map_err(|e| e.to_string())?;
let processes = process_monitor.collect_processes(&sys)?;
let system_stats = system_monitor.collect_stats(&sys, &networks, &disks);
Ok((processes, system_stats))
}
/// Attempts to kill a process with the specified PID
///
/// # Arguments
///
/// * `pid` - Process ID to kill
/// * `state` - The application state
///
/// # Returns
///
/// * `true` if the process was successfully killed
/// * `false` if the process couldn't be killed or wasn't found
///
/// # Errors
///
/// Returns an error string if failed to acquire lock on system state
#[tauri::command]
pub async fn kill_process(pid: u32, state: State<'_, AppState>) -> Result {
let sys = state.sys.lock().map_err(|e| e.to_string())?;
Ok(ProcessMonitor::kill_process(&sys, pid))
}
================================================
FILE: src-tauri/src/main.rs
================================================
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
//! NeoHtop - A modern system monitor built with Tauri
//!
//! This is the main entry point for the application. It sets up the Tauri
//! application, initializes plugins, and configures window effects.
mod commands;
mod monitoring;
mod state;
mod ui;
use state::AppState;
use tauri::Manager;
/// Main entry point for the application
///
/// # Panics
///
/// Will panic if:
/// - Unable to create the main window
/// - Failed to apply window effects
/// - Failed to initialize the application state
fn main() {
#[cfg(target_os = "linux")]
if std::env::var("XDG_SESSION_TYPE").unwrap_or_default() == "wayland" {
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
}
tauri::Builder::default()
.setup(|app| {
let window = app.get_webview_window("main").unwrap();
ui::setup_window_effects(&window).expect("Failed to apply window effects");
Ok(())
})
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_os::init())
.manage(AppState::new())
.invoke_handler(tauri::generate_handler![
commands::get_processes,
commands::kill_process,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
================================================
FILE: src-tauri/src/monitoring/mod.rs
================================================
//! System monitoring functionality
//!
//! This module provides types and functionality for monitoring system resources
//! and processes. It includes process monitoring, system statistics collection,
//! and data structures for representing system state.
mod process_monitor;
mod system_monitor;
mod types;
pub use process_monitor::ProcessMonitor;
pub use system_monitor::SystemMonitor;
pub use types::*; // Re-export all types
================================================
FILE: src-tauri/src/monitoring/process_monitor.rs
================================================
//! Process monitoring functionality
//!
//! This module handles monitoring and managing system processes, including
//! collecting process information and managing process lifecycle.
use super::{ProcessData, ProcessInfo, ProcessStaticInfo};
use std::collections::HashMap;
use std::ffi::OsString;
use std::fmt::Debug;
use std::time::{SystemTime, UNIX_EPOCH};
use sysinfo::ProcessStatus;
fn os_string_vec_to_string_vec(v: &[OsString]) -> Vec {
v.iter()
.map(|c| c.to_string_lossy().into_owned())
.collect::>()
}
/// Monitors and manages system processes
#[derive(Debug)]
pub struct ProcessMonitor {
/// Cache for static process information to avoid redundant allocations
process_cache: HashMap,
}
impl ProcessMonitor {
/// Creates a new process monitor instance
pub fn new() -> Self {
Self {
process_cache: HashMap::new(),
}
}
/// Collects information about all running processes
///
/// # Arguments
///
/// * `sys` - System information provider
///
/// # Returns
///
/// A vector of process information, or an error string if collection failed
pub fn collect_processes(&mut self, sys: &sysinfo::System) -> Result, String> {
let current_time = Self::get_current_time()?;
let processes_data = self.collect_process_data(sys, current_time);
Ok(self.build_process_info(processes_data))
}
/// Attempts to kill a process
///
/// # Arguments
///
/// * `sys` - System information provider
/// * `pid` - Process ID to kill
///
/// # Returns
///
/// Boolean indicating whether the process was successfully killed
pub fn kill_process(sys: &sysinfo::System, pid: u32) -> bool {
sys.process(sysinfo::Pid::from(pid as usize))
.map(|process| process.kill())
.unwrap_or(false)
}
/// Gets the current system time in seconds since UNIX epoch
fn get_current_time() -> Result {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.map_err(|e| format!("Failed to get system time: {}", e))
}
/// Collects raw process data from the system
fn collect_process_data(&self, sys: &sysinfo::System, current_time: u64) -> Vec {
sys.processes()
.iter()
.map(|(pid, process)| {
let start_time = process.start_time();
ProcessData {
pid: pid.as_u32(),
name: process.name().to_string_lossy().into_owned(),
cmd: os_string_vec_to_string_vec(&process.cmd()),
user_id: process.user_id().map(|uid| uid.to_string()),
cpu_usage: process.cpu_usage(),
memory: process.memory(),
status: process.status(),
ppid: process.parent().map(|p| p.as_u32()),
environ: os_string_vec_to_string_vec(&process.environ()),
root: process
.root()
.map(|p| p.to_string_lossy().into_owned())
.unwrap_or_default(),
virtual_memory: process.virtual_memory(),
start_time,
run_time: if start_time > 0 {
current_time.saturating_sub(start_time)
} else {
0
},
disk_usage: process.disk_usage(),
session_id: process.session_id().map(|id| id.as_u32()),
}
})
.collect()
}
/// Builds process information from raw process data
fn build_process_info(&mut self, processes: Vec) -> Vec {
processes
.into_iter()
.map(|data| {
let cached_info =
self.process_cache
.entry(data.pid)
.or_insert_with(|| ProcessStaticInfo {
name: data.name.clone(),
command: data.cmd.join(" "),
user: data.user_id.unwrap_or_else(|| "-".to_string()),
});
ProcessInfo {
pid: data.pid,
ppid: data.ppid.unwrap_or(0),
name: cached_info.name.clone(),
cpu_usage: data.cpu_usage,
memory_usage: data.memory,
status: Self::format_status(data.status),
user: cached_info.user.clone(),
command: cached_info.command.clone(),
threads: None,
environ: data.environ,
root: data.root,
virtual_memory: data.virtual_memory,
start_time: data.start_time,
run_time: data.run_time,
disk_usage: (data.disk_usage.read_bytes, data.disk_usage.written_bytes),
session_id: data.session_id,
}
})
.collect()
}
/// Formats process status into a human-readable string
pub fn format_status(status: ProcessStatus) -> String {
match status {
ProcessStatus::Run => "Running",
ProcessStatus::Sleep => "Sleeping",
ProcessStatus::Idle => "Idle",
_ => "Unknown",
}
.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
use sysinfo::System;
/// Tests creation of a new process monitor
#[test]
fn test_process_monitor_creation() {
let monitor = ProcessMonitor::new();
assert!(monitor.process_cache.is_empty());
}
/// Tests process collection functionality
#[test]
fn test_process_collection() {
let mut monitor = ProcessMonitor::new();
let mut sys = System::new();
sys.refresh_all();
let result = monitor.collect_processes(&sys);
assert!(result.is_ok());
}
}
================================================
FILE: src-tauri/src/monitoring/system_monitor.rs
================================================
//! System statistics monitoring
//!
//! This module handles collection and monitoring of system-wide statistics
//! including CPU, memory, network, and disk usage.
use super::SystemStats;
use std::fmt::Debug;
use std::path::Path;
use std::time::Instant;
use sysinfo::{Disk, Disks, Networks, System};
/// Monitors system-wide statistics
#[derive(Debug)]
pub struct SystemMonitor {
/// Tracks network usage between updates
last_network_update: (Instant, u64, u64),
}
impl SystemMonitor {
/// Creates a new system monitor instance
///
/// # Arguments
///
/// * `sys` - System information provider for initial readings
pub fn new(networks: &Networks) -> Self {
let (initial_rx, initial_tx) =
networks
.iter()
.fold((0, 0), |(initial_rx, initial_tx), (_, data)| {
(
initial_rx + data.total_received(),
initial_tx + data.total_transmitted(),
)
});
Self {
last_network_update: (Instant::now(), initial_rx, initial_tx),
}
}
/// Collects current system statistics
///
/// # Arguments
///
/// * `sys` - System information provider
pub fn collect_stats(
&mut self,
sys: &System,
networks: &Networks,
disks: &Disks,
) -> SystemStats {
let (network_rx, network_tx) = self.calculate_network_stats(networks);
let (disk_total, disk_used, disk_free) = self.calculate_disk_stats(disks);
let load_average = System::load_average();
SystemStats {
cpu_usage: sys.cpus().iter().map(|cpu| cpu.cpu_usage()).collect(),
memory_total: sys.total_memory(),
memory_used: sys.used_memory(),
memory_free: sys.total_memory() - sys.used_memory(),
memory_cached: sys.total_memory()
- (sys.used_memory() + (sys.total_memory() - sys.used_memory())),
uptime: System::uptime(),
load_avg: [load_average.one, load_average.five, load_average.fifteen],
network_rx_bytes: network_rx,
network_tx_bytes: network_tx,
disk_total_bytes: disk_total,
disk_used_bytes: disk_used,
disk_free_bytes: disk_free,
}
}
/// Filters disks based on platform-specific criteria
#[cfg(not(target_os = "windows"))]
fn filter_disks(disks: &[Disk]) -> impl Iterator {
disks
.iter()
.filter(|disk| disk.mount_point() == Path::new("/"))
}
/// Windows-specific disk filtering
#[cfg(target_os = "windows")]
fn filter_disks(disks: &[Disk]) -> impl Iterator {
disks.iter()
}
/// Calculates network usage rates
fn calculate_network_stats(&mut self, networks: &Networks) -> (u64, u64) {
let (current_rx, current_tx) =
networks
.iter()
.fold((0, 0), |(current_rx, current_tx), (_, data)| {
(
current_rx + data.total_received(),
current_tx + data.total_transmitted(),
)
});
let elapsed = self.last_network_update.0.elapsed().as_secs_f64();
let rx_rate = ((current_rx - self.last_network_update.1) as f64 / elapsed) as u64;
let tx_rate = ((current_tx - self.last_network_update.2) as f64 / elapsed) as u64;
self.last_network_update = (Instant::now(), current_rx, current_tx);
(rx_rate, tx_rate)
}
/// Calculates disk usage statistics and returns `(total, used, free)`.
fn calculate_disk_stats(&self, disks: &Disks) -> (u64, u64, u64) {
Self::filter_disks(disks).fold((0, 0, 0), |(total, used, free), disk| {
(
total + disk.total_space(),
used + (disk.total_space() - disk.available_space()),
free + disk.available_space(),
)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use sysinfo::System;
/// Tests creation of system monitor
#[test]
fn test_system_monitor_creation() {
let networks = Networks::new();
let monitor = SystemMonitor::new(&networks);
assert!(monitor.last_network_update.1 >= 0);
assert!(monitor.last_network_update.2 >= 0);
}
/// Tests system statistics collection
#[test]
fn test_stats_collection() {
let mut networks = Networks::new();
let mut monitor = SystemMonitor::new(&networks);
networks.refresh(true);
let sys = System::new_all();
let disks = Disks::new_with_refreshed_list();
let stats = monitor.collect_stats(&sys, &networks, &disks);
assert!(!stats.cpu_usage.is_empty());
assert!(stats.memory_total > 0);
}
}
================================================
FILE: src-tauri/src/monitoring/types.rs
================================================
use serde::Serialize;
use std::fmt::Debug;
use sysinfo::{DiskUsage, ProcessStatus};
/// Internal representation of process data collected from the system
/// This struct is used internally and not exposed directly to the frontend
#[derive(Clone, Debug)]
pub(crate) struct ProcessData {
/// Process ID
pub pid: u32,
/// Name of the process
pub name: String,
/// Complete command line arguments
pub cmd: Vec,
/// User ID that owns the process
pub user_id: Option,
/// CPU usage as percentage (0-100)
pub cpu_usage: f32,
/// Physical memory usage in bytes
pub memory: u64,
/// Current process status
pub status: ProcessStatus,
/// Parent process ID
pub ppid: Option,
/// Environment variables
pub environ: Vec,
/// Root directory of the process
pub root: String,
/// Virtual memory usage in bytes
pub virtual_memory: u64,
/// Process start time (Unix timestamp)
pub start_time: u64,
/// Process running time in seconds
pub run_time: u64,
/// Disk I/O statistics
pub disk_usage: DiskUsage,
/// Session ID of the process
pub session_id: Option,
}
/// Static information about a process that doesn't change frequently
/// Used for caching purposes to avoid frequent updates of stable data
#[derive(Clone, Debug)]
pub struct ProcessStaticInfo {
/// Process name
pub name: String,
/// Full command string
pub command: String,
/// Username of the process owner
pub user: String,
}
/// Process information exposed to the frontend via Tauri
/// Contains formatted and filtered process data for UI consumption
#[derive(Serialize, Debug)]
pub struct ProcessInfo {
/// Process ID
pub pid: u32,
/// Parent process ID
pub ppid: u32,
/// Process name
pub name: String,
/// CPU usage as percentage (0-100)
pub cpu_usage: f32,
/// Physical memory usage in bytes
pub memory_usage: u64,
/// Process status as string
pub status: String,
/// Username of the process owner
pub user: String,
/// Full command string
pub command: String,
/// Number of threads (if available)
pub threads: Option,
/// Environment variables
pub environ: Vec,
/// Root directory of the process
pub root: String,
/// Virtual memory usage in bytes
pub virtual_memory: u64,
/// Process start time (Unix timestamp)
pub start_time: u64,
/// Process running time in seconds
pub run_time: u64,
/// Disk I/O statistics (read bytes, written bytes)
pub disk_usage: (u64, u64),
/// Session ID of the process
pub session_id: Option,
}
/// System-wide statistics exposed to the frontend
/// Provides overall system resource usage and performance metrics
#[derive(Serialize, Debug)]
pub struct SystemStats {
/// CPU usage per core as percentage (0-100)
pub cpu_usage: Vec,
/// Total physical memory in bytes
pub memory_total: u64,
/// Used physical memory in bytes
pub memory_used: u64,
/// Free physical memory in bytes
pub memory_free: u64,
/// Cached memory in bytes
pub memory_cached: u64,
/// System uptime in seconds
pub uptime: u64,
/// Load averages for 1, 5, and 15 minutes
pub load_avg: [f64; 3],
/// Total bytes received over network
pub network_rx_bytes: u64,
/// Total bytes transmitted over network
pub network_tx_bytes: u64,
/// Total disk space in bytes
pub disk_total_bytes: u64,
/// Used disk space in bytes
pub disk_used_bytes: u64,
/// Free disk space in bytes
pub disk_free_bytes: u64,
}
================================================
FILE: src-tauri/src/state.rs
================================================
//! Application state management
//!
//! This module handles the global application state, including system monitoring
//! and process tracking capabilities.
use crate::monitoring::{ProcessMonitor, SystemMonitor};
use std::sync::Mutex;
use sysinfo::{Disks, Networks, System};
/// Global application state
///
/// Maintains thread-safe access to system information and monitoring components
#[derive(Debug)]
pub struct AppState {
/// System information handler
pub sys: Mutex,
/// Networks information handler
pub networks: Mutex,
/// Networks information handler
pub disks: Mutex,
/// Process monitoring component
pub process_monitor: Mutex,
/// System statistics monitoring component
pub system_monitor: Mutex,
}
impl AppState {
/// Creates a new instance of the application state
///
/// Initializes system monitoring and process tracking components
///
/// # Returns
///
/// A new `AppState` instance with initialized monitors
pub fn new() -> Self {
let sys = System::new_all();
let disks = Disks::new_with_refreshed_list();
let networks = Networks::new_with_refreshed_list();
Self {
process_monitor: Mutex::new(ProcessMonitor::new()),
system_monitor: Mutex::new(SystemMonitor::new(&networks)),
sys: Mutex::new(sys),
disks: Mutex::new(disks),
networks: Mutex::new(networks),
}
}
}
================================================
FILE: src-tauri/src/ui/mod.rs
================================================
//! User interface functionality
//!
//! This module handles UI-specific functionality, including window effects
//! and platform-specific visual customizations.
mod window;
pub use window::setup_window_effects;
================================================
FILE: src-tauri/src/ui/window.rs
================================================
//! Window effects and customization
//!
//! Provides platform-specific window effects like transparency and vibrancy.
use tauri::WebviewWindow;
#[cfg(target_os = "windows")]
use window_vibrancy::apply_acrylic;
#[cfg(target_os = "macos")]
use window_vibrancy::{apply_vibrancy, NSVisualEffectMaterial, NSVisualEffectState};
/// Applies Windows-specific window effects
#[cfg(target_os = "windows")]
pub fn setup_window_effects(window: &WebviewWindow) -> Result<(), Box> {
apply_acrylic(window, Some((0, 0, 25, 125)))?;
Ok(())
}
/// Applies macOS-specific window effects
#[cfg(target_os = "macos")]
pub fn setup_window_effects(window: &WebviewWindow) -> Result<(), Box> {
apply_vibrancy(
window,
NSVisualEffectMaterial::HudWindow,
Some(NSVisualEffectState::Active),
None,
)?;
Ok(())
}
/// No-op for platforms without specific window effects
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
pub fn setup_window_effects(_window: &WebviewWindow) -> Result<(), Box> {
Ok(())
}
================================================
FILE: src-tauri/tauri.conf.json
================================================
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
"frontendDist": "../build",
"devUrl": "http://localhost:1420"
},
"bundle": {
"active": true,
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"macOS": {
"hardenedRuntime": true,
"entitlements": null,
"providerShortName": null
},
"targets": ["app", "dmg"]
},
"productName": "NeoHtop",
"mainBinaryName": "NeoHtop",
"version": "1.2.0",
"identifier": "com.neohtop.dev",
"plugins": {
"os": {
"all": true
}
},
"app": {
"macOSPrivateApi": true,
"windows": [
{
"fullscreen": false,
"theme": "Dark",
"height": 900,
"resizable": true,
"title": "NeoHtop",
"width": 1280,
"minWidth": 1120,
"minHeight": 700,
"titleBarStyle": "Overlay",
"hiddenTitle": true,
"transparent": true
}
],
"security": {
"csp": null
}
}
}
================================================
FILE: svelte.config.js
================================================
// Tauri doesn't have a Node.js server to do proper SSR
// so we will use adapter-static to prerender the app (SSG)
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
import adapter from "@sveltejs/adapter-static";
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter(),
alias: {
$lib: 'src/lib'
}
},
preprocess: vitePreprocess()
};
export default config;
================================================
FILE: vite.config.js
================================================
import { defineConfig } from "vite";
import { sveltekit } from "@sveltejs/kit/vite";
const host = process.env.TAURI_DEV_HOST;
// https://vitejs.dev/config/
export default defineConfig(async () => ({
plugins: [sveltekit()],
resolve: {
alias: {
$lib: "/src/lib",
},
},
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
// 1. prevent vite from obscuring rust errors
clearScreen: false,
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
strictPort: true,
host: host || false,
hmr: host
? {
protocol: "ws",
host,
port: 1421,
}
: undefined,
watch: {
// 3. tell vite to ignore watching `src-tauri`
ignored: ["**/src-tauri/**"],
},
},
}));