Full Code of Abdenasser/neohtop for AI

main dc22a9f475b9 cached
95 files
276.9 KB
79.4k tokens
72 symbols
1 requests
Download .txt
Showing preview only (300K chars total). Download the full file or copy to clipboard to get everything.
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
================================================

<div align="center">
  <img src="app-icon.png" alt="NeoHtop Logo" width="120" />
  <h1>NeoHtop</h1>
  <p>A modern, cross-platform system monitor built on top of Svelte, Rust, and Tauri.</p>

  [![License](https://img.shields.io/github/license/Abdenasser/neohtop)](https://github.com/Abdenasser/neohtop/blob/main/LICENSE)
  [![GitHub stars](https://img.shields.io/github/stars/Abdenasser/neohtop)](https://github.com/Abdenasser/neohtop/stargazers)
  [![GitHub issues](https://img.shields.io/github/issues/Abdenasser/neohtop)](https://github.com/Abdenasser/neohtop/issues)
  [![GitHub release](https://img.shields.io/github/v/release/Abdenasser/neohtop)](https://github.com/Abdenasser/neohtop/releases)
  [![Notarized by Apple](https://img.shields.io/badge/Release_Notarized_by_Apple-000000?style=flat-square&logo=apple&logoColor=white)](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution)
</div>

<div align="center">
  <picture>
    <!-- <source media="(prefers-color-scheme: dark)" srcset="screenshot.png">
    <source media="(prefers-color-scheme: light)" srcset="screenshot-light.png"> -->
    <img alt="NeoHtop Screenshot" src="./screenshot.png" width="800">
  </picture>
</div>

<div align="center">
  <p>If you find this project helpful, consider buying me a coffee:</p>
  <a href="https://www.buymeacoffee.com/abdenasser" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
  <p>Or sponsor me on GitHub:</p>
  <a href="https://github.com/sponsors/Abdenasser" target="_blank"><img src="https://img.shields.io/badge/Sponsor-abdenasser-white?style=flat&logo=github&logoColor=pink" alt="Sponsor @abdenasser" style="height: auto !important;width: 217px !important;"></a>

</div>

## 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
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>NeoHtop - Blazing-fast system monitoring for your desktop (built with Rust, Tauri & Svelte)</title>
  <meta name="description"
    content="A beautiful, efficient system monitor built with Rust and Svelte. Monitor processes, CPU, and memory usage in real-time with a modern interface.">
  <meta name="keywords"
    content="system monitor, task manager, cross-platform, process monitor, cpu usage, memory usage, rust, svelte">
  <meta name="author" content="Your Name">
  <meta property="og:type" content="website">
  <meta property="og:url" content="https://abdenasser.github.io/neohtop/">
  <meta property="og:title" content="NeoHtop - Blazing-fast system monitoring for your desktop">
  <meta property="og:description"
    content="A beautiful, efficient system monitor built with Rust and Svelte. Monitor processes, CPU, and memory usage in real-time.">
  <meta property="og:image" content="https://github.com/Abdenasser/neohtop/raw/main/app-icon.png">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:creator" content="@__abdenasser">
  <meta name="twitter:title" content="NeoHtop - Blazing-fast system monitoring for your desktop">
  <meta name="twitter:description" content="A beautiful, efficient system monitor built with Rust and Svelte.">
  <meta name="twitter:image" content="https://github.com/Abdenasser/neohtop/raw/main/app-icon.png">
  <link rel="icon" type="image/png" href="favicon.ico">
  <link rel="apple-touch-icon" href="apple-touch-icon.png">
  <link rel="stylesheet" href="styles.css">
  <link rel="preload" href="https://github.com/Abdenasser/neohtop/raw/main/app-icon.png" as="image">
  <link rel="preload" href="https://github.com/Abdenasser/neohtop/raw/main/screenshot.png" as="image">
</head>

<body>
  <header class="glass-nav">
    <nav>
      <div class="logo-container">
        <img src="https://github.com/Abdenasser/neohtop/raw/main/app-icon.png" alt="NeoHtop" class="nav-logo">
        <span class="nav-brand">NeoHtop</span>
      </div>
      <button class="menu-button" aria-label="Toggle menu">
        ☰
      </button>
      <div class="nav-links">
        <a href="#home">Home</a>
        <a href="#features">Features</a>
        <a href="#download">Download</a>
        <a href="#install">Install</a>
        <a href="#testimonials">Testimonials</a>
        <a href="#faq">FAQ</a>
        <button id="themeToggle" class="theme-toggle" aria-label="Toggle theme">
          <span class="theme-icon">🌙</span>
        </button>
      </div>
    </nav>
  </header>

  <main>
    <section id="home" class="hero">
      <div class="hero-content">
        <div class="hero-text">
          <h1 class="gradient-text">Monitor Your System<br>With Style</h1>
          <p class="hero-subtitle">A beautiful, lightning-fast cross-platform system monitor.</p>
          <div class="hero-cta">
            <a href="#download" class="primary-button">Download Now</a>
            <a href="https://github.com/abdenasser/neohtop" class="secondary-button">
              View on GitHub
            </a>
          </div>
          <div class="badges">
            <script data-name="BMC-Widget" data-cfasync="false"
              src="https://cdnjs.buymeacoffee.com/1.0.0/widget.prod.min.js" data-id="abdenasser"
              data-description="Support me on Buy me a coffee!" data-message="" data-color="#FF5F5F"
              data-position="Right" data-x_margin="18" data-y_margin="18"></script>
          </div>
        </div>
      </div>
      <div class="hero-background">
        <img src="https://github.com/Abdenasser/neohtop/raw/main/screenshot-light.png" alt="NeoHtop Interface light" />
        <img src="https://github.com/Abdenasser/neohtop/raw/main/screenshot.png" alt="NeoHtop Interface dark" />
      </div>
    </section>

    <section id="features" class="features">
      <h2 class="section-title">Why Choose NeoHtop?</h2>
      <div class="feature-grid">
        <div class="feature-card">
          <div class="feature-icon">🚀</div>
          <h3>Real-time Monitoring</h3>
          <p>Track system processes in real-time with minimal resource usage</p>
        </div>
        <div class="feature-card">
          <div class="feature-icon">🎨</div>
          <h3>Modern UI</h3>
          <p>Beautiful interface with automatic dark/light theme detection</p>
        </div>
        <div class="feature-card">
          <div class="feature-icon">🔍</div>
          <h3>Smart Search</h3>
          <p>Quick process search with advanced filtering options</p>
        </div>
        <div class="feature-card">
          <div class="feature-icon">📌</div>
          <h3>Process Pinning</h3>
          <p>Keep important processes in view for easy monitoring</p>
        </div>
        <div class="feature-card">
          <div class="feature-icon">⚡️</div>
          <h3>Resource Efficient</h3>
          <p>Built with Rust for optimal performance and low memory usage</p>
        </div>
        <div class="feature-card">
          <div class="feature-icon">🛠</div>
          <h3>Process Management</h3>
          <p>View and manage processes with detailed information</p>
        </div>
      </div>
    </section>

    <section id="download" class="downloads">
      <div class="download-container glass-card">
        <h2>Download NeoHtop <span id="current-version">Loading...</span></h2>
        <div class="download-stats">
          <span id="download-count">...</span> Total Downloads
        </div>
        <p class="download-subtitle">Choose your platform to get started</p>
        <div class="download-options">
          <!-- macOS Downloads -->
          <div class="download-group">
            <h3 class="platform-title">macOS</h3>
            <!-- notarized by apple badge -->
            <div class="notarized-badge">
              <img
                src="https://img.shields.io/badge/Notarized%20by%20Apple-000000?style=for-the-badge&logo=apple&logoColor=white"
                alt="Notarized by Apple">
            </div>
            <div class="platform-options">
              <a href="" class="download-button macos" data-type="macos-intel" data-version="latest">
                <span class="icon">💻</span>
                <div class="button-text">
                  <span class="primary">Intel Chip</span>
                  <span class="secondary">macOS 10.15 or later</span>
                </div>
              </a>
              <a href="" class="download-button macos" data-type="macos-silicon" data-version="latest">
                <span class="icon">🍎</span>
                <div class="button-text">
                  <span class="primary">Apple Silicon</span>
                  <span class="secondary">macOS 11.0 or later</span>
                </div>
              </a>
            </div>
          </div>

          <!-- Windows Download -->
          <div class="download-group">
            <h3 class="platform-title">Windows</h3>
            <div class="platform-options">
              <a href="" class="download-button windows" data-type="windows" data-version="latest">
                <span class="icon">🫣</span>
                <div class="button-text">
                  <span class="primary">Windows</span>
                  <span class="secondary">Windows 10 or later</span>
                </div>
              </a>
            </div>
          </div>

          <!-- Linux Downloads -->
          <div class="download-group">
            <h3 class="platform-title">Linux</h3>
            <div class="platform-options">
              <!-- x86_64 Downloads -->
              <div class="linux-downloads">
                <h4>x86_64</h4>
                <a href="" class="download-button linux" data-type="linux-deb-x64" data-version="latest">
                  <span class="icon">📦</span>
                  <div class="button-text">
                    <span class="primary">.deb Package</span>
                    <span class="secondary">Debian/Ubuntu</span>
                  </div>
                </a>
                <a href="" class="download-button linux" data-type="linux-appimage-x64" data-version="latest"
                  style="margin-top: 10px;">
                  <span class="icon">🐧</span>
                  <div class="button-text">
                    <span class="primary">AppImage</span>
                    <span class="secondary">Universal Linux</span>
                  </div>
                </a>
                <a href="" class="download-button linux" data-type="linux-rpm-x64" data-version="latest"
                  style="margin-top: 10px;">
                  <span class="icon">📦</span>
                  <div class="button-text">
                    <span class="primary">.rpm Package</span>
                    <span class="secondary">Fedora/RHEL x86_64</span>
                  </div>
                </a>
              </div>

              <!-- ARM64 Downloads -->
              <div class="linux-downloads">
                <h4>ARM64</h4>
                <a href="" class="download-button linux" data-type="linux-deb-arm64" data-version="latest">
                  <span class="icon">📦</span>
                  <div class="button-text">
                    <span class="primary">.deb Package</span>
                    <span class="secondary">Debian/Ubuntu ARM64</span>
                  </div>
                </a>
                <a href="" class="download-button linux" data-type="linux-appimage-arm64" data-version="latest"
                  style="margin-top: 10px;">
                  <span class="icon">🐧</span>
                  <div class="button-text">
                    <span class="primary">AppImage</span>
                    <span class="secondary">Universal Linux ARM64</span>
                  </div>
                </a>
                <a href="" class="download-button linux" data-type="linux-rpm-arm64" data-version="latest"
                  style="margin-top: 10px;">
                  <span class="icon">📦</span>
                  <div class="button-text">
                    <span class="primary">.rpm Package</span>
                    <span class="secondary">Fedora/RHEL ARM64</span>
                  </div>
                </a>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>

    <section id="install" class="install">
      <h2>Installation Guide</h2>
      <div class="install-steps">
        <div class="step">
          <span class="step-number">1</span>
          <h3>Download</h3>
          <p>Choose and download the appropriate version for your Mac</p>
        </div>
        <div class="step">
          <span class="step-number">2</span>
          <h3>Open DMG</h3>
          <p>Double-click the downloaded .dmg file</p>
        </div>
        <div class="step">
          <span class="step-number">3</span>
          <h3>Install</h3>
          <p>Drag NeoHtop to your Applications folder</p>
        </div>
        <div class="step">
          <span class="step-number">4</span>
          <h3>First Launch</h3>
          <p>Right-click and choose Open to bypass Gatekeeper</p>
        </div>
      </div>
    </section>

    <section id="testimonials" class="testimonials">
      <h2 class="section-title">What People Are Saying</h2>
      <div class="testimonial-grid">
        <blockquote class="twitter-tweet">
          <p lang="en" dir="ltr">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! <a
              href="https://t.co/TAzCIEb8uF">pic.twitter.com/TAzCIEb8uF</a></p>&mdash; JoeyBaggaDonuts (@haxxusj) <a
            href="https://twitter.com/haxxusj/status/1854590782684250487?ref_src=twsrc%5Etfw">November 7, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="zh" dir="ltr">Htop 加强版: NeoHtop<br>基于 Svelte、Rust 和 Tauri 构建的现代化、跨平台的系统监控工具<a
              href="https://t.co/OtWlxxrCHZ">https://t.co/OtWlxxrCHZ</a> <a
              href="https://t.co/2MK9n8rZVw">pic.twitter.com/2MK9n8rZVw</a></p>&mdash; Geek (@geekbb) <a
            href="https://twitter.com/geekbb/status/1854081285846888881?ref_src=twsrc%5Etfw">November 6, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="zh" dir="ltr">💻 NeoHtop:现代跨平台系统监视器!实时追踪 CPU 和内存使用,支持进程管理。<br><br>基于 Svelte、Rust 和 Tauri
            构建,轻量高效,非常适合日常系统监控!<br><br>👉 <a href="https://t.co/MaOIMMbbPV">https://t.co/MaOIMMbbPV</a> <a
              href="https://t.co/rFLyULJVei">https://t.co/rFLyULJVei</a> <a
              href="https://t.co/6c6hGOeiLa">pic.twitter.com/6c6hGOeiLa</a></p>&mdash; 小弟调调 (@jaywcjlove) <a
            href="https://twitter.com/jaywcjlove/status/1854089502853357880?ref_src=twsrc%5Etfw">November 6, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="en" dir="ltr">neoHTOP: A modern, cross-platform system monitor built on top of Svelte, Rust and
            Tauri<a href="https://t.co/MKZ53ZThKv">https://t.co/MKZ53ZThKv</a></p>&mdash; Nitin (@gniting) <a
            href="https://twitter.com/gniting/status/1853920938976223463?ref_src=twsrc%5Etfw">November 5, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="en" dir="ltr">🚀 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… <a
              href="https://t.co/qq8cXI8Qq1">pic.twitter.com/qq8cXI8Qq1</a></p>&mdash; Durgesh Gupta (@ilearnbydoing) <a
            href="https://twitter.com/ilearnbydoing/status/1854037593987428754?ref_src=twsrc%5Etfw">November 6, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="zxx" dir="ltr"><a href="https://t.co/2EoanAOvYL">https://t.co/2EoanAOvYL</a></p>&mdash; Tom Dörr
          (@tom_doerr) <a href="https://twitter.com/tom_doerr/status/1853861836736881133?ref_src=twsrc%5Etfw">November
            5, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="ru" dir="ltr">NeoHtop – мощный и стильный менеджер процессов<a
              href="https://t.co/uiGhE2YlFC">https://t.co/uiGhE2YlFC</a></p>&mdash; ITforNote (@itfornote) <a
            href="https://twitter.com/itfornote/status/1855943550036299934?ref_src=twsrc%5Etfw">November 11, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="en" dir="ltr">Discover NeoHtop, a modern task manager for macOS combining terminal power with a
            user-friendly interface, built with Rust, Tauri &amp; Svelte.<br><br>{ author: <a
              href="https://twitter.com/__abdenasser?ref_src=twsrc%5Etfw">@__abdenasser</a> } <a
              href="https://twitter.com/hashtag/CodeNewbie?src=hash&amp;ref_src=twsrc%5Etfw">#CodeNewbie</a><a
              href="https://t.co/rowekkvOUY">https://t.co/rowekkvOUY</a></p>&mdash; CodeNewbie (@CodeNewbies) <a
            href="https://twitter.com/CodeNewbies/status/1854886849677824003?ref_src=twsrc%5Etfw">November 8, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="de" dir="ltr">NeoHtop: Ein Systemmonitor für macOS <a
              href="https://t.co/wUEMeCKpkv">https://t.co/wUEMeCKpkv</a></p>&mdash; Ramón Goeden 🤨 (@websenat) <a
            href="https://twitter.com/websenat/status/1854134191480262993?ref_src=twsrc%5Etfw">November 6, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="de" dir="ltr">NeoHtop: Ein Systemmonitor für macOS <a
              href="https://t.co/RCjYWlC81x">https://t.co/RCjYWlC81x</a></p>&mdash; CaschysBlog (@CaschysBlog) <a
            href="https://twitter.com/CaschysBlog/status/1854110661502017689?ref_src=twsrc%5Etfw">November 6, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="en" dir="ltr">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.<a
              href="https://t.co/c4niSeqw0p">https://t.co/c4niSeqw0p</a> <a
              href="https://t.co/hnqrfSiSFG">pic.twitter.com/hnqrfSiSFG</a></p>&mdash; Hustle Hacker (@HustleHackerAI)
          <a href="https://twitter.com/HustleHackerAI/status/1854352028304040139?ref_src=twsrc%5Etfw">November 7,
            2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="ja" dir="ltr">NeoHtopめちゃかっこいい。macOSでも動く。Apple
            Silicon版もちゃんとある。手元で動かしてみたけどアクティビティモニタより好きかも。<br><br>Abdenasser/neohtop: 💪🏻 htop on steroids <a
              href="https://t.co/u70aTS1rKK">https://t.co/u70aTS1rKK</a> <a
              href="https://t.co/c0iyjiW73q">pic.twitter.com/c0iyjiW73q</a></p>&mdash; Isao Shimizu (@isaoshimizu) <a
            href="https://twitter.com/isaoshimizu/status/1854097990698586584?ref_src=twsrc%5Etfw">November 6, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="ja" dir="ltr">見た目好みで使ってみてるシステムモニター、<a
              href="https://t.co/7lHLB8WtVk">https://t.co/7lHLB8WtVk</a><br>トグルボタンのアクティブ状態をアイコンを45度傾けるという方法で示してて、特殊すぎて2度見した
            <a href="https://t.co/JFC6ueyfBF">pic.twitter.com/JFC6ueyfBF</a>
          </p>&mdash; UMERUMA (@umeruma) <a
            href="https://twitter.com/umeruma/status/1855209294888902865?ref_src=twsrc%5Etfw">November 9, 2024</a>
        </blockquote>


        <blockquote class="twitter-tweet">
          <p lang="en" dir="ltr">🖥️ <a
              href="https://twitter.com/hashtag/NeoHtop?src=hash&amp;ref_src=twsrc%5Etfw">#NeoHtop</a>: Cross-platform
            system monitor combining <a
              href="https://twitter.com/hashtag/Svelte?src=hash&amp;ref_src=twsrc%5Etfw">#Svelte</a>, <a
              href="https://twitter.com/hashtag/Rust?src=hash&amp;ref_src=twsrc%5Etfw">#Rust</a>, and <a
              href="https://twitter.com/hashtag/Tauri?src=hash&amp;ref_src=twsrc%5Etfw">#Tauri</a> for a modern
            monitoring experience <a
              href="https://twitter.com/hashtag/opensource?src=hash&amp;ref_src=twsrc%5Etfw">#opensource</a> <a
              href="https://twitter.com/hashtag/devops?src=hash&amp;ref_src=twsrc%5Etfw">#devops</a> <br><br>🚀 Key
            features:<br>Real-time process monitoring<br>CPU &amp; memory usage tracking<br>Process search &amp;
            filtering<br>Dark/light theme support<br><br>💻 Tech stack:…</p>&mdash; Micha(el) Bladowski 🇩🇪 🇺🇦
          (@michabbb) <a href="https://twitter.com/michabbb/status/1853971838768173409?ref_src=twsrc%5Etfw">November 6,
            2024</a>
        </blockquote>

      </div>
    </section>

    <section id="faq" class="faq">
      <h2 class="section-title">Frequently Asked Questions</h2>
      <div class="faq-list">
        <div class="faq-item">
          <button class="faq-question">
            <span class="question-text">How does NeoHtop compare to Activity Monitor?</span>
            <span class="faq-icon">⌄</span>
          </button>
          <div class="faq-answer">
            <p>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.</p>
          </div>
        </div>

        <div class="faq-item">
          <button class="faq-question">
            <span class="question-text">Is NeoHtop resource intensive?</span>
            <span class="faq-icon">⌄</span>
          </button>
          <div class="faq-answer">
            <p>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.</p>
          </div>
        </div>

        <div class="faq-item">
          <button class="faq-question">
            <span class="question-text">Does NeoHtop work on all macOS versions?</span>
            <span class="faq-icon">⌄</span>
          </button>
          <div class="faq-answer">
            <p>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.</p>
          </div>
        </div>

        <div class="faq-item">
          <button class="faq-question">
            <span class="question-text">Can I customize the interface?</span>
            <span class="faq-icon">⌄</span>
          </button>
          <div class="faq-answer">
            <p>Yes, NeoHtop offers various customization options including:
            <ul>
              <li>Dark/Light theme switching</li>
              <li>Customizable columns and metrics</li>
              <li>Adjustable refresh rates</li>
              <li>Process grouping options</li>
            </ul>
            </p>
          </div>
        </div>

        <div class="faq-item">
          <button class="faq-question">
            <span class="question-text">Is NeoHtop open source?</span>
            <span class="faq-icon">⌄</span>
          </button>
          <div class="faq-answer">
            <p>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.</p>
          </div>
        </div>
      </div>
    </section>

    <!-- Google tag (gtag.js) -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-88HFXRBNLS"></script>
    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag() { dataLayer.push(arguments); }
      gtag('js', new Date());

      gtag('config', 'G-88HFXRBNLS');
    </script>
    <script src="main.js"></script>
  </main>
  <footer class="footer">
    <div class="footer-content">
      <div class="footer-section">
        <h3>NeoHtop</h3>
        <p class="footer-description">
          A modern system monitor built with Rust and Svelte.
          Open source and free to use.
        </p>
      </div>

      <div class="footer-section">
        <h4>Links</h4>
        <ul class="footer-links">
          <li>
            <a href="https://github.com/abdenasser/neohtop" target="_blank" rel="noopener">
              <span class="icon">📦</span> GitHub Repository
            </a>
          </li>
          <li>
            <a href="https://github.com/abdenasser" target="_blank" rel="noopener">
              <span class="icon">👨‍💻</span> Creator
            </a>
          </li>
          <li>
            <a href="https://github.com/abdenasser/neohtop/issues" target="_blank" rel="noopener">
              <span class="icon">🐛</span> Report Issue
            </a>
          </li>
        </ul>
      </div>

      <div class="footer-section">
        <h4>Tech Stack</h4>
        <ul class="tech-stack-list">
          <li>
            <a href="https://www.rust-lang.org/" target="_blank" rel="noopener">
              <span class="icon">🦀</span> Rust
            </a>
          </li>
          <li>
            <a href="https://tauri.app/" target="_blank" rel="noopener">
              <span class="icon">⚡</span> Tauri
            </a>
          </li>
          <li>
            <a href="https://svelte.dev/" target="_blank" rel="noopener">
              <span class="icon">🎯</span> Svelte
            </a>
          </li>
        </ul>
      </div>
    </div>

    <div class="footer-bottom">
      <p>Made with <span class="heart">❤️</span> and <span class="coffee">☕</span> by
        <a href="https://github.com/abdenasser" target="_blank" rel="noopener">Abdenasser</a>
      </p>
    </div>
  </footer>
</body>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

</html>

================================================
FILE: docs/main.js
================================================
// ===============================
// Theme Management
// ===============================
const themeToggle = document.getElementById('themeToggle');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');

function setTheme(isDark) {
  document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
  themeToggle.querySelector('.theme-icon').textContent = isDark ? '☀️' : '🌙';
  localStorage.setItem('theme', isDark ? 'dark' : 'light');
}

// Initialize theme
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
  setTheme(savedTheme === 'dark');
} else {
  setTheme(prefersDark.matches);
}

// Theme event listeners
themeToggle.addEventListener('click', () => {
  const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
  setTheme(!isDark);
});

prefersDark.addEventListener('change', (e) => {
  if (!localStorage.getItem('theme')) {
    setTheme(e.matches);
  }
});

// ===============================
// Version and Download Management
// ===============================
async function fetchDownloadStats() {
  try {
    const releasesResponse = await fetch('https://api.github.com/repos/abdenasser/neohtop/releases');
    const releases = await releasesResponse.json();
    const githubDownloads = releases.reduce((total, release) => {
      const releaseDownloads = release.assets.reduce((sum, asset) =>
        sum + asset.download_count, 0);
      return total + releaseDownloads;
    }, 0);

    const brewResponse = await fetch('https://formulae.brew.sh/api/analytics/install/homebrew-core/365d.json');
    const brewData = await brewResponse.json();
    const brewInstalls = brewData.formulae?.neohtop?.[0]?.count || 0;

    const totalDownloads = githubDownloads + brewInstalls;
    document.getElementById('download-count').textContent = new Intl.NumberFormat().format(totalDownloads);
  } catch (error) {
    console.error('Failed to fetch download stats:', error);
    document.getElementById('download-count').textContent = 'N/A';
  }
}

async function updateVersion() {
  try {
    const response = await fetch('https://api.github.com/repos/Abdenasser/neohtop/releases/latest');
    const data = await response.json();
    const version = data.tag_name;
    const versionNumber = version.match(/\d+\.\d+\.\d+/)?.[0];

    if (versionNumber) {
      document.getElementById('current-version').textContent = "v" + versionNumber;
      updateDownloadLinks(versionNumber);
    }
  } catch (error) {
    console.error('Failed to fetch version:', error);
  }
}

function updateDownloadLinks(versionNumber) {
  const platformUrls = {
    'macos-intel': `intel-NeoHtop_${versionNumber}_x64.dmg`,
    'macos-silicon': `silicon-NeoHtop_${versionNumber}_aarch64.dmg`,
    'windows': `NeoHtop_${versionNumber}_x64.exe`,
    'linux-deb-x64': `NeoHtop_${versionNumber}_x86_64.deb`,
    'linux-appimage-x64': `NeoHtop_${versionNumber}_x86_64.AppImage`,
    'linux-rpm-x64': `NeoHtop_${versionNumber}_x86_64.rpm`,
    'linux-deb-arm64': `NeoHtop_${versionNumber}_aarch64.deb`,
    'linux-appimage-arm64': `NeoHtop_${versionNumber}_aarch64.AppImage`,
    'linux-rpm-arm64': `NeoHtop_${versionNumber}_aarch64.rpm`
  };

  document.querySelectorAll('.download-button').forEach(link => {
    const platform = link.getAttribute('data-type');
    if (platformUrls[platform]) {
      link.href = `https://github.com/Abdenasser/neohtop/releases/download/v${versionNumber}/${platformUrls[platform]}`;
    }
  });
}

// ===============================
// UI Interactions
// ===============================
// FAQ Accordion
document.querySelectorAll('.faq-question').forEach(button => {
  button.addEventListener('click', () => {
    const faqItem = button.parentElement;
    const isActive = faqItem.classList.contains('active');
    document.querySelectorAll('.faq-item').forEach(item => item.classList.remove('active'));
    if (!isActive) faqItem.classList.add('active');
  });
});

// Download tracking
document.querySelectorAll('.download-button').forEach(button => {
  button.addEventListener('click', (e) => {
    gtag('event', 'download', {
      'event_category': 'App',
      'event_label': button.getAttribute('data-type'),
      'value': button.getAttribute('data-version')
    });
  });
});

// Smooth scroll
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
  anchor.addEventListener('click', function (e) {
    e.preventDefault();
    const target = document.querySelector(this.getAttribute('href'));
    if (target) {
      target.scrollIntoView({ behavior: 'smooth', block: 'start' });
    }
  });
});
// ===============================
// Animations
// ===============================
const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('visible');
      }
    });
  },
  { threshold: 0.1 }
);

document.querySelectorAll('.feature, .step, .download-button').forEach(el => {
  observer.observe(el);
});

// ===============================
// Initialization
// ===============================
document.addEventListener('DOMContentLoaded', () => {
  updateVersion();
  fetchDownloadStats();
});


// ===============================
// Mobile Navigation
// ===============================

document.addEventListener('DOMContentLoaded', () => {
  const menuButton = document.querySelector('.menu-button');
  const navLinks = document.querySelector('.nav-links');

  menuButton.addEventListener('click', () => {
    navLinks.classList.toggle('active');
  });

  // Close menu when clicking outside
  document.addEventListener('click', (e) => {
    if (!navLinks.contains(e.target) && !menuButton.contains(e.target)) {
      navLinks.classList.remove('active');
    }
  });
});

================================================
FILE: docs/styles.css
================================================
:root {
  --primary-color: #6366f1;
  --secondary-color: #818cf8;
  --background: #ffffff;
  --text-primary: #1f2937;
  --text-secondary: #4b5563;
  --card-background: rgba(255, 255, 255, 0.8);
}

[data-theme="dark"] {
  --background: #0f172a;
  --text-primary: #f1f5f9;
  --text-secondary: #cbd5e1;
  --card-background: rgba(30, 41, 59, 0.8);
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  scroll-behavior: smooth;
}

body {
  font-family: 'Inter', system-ui, sans-serif;
  background: var(--background);
  color: var(--text-primary);
  line-height: 1.6;
  transition: background-color 0.3s, color 0.3s;
  margin: 0;
  padding: 0;
  min-height: 100vh;
  width: 100%;
}

/* Header & Navigation */
header {
  background: var(--card);
  position: sticky;
  top: 0;
  z-index: 100;
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}

nav {
  max-width: 1200px;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.logo-container {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.nav-logo {
  width: 32px;
  /* Adjust size as needed */
  height: 32px;
  object-fit: contain;
}

.nav-brand {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--text-primary);
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 2rem;
}

.nav-links a {
  color: var(--text-primary);
  text-decoration: none;
  font-weight: 500;
  transition: color 0.2s;
}

.nav-links a:hover {
  color: var(--primary-color);
}

.theme-toggle {
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 0.5rem;
  color: var(--text-primary);
}

/* Theme Toggle */
.theme-toggle {
  background: none;
  border: none;
  color: var(--text);
  cursor: pointer;
  padding: 0.5rem;
  border-radius: 50%;
  transition: background-color 0.2s;
}

.theme-toggle:hover {
  background: var(--hover);
}

/* Main Content */
main {
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem;
  padding-top: 64px;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  width: 100%;
  /* Should match the height of your nav */
}

/* Sections */
section {
  margin: 6rem 0;
  scroll-margin-top: 5rem;
}

/* Hero Section */
.hero {
  height: 600px;
  width: 100%;
  display: flex;
  align-items: center;
  overflow: hidden;
}

.hero-content {
  flex: 0 0 50%;
  padding: 2rem;
  z-index: 2;
  position: relative;
}

.hero-content::before {
  content: '';
  position: absolute;
  top: -50px;
  left: -50px;
  width: calc(100% + 100px);
  height: calc(100% + 100px);
  background: linear-gradient(to right,
      var(--background) 0%,
      var(--background) 60%,
      transparent 100%);
  z-index: -1;
}

.hero-background {
  flex: 0 0 90%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin-right: -40%;
  padding-right: 40%;
}

.hero-background img {
  max-width: 200%;
  max-height: 200%;
  width: auto;
  height: 100%;
  object-fit: contain;
}

.hero-background img[src*="screenshot.png"] {
  display: none;
}

.hero-background img[src*="screenshot-light.png"] {
  display: block;
}

/* Dark mode */
[data-theme="dark"] .hero-background img[src*="screenshot.png"] {
  display: block;
}

[data-theme="dark"] .hero-background img[src*="screenshot-light.png"] {
  display: none;
}

.gradient-text {
  font-size: 3rem;
  font-weight: 800;
  line-height: 1.2;
  margin-bottom: 1rem;
}

.hero-subtitle {
  font-size: 1.125rem;
  color: var(--text-secondary);
  margin-bottom: 1.5rem;
}

.hero-cta {
  display: flex;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.hero-image {
  width: 100%;
}

.floating-screenshot {
  width: 100%;
  height: auto;
  max-width: 600px;
  border-radius: 12px;
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
  animation: float 6s ease-in-out infinite;
}

/* Responsive design */
@media (max-width: 1024px) {
  .hero {
    flex-direction: column;
    height: auto;
    min-height: auto;
  }

  .hero-content {
    flex: 0 0 100%;
    width: 100%;
    padding: 3rem 2rem;
    text-align: center;
  }

  .hero-background {
    flex: 0 0 100%;
    margin-right: 0;
    padding-right: 0;
  }

  .hero-background img {
    width: 100%;
    height: auto;
    max-height: 400px;
    object-fit: contain;
  }

  .hero-content::before {
    display: none;
    /* Remove the gradient since content and image are now stacked */
  }

  .hero-cta {
    justify-content: center;
  }

  .tech-stack {
    justify-content: center;
  }
}

@media (max-width: 640px) {
  .hero {
    padding: 2rem 1rem;
  }

  .hero-content {
    padding: 2rem 1rem;
  }

  .gradient-text {
    font-size: 2.5rem;
  }

  .hero-cta {
    flex-direction: column;
  }
}

.logo {
  width: 120px;
  height: 120px;
  margin-bottom: 1rem;
  transition: transform 0.3s;
}

.logo:hover {
  transform: scale(1.05);
}

.badges {
  display: flex;
  gap: 0.5rem;
  justify-content: center;

  /* Centered only on tablets and smaller screens */
  @media (min-width: 1024px) {
    justify-content: flex-start;
    /* Align to the start on larger screens */
  }

  margin-top: 1rem;
}

.badge {
  background: var(--primary);
  color: white;
  padding: 0.25rem 0.75rem;
  border-radius: 9999px;
  font-size: 0.875rem;
}

/* Features */
.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
  margin-top: 2rem;
}

.feature {
  background: var(--card);
  padding: 2rem;
  border-radius: 12px;
  transition: transform 0.2s;
  border: 1px solid var(--border);
}

.feature:hover {
  transform: translateY(-4px);
}

/* Download Section */
.download-options {
  display: flex;
  gap: 1.5rem;
  justify-content: center;
  margin: 2rem 0;
  flex-wrap: wrap;
  padding: 0 1rem;
}

.download-button {
  background: var(--primary-color);
  color: white;
  padding: 1rem 1.5rem;
  border-radius: 8px;
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 1rem;
  transition: all 0.2s;
  min-width: 250px;
}

.download-button .button-text {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.download-button .primary {
  font-weight: 600;
  font-size: 1rem;
}

.download-button .secondary {
  font-size: 0.85rem;
  opacity: 0.9;
}

/* New styles for nested links */
.download-button .secondary a {
  color: inherit;
  text-decoration: none;
  padding: 2px 6px;
  border-radius: 4px;
  transition: all 0.2s;
  display: inline-block;
}

.download-button .secondary a:hover {
  background-color: rgba(255, 255, 255, 0.2);
  transform: translateY(-1px);
}

.download-button:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.download-button .icon {
  font-size: 1.5rem;
}

.button-text {
  display: flex;
  flex-direction: column;
}

.button-text .primary {
  font-weight: 600;
}

.button-text .secondary {
  font-size: 0.875rem;
  opacity: 0.8;
}

/* Primary button style (can be used for other buttons too) */
.primary-button {
  background: var(--primary-color);
  color: white;
  padding: 0.75rem 1.5rem;
  border-radius: 8px;
  text-decoration: none;
  font-weight: 500;
  transition: all 0.2s;
}

.primary-button:hover {
  background: var(--secondary-color);
  transform: translateY(-2px);
}

/* Secondary button style */
.secondary-button {
  background: rgba(99, 102, 241, 0.1);
  color: var(--primary-color);
  padding: 0.75rem 1.5rem;
  border-radius: 8px;
  text-decoration: none;
  font-weight: 500;
  transition: all 0.2s;
}

.secondary-button:hover {
  background: rgba(99, 102, 241, 0.2);
  transform: translateY(-2px);
}

@media (max-width: 640px) {
  .download-options {
    flex-direction: column;
    align-items: stretch;
  }

  .download-button {
    justify-content: center;
  }
}

/* Installation Steps */
.install-steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 2rem;
  margin-top: 2rem;
}

.step {
  background: var(--card-background);
  padding: 2rem;
  border-radius: 12px;
  position: relative;
  border: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
    0 2px 4px -1px rgba(0, 0, 0, 0.06);
  transition: transform 0.2s ease;
}

.step:hover {
  transform: translateY(-4px);
}

.step-number {
  position: absolute;
  top: -1rem;
  left: -1rem;
  background: var(--primary-color);
  color: white;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}

.step h3 {
  margin-bottom: 1rem;
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--text-primary);
}

.step p {
  color: var(--text-secondary);
  line-height: 1.5;
}

/* Dark mode adjustments */
[data-theme="dark"] .step {
  border-color: rgba(255, 255, 255, 0.1);
  background: rgba(30, 41, 59, 0.8);
}

@media (max-width: 768px) {
  .install-steps {
    grid-template-columns: 1fr;
  }
}

/* Keyboard Shortcuts */
.shortcuts {
  background: var(--card);
  border-radius: 12px;
  padding: 2rem;
  margin-top: 2rem;
  border: 1px solid var(--border);
}

.shortcut-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1rem;
}

.shortcut {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem;
  border-radius: 6px;
}

.shortcut:hover {
  background: var(--hover);
}

.key {
  background: var(--code-bg);
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  font-family: monospace;
  font-size: 0.875rem;
}

/* FAQ Section */
.faq {
  max-width: 800px;
  margin: 4rem auto;
  padding: 0 2rem;
}

.faq-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.faq-item {
  background: var(--card-background);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 12px;
  overflow: hidden;
  transition: all 0.3s ease;
}

.faq-question {
  width: 100%;
  padding: 1.5rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  color: var(--text-primary);
  font-size: 1.1rem;
  font-weight: 500;
}

.faq-icon {
  font-size: 1.5rem;
  transition: transform 0.3s ease;
}

.faq-item.active .faq-icon {
  transform: rotate(180deg);
}

.faq-answer {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease-out;
  padding: 0 1.5rem;
}

.faq-item.active .faq-answer {
  max-height: 500px;
  /* Adjust based on content */
  padding: 0 1.5rem 1.5rem;
}

.faq-answer p {
  color: var(--text-secondary);
  line-height: 1.6;
}

.faq-answer ul {
  margin-top: 0.5rem;
  margin-left: 1.5rem;
  color: var(--text-secondary);
}

.faq-answer li {
  margin-bottom: 0.5rem;
}

/* Dark mode adjustments */
[data-theme="dark"] .faq-item {
  border-color: rgba(255, 255, 255, 0.1);
  background: rgba(30, 41, 59, 0.8);
}

@media (max-width: 768px) {
  .faq {
    margin: 3rem auto;
  }

  .faq-question {
    padding: 1.25rem;
    font-size: 1rem;
  }
}

/* Footer */
footer {
  border-top: 1px solid var(--border);
  margin-top: 6rem;
  padding-top: 3rem;
}

.footer-content {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 2rem;
  margin-bottom: 2rem;
}

.footer-section {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.footer-section a {
  color: var(--text);
  text-decoration: none;
  opacity: 0.8;
}

.footer-section a:hover {
  opacity: 1;
}

/* Responsive Design */
@media (max-width: 768px) {
  nav {
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  .download-options {
    flex-direction: column;
  }

  .feature-grid {
    grid-template-columns: 1fr;
  }

  .install-steps {
    grid-template-columns: 1fr;
  }
}

/* Animations */
@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.feature,
.step,
.download-button {
  animation: fadeIn 0.5s ease-out;
}

.screenshot {
  width: 100%;
  max-width: 1200px;
  margin: 2rem auto;
  padding: 0 1rem;
}

.screenshot-img {
  width: 100%;
  height: auto;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.glass-nav {
  backdrop-filter: blur(12px);
  background: var(--card-background);
  position: fixed;
  width: 100%;
  top: 0;
  z-index: 1000;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  padding: 1rem 2rem;
  box-sizing: border-box;
  box-shadow: none;
}

[data-theme="dark"] .glass-nav {
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(30, 41, 59, 0.8);
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}

.tech-badge {
  background: rgba(99, 102, 241, 0.1);
  color: var(--primary-color);
  padding: 0.5rem 1rem;
  border-radius: 9999px;
  font-weight: 500;
  margin-right: 0.5rem;
}

.feature-card {
  background: var(--card-background);
  border-radius: 16px;
  padding: 2rem;
  transition: transform 0.2s;
}

.feature-card:hover {
  transform: translateY(-5px);
}

@keyframes float {
  0% {
    transform: translateY(0px);
  }

  50% {
    transform: translateY(-20px);
  }

  100% {
    transform: translateY(0px);
  }
}

/* Features Section */
.features {
  padding: 4rem 2rem;
}

.section-title {
  text-align: center;
  margin-bottom: 3rem;
  font-size: 2.5rem;
  font-weight: 700;
}

.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
  max-width: 1200px;
  margin: 0 auto;
}

.feature-card {
  background: var(--card-background);
  padding: 2rem;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
    0 2px 4px -1px rgba(0, 0, 0, 0.06);
  transition: transform 0.2s ease;
}

.feature-card:hover {
  transform: translateY(-4px);
}

.feature-icon {
  font-size: 2.5rem;
  margin-bottom: 1rem;
}

.feature-card h3 {
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 0.75rem;
  color: var(--text-primary);
}

.feature-card p {
  color: var(--text-secondary);
  line-height: 1.5;
}

/* Dark mode adjustments */
[data-theme="dark"] .feature-card {
  border-color: rgba(255, 255, 255, 0.1);
  background: rgba(30, 41, 59, 0.8);
}

@media (max-width: 768px) {
  .feature-grid {
    grid-template-columns: 1fr;
  }

  .section-title {
    font-size: 2rem;
  }
}

/* Footer Styles */
.footer {
  background: var(--card-background);
  border-top: 1px solid rgba(0, 0, 0, 0.1);
  padding: 4rem 2rem 2rem;
  margin-top: 4rem;
  width: 100%;
  box-sizing: border-box;
  box-shadow: none;
}

[data-theme="dark"] .footer {
  background: rgba(30, 41, 59, 0.8);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.footer-content {
  max-width: 1200px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  gap: 3rem;
  margin-bottom: 3rem;
}

.footer-section h3 {
  font-size: 1.5rem;
  font-weight: 600;
  margin-bottom: 1rem;
  color: var(--text-primary);
}

.footer-section h4 {
  font-size: 1.1rem;
  font-weight: 600;
  margin-bottom: 1rem;
  color: var(--text-primary);
}

.footer-description {
  color: var(--text-secondary);
  line-height: 1.6;
  margin-bottom: 1rem;
}

.footer-links,
.tech-stack-list {
  list-style: none;
  padding: 0;
}

.footer-links li,
.tech-stack-list li {
  margin-bottom: 0.75rem;
}

.footer-links a,
.tech-stack-list a {
  color: var(--text-secondary);
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  transition: color 0.2s ease;
}

.footer-links a:hover,
.tech-stack-list a:hover {
  color: var(--primary-color);
}

.footer-bottom {
  max-width: 1200px;
  margin: 0 auto;
  padding-top: 2rem;
  border-top: 1px solid rgba(0, 0, 0, 0.1);
  text-align: center;
  color: var(--text-secondary);
}

[data-theme="dark"] .footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.footer-bottom p {
  margin-bottom: 0.5rem;
}

.footer-bottom a {
  color: var(--primary-color);
  text-decoration: none;
}

.footer-bottom a:hover {
  text-decoration: underline;
}

.heart {
  color: #ff4b4b;
  display: inline-block;
  animation: heartbeat 1.5s ease infinite;
}

.coffee {
  display: inline-block;
  animation: wiggle 1s ease infinite;
}

@keyframes heartbeat {

  0%,
  100% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.1);
  }
}

@keyframes wiggle {

  0%,
  100% {
    transform: rotate(0deg);
  }

  25% {
    transform: rotate(-10deg);
  }

  75% {
    transform: rotate(10deg);
  }
}

/* Responsive adjustments */
@media (max-width: 768px) {
  .footer-content {
    grid-template-columns: 1fr;
    gap: 2rem;
  }

  .footer {
    padding: 3rem 1.5rem 1.5rem;
  }
}

/* Dark mode adjustments */
[data-theme="dark"] .footer {
  background: rgba(30, 41, 59, 0.8);
  border-color: rgba(255, 255, 255, 0.1);
}

/* Common card styles */
.feature-card,
.step,
.faq-item {
  background: var(--card-background);
  border-radius: 12px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  transition: transform 0.2s ease;
  box-shadow: none;
}

/* Dark mode specific adjustments */
[data-theme="dark"] .feature-card,
[data-theme="dark"] .step,
[data-theme="dark"] .faq-item {
  border-color: rgba(255, 255, 255, 0.1);
  background: rgba(30, 41, 59, 0.8);
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2);
}

/* Hover states */
.feature-card:hover,
.step:hover {
  transform: translateY(-4px);
  border-color: var(--primary-color);
}

.faq-item.active {
  border-color: var(--primary-color);
}

/* Optional: Add subtle hover effect to nav links */
.nav-links a {
  position: relative;
  padding-bottom: 2px;
}

.nav-links a::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: 0;
  height: 2px;
  background: var(--primary-color);
  transition: width 0.3s ease;
}

.nav-links a:hover::after {
  width: 100%;
}

/* Add this to your CSS */
.ph-badge {
  display: inline-block;
  transition: transform 0.2s ease;
}

.ph-badge:hover {
  transform: translateY(-2px);
}

/* If you want to adjust for dark mode */
[data-theme="dark"] .ph-badge img {
  filter: invert(1);
  /* Optional: if you want to invert colors in dark mode */
}

.coming-soon-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  background: #ff6154;
  /* Product Hunt color */
  color: white;
  border-radius: 8px;
  text-decoration: none;
  font-weight: 500;
  transition: transform 0.2s ease;
}

.coming-soon-badge:hover {
  transform: translateY(-2px);
}

.trust-badge {
  margin-top: 1.5rem;
  padding: 0.75rem;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
}

.verified-icon {
  color: #34C759;
  font-weight: bold;
}

.verified-text {
  color: var(--text-color);
}

.learn-more {
  color: var(--primary-color);
  text-decoration: none;
  margin-left: 0.5rem;
}

.learn-more:hover {
  text-decoration: underline;
}

/* Responsive adjustments */
@media (max-width: 768px) {
  .hero {
    height: 500px;
  }

  .hero-content,
  .hero-background {
    flex: 0 0 100%;
  }
}

.download-button.windows {
  background: #00a4ef;
}

.download-button.windows:hover {
  background: #0090d5;
}

.download-button.linux {
  background: #E95420;
}

.download-button.linux:hover {
  background: #C7431B;
}

.download-options-group {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.download-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.85rem;
}

.download-option .format {
  color: rgba(255, 255, 255, 0.8);
  min-width: 65px;
}

.download-option .separator {
  color: rgba(255, 255, 255, 0.4);
  font-size: 0.7rem;
}

.download-option a {
  color: inherit;
  text-decoration: none;
  padding: 2px 6px;
  border-radius: 4px;
  transition: all 0.2s;
}

.download-option a:hover {
  background-color: rgba(255, 255, 255, 0.2);
  transform: translateY(-1px);
}

.download-button.linux .secondary {
  line-height: 1.6;
  font-size: 0.85rem;
}

.download-button.linux .secondary a {
  color: inherit;
  text-decoration: none;
  padding: 2px 6px;
  border-radius: 4px;
  transition: all 0.2s;
  white-space: nowrap;
}

.download-button.linux .secondary a:hover {
  background-color: rgba(255, 255, 255, 0.2);
  transform: translateY(-1px);
}

.download-button.linux .secondary .separator {
  opacity: 0.7;
  margin: 0 2px;
}

.downloads {
  padding: 2rem 0;
}

.download-container {
  max-width: 900px;
  margin: 0 auto;
  padding: 2rem;
  text-align: center;
}

.download-container h2 {
  font-size: 2.5rem;
  margin-bottom: 1rem;
  background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.download-subtitle {
  color: var(--text-secondary);
  margin-bottom: 3rem;
}

.download-options {
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
}

.download-group {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.platform-title {
  font-size: 1.2rem;
  color: var(--text-secondary);
  text-align: left;
  margin-bottom: 0.5rem;
}

.platform-options {
  display: flex;
  gap: 1rem;
  justify-content: center;
}

.download-button {
  background: var(--primary-color);
  color: white;
  padding: 1rem 1.5rem;
  border-radius: 12px;
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 1rem;
  transition: all 0.2s;
  border: 1px solid transparent;
  min-width: 250px;
}

.download-button:hover {
  transform: translateY(-2px);
  background: var(--secondary-color);
  box-shadow: 0 4px 12px rgba(99, 102, 241, 0.2);
}

.download-button .icon {
  font-size: 1.5rem;
}

.button-text {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  text-align: left;
}

.button-text .primary {
  font-weight: 600;
  font-size: 1rem;
}

.button-text .secondary {
  font-size: 0.85rem;
  opacity: 0.9;
}

.linux-options {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.format-group {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.format-label {
  font-size: 0.9rem;
  color: var(--text-secondary);
  text-align: left;
}

.format-buttons {
  display: flex;
  gap: 1rem;
  justify-content: center;
}

.format-buttons .download-button {
  min-width: 150px;
}

/* Dark mode adjustments */
[data-theme="dark"] .download-button {
  background: rgba(30, 41, 59, 0.8);
  border-color: rgba(255, 255, 255, 0.1);
}

[data-theme="dark"] .download-button:hover {
  border-color: var(--primary-color);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

.version-info {
  text-align: center;
  margin: 1rem 0;
  font-size: 0.9rem;
  color: var(--text-secondary);
}

#current-version {
  font-weight: 600;
  color: var(--primary-color);
}

/* Download Section Responsive Styles */
@media (max-width: 768px) {
  .download-options {
    flex-direction: column;
    gap: 2rem;
  }

  .download-group {
    width: 100%;
  }

  .platform-options {
    flex-direction: column;
  }

  .download-button {
    width: 100%;
    margin: 0.5rem 0;
  }

  .linux-downloads {
    width: 100%;
    margin-top: 1rem;
  }

  .linux-downloads h4 {
    margin-bottom: 0.5rem;
  }

  /* Adjust text size for better readability on mobile */
  .button-text .primary {
    font-size: 0.9rem;
  }

  .button-text .secondary {
    font-size: 0.8rem;
  }
}

/* Extra small devices */
@media (max-width: 480px) {
  .download-container {
    padding: 1rem;
  }

  .download-stats {
    font-size: 0.9rem;
  }

  .platform-title {
    font-size: 1.2rem;
  }
}

.download-stats {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  background: rgba(var(--primary-color-rgb), 0.1);
  border-radius: 6px;
  margin: 1rem 0;
  font-size: 0.9rem;
}

#download-count {
  font-weight: bold;
  color: var(--primary-color);
  font-size: 1.1rem;
}

/* Adjust the existing download-subtitle margin */
.download-subtitle {
  color: var(--text-secondary);
  margin-bottom: 2rem;
  margin-top: 0.5rem;
}

/* Testimonials Section */
.testimonials {
  padding: 4rem 0;
  background: var(--background);
  max-width: 1200px;
  margin: 0 auto;
}

.testimonial-grid {
  column-count: 3;
  column-gap: 2rem;
  margin-top: 3rem;
  padding: 0 2rem;
}

/* Ensure Twitter embeds stack properly */
.twitter-tweet {
  margin: 0 0 2rem 0 !important;
  width: 100% !important;
  break-inside: avoid;
  display: inline-block;
}

/* Responsive adjustments */
@media (max-width: 1024px) {
  .testimonial-grid {
    column-count: 2;
  }
}

@media (max-width: 768px) {
  .testimonial-grid {
    column-count: 1;
  }
}

/* Mobile Navigation Styles */
@media (max-width: 768px) {
  .glass-nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 2rem;
  }

  .menu-button {
    display: block;
    background: none;
    border: none;
    color: var(--text-primary);
    font-size: 1.5rem;
    cursor: pointer;
    padding: 0.5rem;
    z-index: 101;
  }

  .nav-links {
    position: fixed;
    top: calc(60px + 1rem);
    left: 0;
    right: 0;
    background: var(--card-background);
    padding: 1.5rem;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
    transform: translateY(-150%);
    transition: transform 0.3s ease;
    backdrop-filter: blur(12px);
    visibility: hidden;
    opacity: 0;
    z-index: 100;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  }

  .nav-links.active {
    transform: translateY(0);
    visibility: visible;
    opacity: 1;
  }
}

/* Hide menu button on larger screens */
@media (min-width: 769px) {
  .menu-button {
    display: none;
  }
}

================================================
FILE: jsconfig.json
================================================
{
  "extends": "./.svelte-kit/tsconfig.json",
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true,
    "moduleResolution": "bundler"
  }
  // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
  // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
  //
  // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
  // from the referenced tsconfig.json - TypeScript does not merge them in
}


================================================
FILE: package.json
================================================
{
  "name": "neohtop",
  "version": "1.2.0",
  "description": "",
  "type": "module",
  "scripts": {
    "dev": "vite dev",
    "build": "vite build",
    "preview": "vite preview",
    "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
    "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
    "tauri": "tauri",
    "format": "prettier --write ./src && cargo fmt --manifest-path src-tauri/Cargo.toml",
    "format:check": "prettier --check ./src && cargo fmt --manifest-path src-tauri/Cargo.toml -- --check",
    "prepare": "husky install"
  },
  "license": "MIT",
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^6.6.0",
    "@fortawesome/free-solid-svg-icons": "^6.6.0",
    "@tauri-apps/api": "^2.0.3",
    "@tauri-apps/plugin-os": "^2.0.0",
    "@tauri-apps/plugin-shell": "^2.2.1",
    "simple-icons": "^13.15.0",
    "svelte-fa": "^4.0.3"
  },
  "devDependencies": {
    "@sveltejs/adapter-static": "^3.0.5",
    "@sveltejs/kit": "^2.7.0",
    "@sveltejs/vite-plugin-svelte": "^5.0.3",
    "@tauri-apps/cli": "^2.0.4",
    "husky": "^8.0.0",
    "lint-staged": "^15.2.10",
    "prettier": "^3.3.3",
    "prettier-plugin-svelte": "^3.2.7",
    "svelte": "^5.0.0",
    "svelte-check": "^4.0.0",
    "typescript": "^5.5.0",
    "vite": "^6.3.5"
  },
  "lint-staged": {
    "src/**/*.{js,ts,jsx,tsx,svelte}": [
      "prettier --write"
    ],
    "src-tauri/**/*.rs": [
      "cargo fmt --manifest-path src-tauri/Cargo.toml --"
    ]
  }
}


================================================
FILE: src/App.svelte
================================================


================================================
FILE: src/app.css
================================================
:root {
  /* Default theme values will be overridden by theme store */
  --base: #1e1e2e;
  --mantle: #181825;
  --crust: #11111b;
  --text: #cdd6f4;
  --subtext0: #a6adc8;
  --subtext1: #bac2de;
  --surface0: #313244;
  --surface1: #45475a;
  --surface2: #585b70;
  --overlay0: #6c7086;
  --overlay1: #7f849c;
  --blue: #89b4fa;
  --lavender: #b4befe;
  --sapphire: #74c7ec;
  --sky: #89dceb;
  --red: #f38ba8;
  --maroon: #eba0ac;
  --peach: #fab387;
  --yellow: #f9e2af;
  --green: #a6e3a1;
  --teal: #94e2d5;
}

body {
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
    sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
  background-color: var(--base);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
  user-select: none;
}

/* Global scrollbar styles */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--surface2) var(--mantle);
}

::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: var(--mantle);
  border-radius: 4px;
}

::-webkit-scrollbar-thumb {
  background: var(--surface2);
  border-radius: 4px;
  transition: background 0.2s ease;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--surface1);
}

::-webkit-scrollbar-corner {
  background: var(--mantle);
}

/* Glassy theme */

[data-theme="glassy"] body {
  background: transparent !important;
}

[data-theme="glassy"] .toolbar {
  position: relative;
  background: rgba(24, 24, 37, 0.5) !important;
  border: 1px solid rgba(255, 255, 255, 0.1) !important;
  z-index: 9;
}

[data-theme="glassy"] .stat-panel {
  background: rgba(24, 24, 37, 0.2) !important;
  z-index: 100;
}

[data-theme="glassy"] .panel-header {
  border-color: rgba(255, 255, 255, 0.1) !important;
}

[data-theme="glassy"] .col-actions {
  background: rgba(24, 24, 37, 0.2) !important;
  border-bottom: 1px solid rgba(232, 232, 232, 0.1) !important;
  border-left: 1px solid rgba(232, 232, 232, 0.1) !important;
}

[data-theme="glassy"] .search-input,
[data-theme="glassy"] .btn-toggle,
[data-theme="glassy"] .btn-action,
[data-theme="glassy"] .info-button,
[data-theme="glassy"] .btn-page,
[data-theme="glassy"] .theme-button,
[data-theme="glassy"] .usage-pill,
[data-theme="glassy"] .bar-container,
[data-theme="glassy"] .select-input {
  background: rgba(24, 24, 37, 0.2) !important;
  z-index: 100;
}

[data-theme="glassy"] .btn-clear {
  z-index: 100;
}

[data-theme="glassy"] tr.pinned,
[data-theme="glassy"] tr:hover {
  background: rgba(24, 24, 37, 0.3) !important;
}

[data-theme="glassy"] th {
  background: rgba(24, 24, 37, 0.5) !important;
}

[data-theme="glassy"] td {
  border-bottom: 1px solid rgba(232, 232, 232, 0.1) !important;
  background: rgba(24, 24, 37, 0.2) !important;
}


================================================
FILE: src/app.html
================================================
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%sveltekit.assets%/favicon.png" />
    <meta name="viewport" content="width=device-width" />
    <title>NeoHtop</title>
    %sveltekit.head%
  </head>

  <body data-sveltekit-preload-data="hover">
    <div style="display: contents">%sveltekit.body%</div>
  </body>
</html>


================================================
FILE: src/lib/components/AppInfo.svelte
================================================
<script lang="ts">
  import { getVersion } from "@tauri-apps/api/app";
  import { onMount } from "svelte";
  import { ThemeSwitcher } from "$lib/components";
  import { faInfo } from "@fortawesome/free-solid-svg-icons";
  import Fa from "svelte-fa";
  import { ASCII_ART, APP_INFO } from "$lib/constants";

  let version = "";
  let latestVersion = "";
  let showInfo = false;
  let hasUpdate = false;

  async function checkLatestVersion() {
    try {
      const response = await fetch(
        "https://api.github.com/repos/abdenasser/neohtop/releases/latest",
      );
      const data = await response.json();

      // Extract version number from tag (e.g., "1.0.6" from "macos-nightly-1.0.6")
      const versionMatch = data.tag_name.match(/\d+\.\d+\.\d+/);
      if (!versionMatch) {
        console.warn(
          "Unexpected version format in latest release:",
          data.tag_name,
        );
        return;
      }

      latestVersion = versionMatch[0];

      // Extract version number from current version
      const currentVersionMatch = version.match(/\d+\.\d+\.\d+/);
      if (!currentVersionMatch) {
        console.warn("Unexpected current version format:", version);
        return;
      }

      // Compare only the version numbers
      hasUpdate = currentVersionMatch[0] !== latestVersion;
    } catch (error) {
      console.error("Failed to check latest version:", error);
      latestVersion = "";
      hasUpdate = false;
    }
  }

  onMount(async () => {
    try {
      version = await getVersion();
      await checkLatestVersion();
    } catch (error) {
      console.error("Failed to initialize version info:", error);
      version = "";
    }
  });
</script>

<div class="app-info">
  <ThemeSwitcher />
  <button
    class:info-button={true}
    class:has-update={hasUpdate}
    on:click={() => (showInfo = !showInfo)}
    aria-label="Toggle app info"
  >
    <span class="icon" class:update-available={hasUpdate}>
      <Fa icon={faInfo} />
    </span>
  </button>

  {#if showInfo}
    <!-- svelte-ignore a11y_no_static_element_interactions -->
    <div class="info-panel" on:mouseleave={() => (showInfo = false)}>
      <div class="info-content">
        <pre class="ascii-art">{ASCII_ART}</pre>
        <div class="details">
          <div class="detail-row">
            <span>NeoHtop v{version}</span>
            {#if hasUpdate}
              <a
                href={`https://github.com/abdenasser/neohtop/releases/latest`}
                class="update-button"
                target="_blank"
                rel="noopener noreferrer"
              >
                Update to v{latestVersion}
              </a>
            {/if}
          </div>
          <div class="detail-row">
            <span class="label">app</span>
            <span class="separator">::</span>
            <span class="value">{APP_INFO.name}</span>
          </div>
          <div class="detail-row">
            <span class="label">source</span>
            <span class="separator">::</span>
            <a
              href={APP_INFO.github}
              class="value"
              target="_blank"
              rel="noopener noreferrer"
            >
              {APP_INFO.github}
            </a>
          </div>
          <div class="detail-row">
            <span class="label">stack</span>
            <span class="separator">::</span>
            <span class="value">{APP_INFO.stack.join(", ")}</span>
          </div>
        </div>
      </div>
    </div>
  {/if}
</div>

<style>
  .app-info {
    display: flex;
    gap: 8px;
    position: relative;
  }

  .info-button,
  :global(.theme-button) {
    height: 28px;
    padding: 0 12px;
    font-size: 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
    box-sizing: border-box;
  }

  .info-button:hover,
  :global(.theme-button:hover) {
    background: var(--surface1);
  }

  .icon {
    display: inline-flex;
    align-items: center;
    font-size: 10px;
    color: var(--subtext0);
  }

  .icon.update-available {
    color: var(--red);
  }

  .info-panel {
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 4px;
    padding: 16px;
    background: var(--base);
    border: 1px solid var(--surface0);
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
    z-index: 100;
    min-width: 400px;
  }

  .info-content {
    display: flex;
    gap: 24px;
  }

  .ascii-art {
    font-family: monospace;
    font-size: 8px;
    line-height: 1;
    color: var(--mauve);
    margin: 0;
    padding: 0;
    white-space: pre;
  }

  .details {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 8px 0;
  }

  .detail-row {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: monospace;
    font-size: 13px;
  }

  .label {
    color: var(--green);
    min-width: 80px;
  }

  .separator {
    color: var(--subtext0);
  }

  .value {
    color: var(--text);
  }

  .detail-row span {
    color: var(--text);
    font-weight: 500;
  }

  .info-button.has-update {
    border-color: var(--red);
  }

  .info-button.has-update:hover {
    background: color-mix(in srgb, var(--red) 10%, transparent);
  }

  .info-button.has-update .icon {
    color: var(--red);
  }

  .update-button {
    display: inline-flex;
    align-items: center;
    padding: 4px 8px;
    font-size: 12px;
    color: var(--base);
    background: var(--red);
    border-radius: 4px;
    text-decoration: none;
    margin-left: 8px;
    transition: all 0.2s ease;
  }

  .update-button:hover {
    background: color-mix(in srgb, var(--red) 90%, white);
    transform: translateY(-1px);
  }
</style>


================================================
FILE: src/lib/components/ThemeSwitcher.svelte
================================================
<script lang="ts">
  import { themeStore } from "$lib/stores";
  import { overlayStore } from "$lib/stores/overlay";
  import { themes } from "$lib/definitions";
  import Fa from "svelte-fa";
  import {
    faChevronDown,
    faChevronRight,
    faChevronLeft,
  } from "@fortawesome/free-solid-svg-icons";
  import { platform } from "@tauri-apps/plugin-os";
  import { THEME_GROUPS } from "$lib/constants";
  import { onDestroy } from "svelte";

  let containerElement: HTMLDivElement;
  let overlayElement: HTMLDivElement;
  let optionsContainer: HTMLDivElement;
  let canScrollLeft = false;
  let canScrollRight = false;

  $: showMenu = $overlayStore === "theme";

  const themeGroups = [
    ...THEME_GROUPS,
    ...(platform() === "windows" || platform() === "macos"
      ? [
          {
            label: "Glassy",
            themes: ["glassy"],
          },
        ]
      : []),
  ];

  function updateOverlayPosition() {
    if (overlayElement && containerElement) {
      const toolbarContent = containerElement.closest(".toolbar-content");
      if (toolbarContent) {
        const toolbarRect = toolbarContent.getBoundingClientRect();
        const containerRect = containerElement.getBoundingClientRect();

        const leftOffset = containerRect.left - toolbarRect.left;
        const rightOffset = toolbarRect.right - containerRect.right;
        const topOffset = containerRect.top - toolbarRect.top;

        overlayElement.style.left = `-${leftOffset}px`;
        overlayElement.style.right = `-${rightOffset}px`;
        overlayElement.style.top = `-${topOffset}px`;
      }
    }
  }

  function updateScrollButtons() {
    if (optionsContainer) {
      canScrollLeft = optionsContainer.scrollLeft > 0;
      canScrollRight =
        optionsContainer.scrollLeft <
        optionsContainer.scrollWidth - optionsContainer.clientWidth;
    }
  }

  function scrollLeft() {
    if (optionsContainer) {
      optionsContainer.scrollBy({ left: -200, behavior: "smooth" });
      setTimeout(updateScrollButtons, 100);
    }
  }

  function scrollRight() {
    if (optionsContainer) {
      optionsContainer.scrollBy({ left: 200, behavior: "smooth" });
      setTimeout(updateScrollButtons, 100);
    }
  }

  function toggleThemeMenu(event: Event) {
    event.stopPropagation();
    if (showMenu) {
      overlayStore.close();
    } else {
      overlayStore.open("theme");
      setTimeout(() => {
        updateOverlayPosition();
        updateScrollButtons();
      }, 0);
    }
  }

  function selectTheme(themeName: string) {
    themeStore.setTheme(themeName);
    overlayStore.close();
  }

  function handleClickOutside(event: MouseEvent) {
    if (
      showMenu &&
      containerElement &&
      !containerElement.contains(event.target as Node)
    ) {
      overlayStore.close();
    }
  }

  function setupClickOutside() {
    if (typeof document !== "undefined") {
      document.addEventListener("click", handleClickOutside);
    }
  }

  function cleanupClickOutside() {
    if (typeof document !== "undefined") {
      document.removeEventListener("click", handleClickOutside);
    }
  }

  $: if (showMenu) {
    setTimeout(setupClickOutside, 0);
  } else {
    cleanupClickOutside();
  }

  onDestroy(() => {
    cleanupClickOutside();
  });
</script>

<div
  class="theme-switcher"
  class:active={showMenu}
  bind:this={containerElement}
>
  <button
    class="theme-button"
    class:active={showMenu}
    on:click={toggleThemeMenu}
    aria-label="Toggle theme menu"
  >
    <div class="current-theme">
      <div class="theme-preview" style:background={$themeStore.colors.base}>
        <div
          class="preview-color"
          style:background={$themeStore.colors.blue}
        ></div>
        <div
          class="preview-color"
          style:background={$themeStore.colors.red}
        ></div>
        <div
          class="preview-color"
          style:background={$themeStore.colors.green}
        ></div>
      </div>
    </div>
    <span class="icon">
      {#if showMenu}
        <Fa icon={faChevronDown} />
      {:else}
        <Fa icon={faChevronRight} />
      {/if}
    </span>
  </button>

  {#if showMenu}
    <div
      class="touchbar-full-overlay"
      bind:this={overlayElement}
      on:click={() => overlayStore.close()}
      on:keydown={(e) => e.key === "Escape" && overlayStore.close()}
      role="dialog"
      aria-label="Theme selection"
      tabindex="-1"
    >
      {#if canScrollLeft}
        <button
          class="scroll-chevron scroll-left"
          on:click|stopPropagation={scrollLeft}
        >
          <Fa icon={faChevronLeft} />
        </button>
      {/if}

      <div
        class="touchbar-horizontal-options"
        bind:this={optionsContainer}
        on:scroll={updateScrollButtons}
      >
        {#each themeGroups as group}
          {#each group.themes as themeName}
            {@const theme = themes[themeName]}
            <button
              class="touchbar-option"
              class:active={$themeStore.name === theme.name}
              on:click|stopPropagation={() => selectTheme(theme.name)}
              title={theme.label}
            >
              <div class="theme-preview" style:background={theme.colors.base}>
                <div
                  class="preview-color"
                  style:background={theme.colors.blue}
                ></div>
                <div
                  class="preview-color"
                  style:background={theme.colors.red}
                ></div>
                <div
                  class="preview-color"
                  style:background={theme.colors.green}
                ></div>
              </div>
              <span class="theme-label">{theme.label}</span>
            </button>
          {/each}
        {/each}
      </div>

      {#if canScrollRight}
        <button
          class="scroll-chevron scroll-right"
          on:click|stopPropagation={scrollRight}
        >
          <Fa icon={faChevronRight} />
        </button>
      {/if}
    </div>
  {/if}
</div>

<style>
  .theme-switcher {
    position: relative;
  }

  .theme-button {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 0 12px;
    height: 28px;
    font-size: 12px;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
    box-sizing: border-box;
  }

  .theme-button:hover {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .theme-button.active {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .icon {
    font-size: 10px;
    color: var(--subtext0);
  }

  .touchbar-full-overlay {
    position: absolute;
    top: -0px;
    height: 44px;
    background: var(--mantle);
    border: none;
    border-radius: 0;
    box-shadow: none;
    z-index: 1000;
    display: flex;
    align-items: center;
    padding: 0 8px;
    gap: 8px;
  }

  .scroll-chevron {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    color: var(--text);
    cursor: pointer;
    transition: all 0.15s ease;
    flex-shrink: 0;
    font-size: 10px;
    animation: optionSlideIn 0.2s ease-out;
    animation-fill-mode: both;
    animation-delay: 0ms;
  }

  .scroll-chevron:hover {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-horizontal-options {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
    padding: 0 4px;
  }

  .touchbar-horizontal-options::-webkit-scrollbar {
    display: none;
  }

  .touchbar-option {
    padding: 0 8px;
    height: 28px;
    font-size: 12px;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-width: fit-content;
    box-sizing: border-box;
    animation: optionSlideIn 0.2s ease-out;
    animation-fill-mode: both;
  }

  .touchbar-option:nth-child(1) {
    animation-delay: 20ms;
  }
  .touchbar-option:nth-child(2) {
    animation-delay: 40ms;
  }
  .touchbar-option:nth-child(3) {
    animation-delay: 60ms;
  }
  .touchbar-option:nth-child(4) {
    animation-delay: 80ms;
  }
  .touchbar-option:nth-child(5) {
    animation-delay: 100ms;
  }
  .touchbar-option:nth-child(6) {
    animation-delay: 120ms;
  }
  .touchbar-option:nth-child(7) {
    animation-delay: 140ms;
  }
  .touchbar-option:nth-child(8) {
    animation-delay: 160ms;
  }
  .touchbar-option:nth-child(9) {
    animation-delay: 180ms;
  }
  .touchbar-option:nth-child(10) {
    animation-delay: 200ms;
  }

  @keyframes optionSlideIn {
    from {
      opacity: 0;
      transform: translateY(-8px) scale(0.9);
    }
    to {
      opacity: 1;
      transform: translateY(0) scale(1);
    }
  }

  .touchbar-option:hover:not(.disabled) {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-option.active {
    background: var(--blue);
    color: var(--base);
    border-color: var(--blue);
  }

  .theme-preview {
    display: flex;
    gap: 2px;
    padding: 2px;
    border-radius: 3px;
    border: 1px solid var(--surface1);
  }

  .preview-color {
    width: 8px;
    height: 8px;
    border-radius: 1px;
  }

  .theme-label {
    font-size: 12px;
  }

  .current-theme {
    display: flex;
    align-items: center;
    gap: 8px;
  }
</style>


================================================
FILE: src/lib/components/TitleBar.svelte
================================================
<script lang="ts">
</script>

<div class="title-bar" data-tauri-drag-region>
  <div class="title">
    <img src="/32x32.png" alt="NeoHtop" class="app-icon" />
    <div class="neon">NeoHtop</div>
  </div>
</div>

<style>
  .title-bar {
    height: 32px;
    /* background: var(--mantle); */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 12px;
    -webkit-user-select: none;
    user-select: none;
    position: relative;
    overflow: hidden;
  }

  .title {
    display: flex;
    align-items: center;
    position: relative;
    height: 100%;
  }

  .neon {
    font-family: "Courier New", monospace;
    font-size: 14px;
    font-weight: bold;
    color: var(--text);
  }

  .app-icon {
    width: 24px;
    height: 24px;
    margin-right: 4px;
    display: flex;
    align-items: center;
  }
</style>


================================================
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
================================================
<script lang="ts">
  import { Modal } from "$lib/components";

  interface Process {
    pid: number;
    name: string;
  }

  export let show = false;
  export let process: Process | null = null;
  export let onClose: () => void;
  export let onConfirm: () => Promise<void>;
  export let isKilling = false;
</script>

<Modal {show} title="Confirm Action" maxWidth="400px" {onClose}>
  {#if process}
    <div class="confirm-content">
      <p class="confirm-message">Are you sure you want to end this process?</p>
      <div class="process-info">
        <span class="process-name">{process.name}</span>
        <span class="process-pid">(PID: {process.pid})</span>
      </div>
      <div class="confirm-actions">
        <button class="btn-secondary" on:click={onClose} disabled={isKilling}>
          Cancel
        </button>
        <button class="btn-danger" on:click={onConfirm} disabled={isKilling}>
          {#if isKilling}
            <div class="spinner"></div>
            <span>Ending...</span>
          {:else}
            End Process
          {/if}
        </button>
      </div>
    </div>
  {/if}
</Modal>

<style>
  .confirm-content {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  .confirm-message {
    color: var(--text);
    margin: 0;
    font-size: 14px;
  }

  .process-info {
    background: var(--mantle);
    padding: 12px;
    border-radius: 6px;
  }

  .process-name {
    color: var(--text);
    font-weight: 500;
    font-size: 14px;
  }

  .process-pid {
    color: var(--subtext0);
    font-size: 12px;
    margin-left: 8px;
  }

  .confirm-actions {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
  }

  .btn-secondary {
    padding: 8px 16px;
    font-size: 13px;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
  }

  .btn-secondary:hover {
    background: var(--surface1);
  }

  .btn-danger {
    padding: 8px 16px;
    font-size: 13px;
    color: var(--base);
    background: var(--red);
    border: none;
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
    display: inline-flex;
    align-items: center;
    gap: 8px;
  }

  .btn-danger:disabled {
    opacity: 0.7;
    cursor: not-allowed;
  }

  .btn-danger:hover {
    background: color-mix(in srgb, var(--red) 90%, white);
  }

  .spinner {
    width: 16px;
  }
</style>


================================================
FILE: src/lib/components/modals/Modal.svelte
================================================
<script lang="ts">
  export let show = false;
  export let maxWidth = "600px";
  export let title: string;
  export let onClose: () => void;
</script>

{#if show}
  <!-- svelte-ignore a11y_click_events_have_key_events -->
  <!-- svelte-ignore a11y_no_static_element_interactions -->
  <div class="modal-backdrop" on:click={onClose}>
    <div class="modal" on:click|stopPropagation style="--max-width: {maxWidth}">
      <div class="modal-header">
        <h2>{title}</h2>
        <button class="btn-close" on:click={onClose}>×</button>
      </div>
      <div class="modal-content">
        <slot />
      </div>
    </div>
  </div>
{/if}

<style>
  .modal-backdrop {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
  }

  .modal {
    background: var(--base);
    border-radius: 8px;
    width: 100%;
    max-width: var(--max-width);
    max-height: 90vh;
    overflow: auto;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
  }

  .modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px;
    border-bottom: 1px solid var(--surface0);
  }

  .modal-header h2 {
    margin: 0;
    font-size: 18px;
    color: var(--text);
  }

  .btn-close {
    background: transparent;
    border: none;
    color: var(--overlay0);
    font-size: 24px;
    cursor: pointer;
    padding: 4px 8px;
  }

  .btn-close:hover {
    color: var(--text);
  }

  .modal-content {
    padding: 16px;
  }
</style>


================================================
FILE: src/lib/components/modals/ProcessDetailsModal.svelte
================================================
<script lang="ts">
  import { Modal } from "$lib/components";
  import { formatBytes } from "$lib/utils";
  import type { Process } from "$lib/types";
  import Fa from "svelte-fa";
  import {
    faMemory,
    faMicrochip,
    faCodeFork,
    faTerminal,
    faList,
  } from "@fortawesome/free-solid-svg-icons";

  export let show = false;
  export let process: Process | null = null;
  export let onClose: () => void;
  export let processes: Process[] = [];
  export let onShowDetails: (process: Process) => void;

  $: childProcesses = process
    ? processes.filter((p) => p.ppid === process.pid)
    : [];
</script>

<Modal
  {show}
  title={`${process ? process.name.slice(0, 10) : "Unknown Process"} - Process Details`}
  maxWidth="1000px"
  {onClose}
>
  {#if process}
    <div class="modal-content">
      <!-- Header Stats -->
      <div class="header-stats">
        <div class="stat-item">
          <div class="stat-label">PID</div>
          <div class="stat-value">{process.pid}</div>
        </div>
        <div class="stat-item">
          <div class="stat-label">Status</div>
          <div
            class="stat-value status"
            class:running={process.status === "Running"}
          >
            {process.status}
          </div>
        </div>
        <div class="stat-item">
          <div class="stat-label">CPU</div>
          <div class="stat-value">{process.cpu_usage.toFixed(1)}%</div>
        </div>
        <div class="stat-item">
          <div class="stat-label">Memory</div>
          <div class="stat-value">{formatBytes(process.memory_usage)}</div>
        </div>
      </div>

      <!-- Main Content -->
      <div class="content-grid">
        <!-- Left Column -->
        <div class="content-column">
          <!-- Process Info -->
          <div class="card">
            <div class="card-header">
              <Fa icon={faMicrochip} />
              <span>Process Information</span>
            </div>
            <div class="card-content">
              <div class="info-grid">
                <div class="info-item">
                  <span class="info-label">Name</span>
                  <span class="info-value">{process.name}</span>
                </div>
                <div class="info-item">
                  <span class="info-label">User</span>
                  <span class="info-value">{process.user}</span>
                </div>
                <div class="info-item">
                  <span class="info-label">Parent PID</span>
                  <!-- svelte-ignore a11y_click_events_have_key_events -->
                  <!-- svelte-ignore a11y_no_static_element_interactions -->
                  <span
                    class="info-value clickable"
                    on:click={() => {
                      const parent = processes.find(
                        (p) => p.pid === process.ppid,
                      );
                      if (parent) onShowDetails(parent);
                    }}
                  >
                    {process.ppid}
                  </span>
                </div>
                <div class="info-item">
                  <span class="info-label">Session ID</span>
                  <span class="info-value">{process.session_id}</span>
                </div>
              </div>
            </div>
          </div>

          <!-- Resource Usage -->
          <div class="card">
            <div class="card-header">
              <Fa icon={faMemory} />
              <span>Resource Usage</span>
            </div>
            <div class="card-content">
              <div class="resource-grid">
                <div class="resource-item">
                  <div class="resource-header">
                    <span>CPU Usage</span>
                    <span class="resource-value"
                      >{process.cpu_usage.toFixed(1)}%</span
                    >
                  </div>
                  <div class="progress-bar">
                    <div
                      class="progress-fill"
                      style="width: {process.cpu_usage}%"
                      class:high={process.cpu_usage > 50}
                      class:critical={process.cpu_usage > 80}
                    ></div>
                  </div>
                </div>
                <div class="resource-item">
                  <div class="resource-header">
                    <span>Memory Usage</span>
                  </div>
                  <div class="memory-stats">
                    <div>Physical: {formatBytes(process.memory_usage)}</div>
                    <div>Virtual: {formatBytes(process.virtual_memory)}</div>
                  </div>
                </div>
                <div class="resource-item">
                  <div class="resource-header">
                    <span>Disk I/O</span>
                  </div>
                  <div class="disk-stats">
                    <div>Read: {formatBytes(process.disk_usage[0])}</div>
                    <div>Written: {formatBytes(process.disk_usage[1])}</div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>

        <!-- Right Column -->
        <div class="content-column">
          <!-- Command -->
          <div class="card">
            <div class="card-header">
              <Fa icon={faTerminal} />
              <span>Command</span>
            </div>
            <div class="card-content">
              <div class="command-text">{process.command}</div>
              <div class="path-text">{process.root}</div>
            </div>
          </div>

          <!-- Child Processes -->
          {#if childProcesses.length > 0}
            <div class="card">
              <div class="card-header">
                <Fa icon={faCodeFork} />
                <span>Child Processes ({childProcesses.length})</span>
              </div>
              <div class="card-content">
                <table class="process-table">
                  <thead>
                    <tr>
                      <th>Name</th>
                      <th>PID</th>
                      <th>CPU</th>
                      <th>Memory</th>
                    </tr>
                  </thead>
                  <tbody>
                    {#each childProcesses as child}
                      <tr
                        class="clickable"
                        on:click={() => onShowDetails(child)}
                      >
                        <td>{child.name}</td>
                        <td>{child.pid}</td>
                        <td>{child.cpu_usage.toFixed(1)}%</td>
                        <td>{formatBytes(child.memory_usage)}</td>
                      </tr>
                    {/each}
                  </tbody>
                </table>
              </div>
            </div>
          {/if}

          <!-- Environment Variables -->
          {#if process.environ.length > 0}
            <div class="card">
              <div class="card-header">
                <Fa icon={faList} />
                <span>Environment Variables</span>
              </div>
              <div class="card-content">
                <div class="env-list">
                  {#each process.environ as env}
                    <div class="env-item">{env}</div>
                  {/each}
                </div>
              </div>
            </div>
          {/if}
        </div>
      </div>
    </div>
  {/if}
</Modal>

<style>
  /* Base Modal Content */
  .modal-content {
    display: flex;
    flex-direction: column;
    gap: 24px;
    font-size: 13px;
    color: var(--text);
  }

  /* Header Stats */
  .header-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 16px;
    padding: 16px;
    background: var(--surface0);
    border-radius: 8px;
  }

  .stat-item {
    display: flex;
    flex-direction: column;
    gap: 4px;
  }

  .stat-label {
    font-size: 12px;
    color: var(--subtext0);
    font-weight: 500;
  }

  .stat-value {
    font-size: 16px;
    font-weight: 600;
  }

  .stat-value.status {
    color: var(--subtext0);
  }

  .stat-value.status.running {
    color: var(--green);
  }

  /* Main Content Grid */
  .content-grid {
    display: grid;
    grid-template-columns: minmax(300px, 0.4fr) minmax(400px, 0.6fr);
    gap: 24px;
  }

  .content-column {
    display: flex;
    flex-direction: column;
    gap: 24px;
    min-width: 0; /* Prevent overflow issues */
  }

  /* Cards */
  .card {
    background: var(--surface0);
    border-radius: 8px;
    overflow: hidden;
    min-width: 0; /* Prevent overflow issues */
  }

  .card-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 16px;
    background: var(--surface1);
    color: var(--subtext0);
    font-weight: 500;
  }

  .card-header :global(svg) {
    width: 14px;
    height: 14px;
    color: var(--blue);
  }

  .card-content {
    padding: 16px;
    overflow: auto;
  }

  /* Info Grid */
  .info-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 16px;
  }

  .info-item {
    display: flex;
    flex-direction: column;
    gap: 4px;
  }

  .info-label {
    color: var(--subtext0);
    font-size: 12px;
  }

  .info-value {
    color: var(--text);
  }

  .info-value.clickable {
    cursor: pointer;
    color: var(--blue);
  }

  .info-value.clickable:hover {
    text-decoration: underline;
  }

  /* Resource Usage */
  .resource-grid {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  .resource-item {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }

  .resource-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    color: var(--subtext0);
    font-size: 12px;
  }

  .resource-value {
    color: var(--text);
  }

  /* Progress Bar */
  .progress-bar {
    height: 6px;
    background: var(--surface1);
    border-radius: 3px;
    overflow: hidden;
  }

  .progress-fill {
    height: 100%;
    background: var(--blue);
    transition: width 0.2s ease;
  }

  .progress-fill.high {
    background: var(--yellow);
  }

  .progress-fill.critical {
    background: var(--red);
  }

  /* Memory and Disk Stats */
  .memory-stats,
  .disk-stats {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    color: var(--text);
  }

  /* Command and Path */
  .command-text,
  .path-text {
    word-break: break-all;
    white-space: pre-wrap;
  }

  .path-text {
    margin-top: 8px;
    font-size: 12px;
    color: var(--subtext0);
  }

  /* Process Table */
  .process-table {
    width: 100%;
    border-collapse: collapse;
  }

  .process-table th {
    text-align: left;
    padding: 8px;
    color: var(--subtext0);
    font-weight: 500;
    border-bottom: 1px solid var(--surface1);
  }

  .process-table td {
    padding: 8px;
    border-bottom: 1px solid var(--surface1);
  }

  .process-table tr:last-child td {
    border-bottom: none;
  }

  .process-table tr.clickable {
    cursor: pointer;
  }

  .process-table tr.clickable:hover {
    background: var(--surface1);
  }

  /* Environment Variables */
  .env-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    max-height: 200px;
    overflow-y: auto;
    margin: -16px;
    padding: 16px;
  }

  .env-item {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
      "Liberation Mono", "Courier New", monospace;
    padding: 4px 8px;
    border-radius: 4px;
    color: var(--subtext1);
    font-size: 12px;
  }

  .env-item:hover {
    background: var(--surface1);
  }

  /* Update scrollbar styles to match the container edges */
  .env-list::-webkit-scrollbar {
    width: 8px;
    height: 8px;
  }

  .env-list::-webkit-scrollbar-track {
    background: var(--surface0);
    border-radius: 0;
  }

  .env-list::-webkit-scrollbar-thumb {
    background: var(--surface2);
    border-radius: 4px;
    border: 2px solid var(--surface0);
  }

  .env-list::-webkit-scrollbar-thumb:hover {
    background: var(--surface1);
  }

  /* Scrollbar Styles */
  :global(.modal-content *::-webkit-scrollbar) {
    width: 8px;
    height: 8px;
  }

  :global(.modal-content *::-webkit-scrollbar-track) {
    background: var(--mantle);
    border-radius: 4px;
  }

  :global(.modal-content *::-webkit-scrollbar-thumb) {
    background: var(--surface2);
    border-radius: 4px;
  }

  :global(.modal-content *::-webkit-scrollbar-thumb:hover) {
    background: var(--surface1);
  }

  /* Responsive Design */
  @media (max-width: 900px) {
    .content-grid {
      grid-template-columns: 1fr;
    }

    .header-stats {
      grid-template-columns: repeat(2, 1fr);
    }
  }
</style>


================================================
FILE: src/lib/components/modals/index.ts
================================================
export { default as Modal } from "./Modal.svelte";
export { default as ProcessDetailsModal } from "./ProcessDetailsModal.svelte";
export { default as KillProcessModal } from "./KillProcessModal.svelte";


================================================
FILE: src/lib/components/process/ActionButtons.svelte
================================================
<script lang="ts">
  import {
    faThumbtack,
    faInfoCircle,
    faXmark,
  } from "@fortawesome/free-solid-svg-icons";
  import Fa from "svelte-fa";
  import type { Process } from "$lib/types";

  export let process: Process;
  export let isPinned: boolean;
  export let onTogglePin: (command: string) => void;
  export let onShowDetails: (process: Process) => void;
  export let onKillProcess: (process: Process) => void;
</script>

<td class="col-actions">
  <div class="action-buttons">
    <button
      class="btn-action pin-btn"
      class:pinned={isPinned}
      on:click={() => onTogglePin(process.command)}
      title={isPinned ? "Unpin" : "Pin"}
    >
      <Fa icon={faThumbtack} />
    </button>
    <button
      class="btn-action info-btn"
      on:click={() => onShowDetails(process)}
      title="Show Details"
    >
      <Fa icon={faInfoCircle} />
    </button>
    <button
      class="btn-action kill-btn"
      on:click={() => onKillProcess(process)}
      title="End Process"
    >
      <Fa icon={faXmark} />
    </button>
  </div>
</td>

<style>
  td {
    padding: 6px 12px;
    border-bottom: 1px solid var(--surface0);
    color: var(--text);
    z-index: 1;
  }
  .col-actions {
    position: sticky;
    right: 0;
    z-index: 2;
    background: var(--base);
    border-left: 1px solid var(--surface0);
    width: 120px;
  }

  .action-buttons {
    display: flex;
    gap: 4px;
    align-items: center;
    justify-content: center;
  }

  .btn-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    cursor: pointer;
    background: transparent;
    border-radius: 6px;
    width: 28px;
    height: 28px;
    transition: all 0.2s ease;
    position: relative;
    overflow: hidden;
  }

  .btn-action::before {
    content: "";
    position: absolute;
    inset: 0;
    opacity: 0.1;
    transition: opacity 0.2s ease;
  }

  .btn-action:hover::before {
    opacity: 0.15;
  }

  .pin-btn {
    color: var(--sapphire);
  }

  .pin-btn::before {
    background: var(--sapphire);
  }

  .pin-btn.pinned {
    color: var(--blue);
    transform: rotate(45deg);
  }

  .pin-btn.pinned::before {
    background: var(--blue);
    opacity: 0.15;
  }

  .info-btn {
    color: var(--lavender);
  }

  .info-btn::before {
    background: var(--lavender);
  }

  .kill-btn {
    color: var(--red);
    border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
  }

  .kill-btn:hover {
    color: var(--base);
    background: var(--red);
  }

  .kill-btn:hover::before {
    opacity: 1;
  }

  .btn-action:hover {
    box-shadow: 0 0 12px color-mix(in srgb, currentColor 20%, transparent);
  }

  .btn-action:active {
    transform: translateY(1px);
  }

  .pin-btn.pinned:active {
    transform: rotate(45deg) translateY(1px);
  }

  .btn-action:focus {
    outline: none;
    box-shadow: 0 0 0 2px color-mix(in srgb, currentColor 30%, transparent);
  }

  .btn-action:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .btn-action:disabled:hover {
    transform: none;
    box-shadow: none;
  }

  .btn-action:disabled::before {
    display: none;
  }
</style>


================================================
FILE: src/lib/components/process/ProcessIcon.svelte
================================================
<script lang="ts">
  import * as SimpleIcons from "simple-icons";

  export let processName: string;
  export let size: number = 16;

  function handleImageError(event: Event) {
    const img = event.target as HTMLImageElement;
    img.src = getIconForProcess("default");
    img.onerror = null;
  }

  function getIconForProcess(name: string): string {
    // First try with com.company.something pattern
    if (name.startsWith("com.")) {
      const companyName = name.replace(/^com\.([^.]+)\..*$/, "$1");
      const formattedCompanyName =
        companyName.charAt(0).toUpperCase() + companyName.slice(1);
      const companyIconKey = `si${formattedCompanyName}`;
      const companyIcon =
        SimpleIcons[companyIconKey as keyof typeof SimpleIcons];

      if (companyIcon) {
        // Use theme color instead of brand color
        const color = getComputedStyle(document.documentElement)
          .getPropertyValue("--text")
          .trim();
        const svg =
          typeof companyIcon === "object" && "svg" in companyIcon
            ? companyIcon.svg
            : "";
        const svgWithColor = svg.replace("<svg", `<svg fill="${color}"`);
        return `data:image/svg+xml;base64,${btoa(svgWithColor)}`;
      }
    }

    // If no company icon found, fall back to original implementation
    const cleanName = name
      .replace(/\.(app|exe)$/i, "")
      .replace(/[-_./\\]/g, " ")
      .split(" ")[0]
      .trim()
      .toLowerCase();

    const formattedName =
      cleanName.charAt(0).toUpperCase() + cleanName.slice(1);
    const iconKey = `si${formattedName}`;
    let simpleIcon = SimpleIcons[iconKey as keyof typeof SimpleIcons];

    // Default icon if no match found
    if (!simpleIcon) {
      simpleIcon = SimpleIcons.siGhostery;
    }

    // Use theme color instead of brand color
    const color = getComputedStyle(document.documentElement)
      .getPropertyValue("--text")
      .trim();

    const svg =
      typeof simpleIcon === "object" && "svg" in simpleIcon
        ? simpleIcon.svg
        : "";
    const svgWithColor = svg.replace("<svg", `<svg fill="${color}"`);
    return `data:image/svg+xml;base64,${btoa(svgWithColor)}`;
  }
</script>

<img
  class="process-icon"
  src={getIconForProcess(processName)}
  alt=""
  height={size}
  width={size}
  on:error={handleImageError}
/>

<style>
  .process-icon {
    width: 16px;
    height: 16px;
    object-fit: contain;
  }
</style>


================================================
FILE: src/lib/components/process/ProcessRow.svelte
================================================
<script lang="ts">
  import type { Process, Column } from "$lib/types";
  import { ProcessIcon, ActionButtons } from "$lib/components";

  export let process: Process;
  export let columns: Column[];
  export let isPinned: boolean;
  export let isHighUsage: boolean;

  export let onTogglePin: (command: string) => void;
  export let onShowDetails: (process: Process) => void;
  export let onKillProcess: (process: Process) => void;
</script>

<tr class:high-usage={isHighUsage} class:pinned={isPinned}>
  {#each columns.filter((col) => col.visible) as column}
    <td class="truncate">
      {#if column.id === "name"}
        <div class="name-cell">
          <ProcessIcon processName={process.name} />
          <span class="process-name">{process.name}</span>
        </div>
      {:else if column.format}
        {@html column.format(process[column.id])}
      {:else}
        {process[column.id]}
      {/if}
    </td>
  {/each}
  <ActionButtons
    {process}
    {isPinned}
    {onTogglePin}
    {onShowDetails}
    {onKillProcess}
  />
</tr>

<style>
  td {
    padding: 6px 12px;
    border-bottom: 1px solid var(--surface0);
    color: var(--text);
    z-index: 1;
  }

  .truncate {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 0;
  }

  tr:hover {
    background-color: var(--surface0);
  }

  .high-usage {
    background-color: color-mix(in srgb, var(--red) 10%, transparent);
  }

  .high-usage:hover {
    background-color: color-mix(in srgb, var(--red) 15%, transparent);
  }

  tr.pinned {
    background-color: color-mix(in srgb, var(--blue) 10%, transparent);
  }

  tr.pinned:hover {
    background-color: color-mix(in srgb, var(--blue) 15%, transparent);
  }

  .name-cell {
    display: flex;
    align-items: center;
    gap: 8px;
  }
</style>


================================================
FILE: src/lib/components/process/ProcessTable.svelte
================================================
<script lang="ts">
  import type { Process, Column } from "$lib/types";
  import { TableHeader, ProcessRow } from "$lib/components";

  export let processes: Process[];
  export let columns: Column[];
  export let systemStats: { memory_total: number } | null;
  export let sortConfig: { field: keyof Process; direction: "asc" | "desc" };
  export let pinnedProcesses: Set<string>;

  export let onToggleSort: (field: keyof Process) => void;
  export let onTogglePin: (command: string) => void;
  export let onShowDetails: (process: Process) => void;
  export let onKillProcess: (process: Process) => void;
</script>

<div class="table-container">
  <table>
    <TableHeader {columns} {sortConfig} {onToggleSort} />
    <tbody>
      {#each processes as process (process.pid)}
        <ProcessRow
          {process}
          {columns}
          isPinned={pinnedProcesses.has(process.command)}
          isHighUsage={process.cpu_usage > 50 ||
            process.memory_usage / (systemStats?.memory_total || 0) > 0.1}
          {onTogglePin}
          {onShowDetails}
          {onKillProcess}
        />
      {/each}
    </tbody>
  </table>
</div>

<style>
  .table-container {
    flex: 1;
    overflow-x: auto;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--surface2) var(--mantle);
  }

  .table-container::-webkit-scrollbar {
    width: 8px;
    height: 8px;
  }

  .table-container::-webkit-scrollbar-track {
    background: var(--mantle);
    border-radius: 4px;
  }

  .table-container::-webkit-scrollbar-thumb {
    background: var(--surface2);
    border-radius: 4px;
    transition: background 0.2s ease;
  }

  .table-container::-webkit-scrollbar-thumb:hover {
    background: var(--surface1);
  }

  .table-container::-webkit-scrollbar-corner {
    background: var(--mantle);
  }

  table {
    width: max-content;
    min-width: 100%;
    table-layout: fixed;
    border-collapse: collapse;
    font-size: 13px;
  }
</style>


================================================
FILE: src/lib/components/process/TableHeader.svelte
================================================
<script lang="ts">
  import type { Process, Column } from "$lib/types";

  export let columns: Column[];
  export let sortConfig: { field: keyof Process; direction: "asc" | "desc" };
  export let onToggleSort: (field: keyof Process) => void;

  function getSortIndicator(field: keyof Process) {
    if (sortConfig.field !== field) return "↕";
    return sortConfig.direction === "asc" ? "↑" : "↓";
  }
</script>

<thead>
  <tr>
    {#each columns.filter((col) => col.visible) as column}
      <th class="sortable" on:click={() => onToggleSort(column.id)}>
        <div class="th-content">
          {column.label}
          <span
            class="sort-indicator"
            class:active={sortConfig.field === column.id}
          >
            {getSortIndicator(column.id)}
          </span>
        </div>
      </th>
    {/each}
    <th>Actions</th>
  </tr>
</thead>

<style>
  th {
    position: sticky;
    top: 0;
    background: var(--mantle);
    text-align: left;
    padding: 8px 12px;
    font-weight: 500;
    color: var(--subtext0);
    border-bottom: 1px solid var(--surface0);
    transition: background-color 0.2s ease;
    z-index: 3;
  }

  th:last-child {
    width: 120px;
    min-width: 120px;
    max-width: 120px;
  }

  .sortable {
    cursor: pointer;
    user-select: none;
  }

  .th-content {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .sort-indicator {
    color: var(--overlay0);
    font-size: 12px;
    opacity: 0.5;
    transition: all 0.2s ease;
  }

  .sort-indicator.active {
    color: var(--blue);
    opacity: 1;
  }

  .sortable:hover .sort-indicator {
    opacity: 1;
  }
</style>


================================================
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
================================================
<script lang="ts">
  import { faMicrochip } from "@fortawesome/free-solid-svg-icons";
  import { PanelHeader, ProgressBar } from "$lib/components";
  import { formatPercentage } from "$lib/utils";

  export let cpuUsage: number[];

  $: averageUsage = formatPercentage(
    cpuUsage.reduce((a, b) => a + b, 0) / cpuUsage.length,
  );
</script>

<div class="stat-panel">
  <PanelHeader icon={faMicrochip} title="CPU Usage" usageValue={averageUsage} />
  <div class="stats-content cpu-grid">
    {#each cpuUsage as usage, i}
      <div class="stat-item with-progress">
        <ProgressBar
          label={`Core ${i}`}
          value={usage}
          labelWidth="2.5rem"
          valueWidth="2.5rem"
        />
      </div>
    {/each}
  </div>
</div>

<style>
  .stat-panel {
    flex: 2.5;
    min-width: 0;
    background-color: var(--mantle);
    border-radius: 6px;
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
  }

  .stats-content {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
  }

  .cpu-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.4rem 2rem;
    height: auto;
    overflow: visible;
  }

  .stat-item.with-progress {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
  }
</style>


================================================
FILE: src/lib/components/stats/MemoryPanel.svelte
================================================
<script lang="ts">
  import { faMemory } from "@fortawesome/free-solid-svg-icons";
  import { PanelHeader, ProgressBar, StatItem } from "$lib/components";
  import { formatMemorySize, formatPercentage } from "$lib/utils";

  export let memoryTotal: number;
  export let memoryUsed: number;
  export let memoryFree: number;

  $: memoryPercentage = (memoryUsed / memoryTotal) * 100;
</script>

<div class="stat-panel">
  <PanelHeader
    icon={faMemory}
    title="Memory"
    usageValue={formatPercentage(memoryPercentage)}
  />
  <div class="stats-content">
    <div class="stat-item with-progress">
      <ProgressBar
        label="Memory usage"
        value={memoryPercentage}
        labelWidth="5rem"
        valueWidth="2.5rem"
      />
    </div>
    <StatItem label="Total" value={formatMemorySize(memoryTotal)} />
    <StatItem label="Used" value={formatMemorySize(memoryUsed)} />
    <StatItem label="Free" value={formatMemorySize(memoryFree)} />
  </div>
</div>

<style>
  .stat-panel {
    flex: 2;
    min-width: 0;
    background-color: var(--mantle);
    border-radius: 6px;
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
  }

  .stats-content {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
  }

  .stat-item.with-progress {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
  }
</style>


================================================
FILE: src/lib/components/stats/NetworkPanel.svelte
================================================
<script lang="ts">
  import { faNetworkWired } from "@fortawesome/free-solid-svg-icons";
  import { PanelHeader, StatItem } from "$lib/components";
  import { formatBytes } from "$lib/utils";

  export let networkRxBytes: number;
  export let networkTxBytes: number;
</script>

<div class="stat-panel">
  <PanelHeader icon={faNetworkWired} title="Network I/O" />
  <div class="network-stats">
    <StatItem label="↓ Receiving" value={formatBytes(networkRxBytes)} />
    <StatItem label="↑ Sending" value={formatBytes(networkTxBytes)} />
  </div>
</div>

<style>
  .stat-panel {
    flex: 0.8;
    min-width: 125px;
    background-color: var(--mantle);
    border-radius: 6px;
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
  }

  .network-stats {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
  }
</style>


================================================
FILE: src/lib/components/stats/PanelHeader.svelte
================================================
<script lang="ts">
  import Fa from "svelte-fa";
  import type { IconDefinition } from "@fortawesome/free-solid-svg-icons";

  export let icon: IconDefinition;
  export let title: string;
  export let usageValue: string | null = null;
</script>

<div class="panel-header">
  <Fa {icon} />
  <h3>{title}</h3>
  {#if usageValue}
    <div class="usage-pill">{usageValue}</div>
  {/if}
</div>

<style>
  .panel-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--surface0);
    flex-shrink: 0;
  }

  .panel-header h3 {
    font-size: 0.8rem;
    font-weight: 600;
    margin: 0;
    color: var(--text);
  }

  .panel-header :global(svg) {
    color: var(--blue);
    width: 0.8rem;
    height: 0.8rem;
  }

  .usage-pill {
    margin-left: auto;
    background: var(--surface0);
    padding: 0.15rem 0.5rem;
    border-radius: 1rem;
    font-size: 0.7rem;
    font-weight: 500;
  }
</style>


================================================
FILE: src/lib/components/stats/ProgressBar.svelte
================================================
<script lang="ts">
  export let label: string;
  export let value: number;
  export let labelWidth = "2.5rem";
  export let valueWidth = "2.5rem";

  function getUsageClass(usage: number): string {
    if (usage > 90) return "critical";
    if (usage > 75) return "high";
    if (usage > 50) return "medium";
    return "low";
  }
</script>

<div
  class="progress-container"
  style="--label-width: {labelWidth}; --value-width: {valueWidth}"
>
  <span class="label">{label}</span>
  <div class="bar-container">
    <div
      class="usage-bar {getUsageClass(value)}"
      style="transform: translateX({value - 100}%);"
    ></div>
  </div>
  <span class="value">{Math.round(value)}%</span>
</div>

<style>
  .progress-container {
    width: 100%;
    display: grid;
    grid-template-columns: var(--label-width) 1fr var(--value-width);
    align-items: center;
    gap: 0.3rem;
    font-size: 0.7rem;
  }

  .label {
    color: var(--subtext0);
    white-space: nowrap;
  }

  .value {
    color: var(--text);
    text-align: right;
    white-space: nowrap;
  }

  .bar-container {
    height: 8px;
    background: var(--surface0);
    border-radius: 4px;
    overflow: hidden;
    position: relative;
  }

  .usage-bar {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 100%;
    transition: transform 0.3s ease;
    transform-origin: left;
  }

  .usage-bar.low {
    background: var(--blue);
  }
  .usage-bar.medium {
    background: var(--yellow);
  }
  .usage-bar.high {
    background: var(--peach);
  }
  .usage-bar.critical {
    background: var(--red);
  }
</style>


================================================
FILE: src/lib/components/stats/StatItem.svelte
================================================
<script lang="ts">
  export let label: string;
  export let value: string;
</script>

<div class="stat-item">
  <span>{label}</span>
  <span>{value}</span>
</div>

<style>
  .stat-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 0.7rem;
    line-height: 1.2;
    margin: 0;
    padding: 0;
  }

  .stat-item span:first-child {
    color: var(--subtext0);
  }

  .stat-item span:last-child {
    color: var(--text);
    font-weight: 500;
  }
</style>


================================================
FILE: src/lib/components/stats/StatPanel.svelte
================================================
<script lang="ts">
  export let title: string;
  export let flex = 1;
</script>

<div class="stat-panel" style="--flex: {flex}">
  <h3 class="panel-title">{title}</h3>
  <div class="panel-content">
    <slot />
  </div>
</div>

<style>
  .stat-panel {
    flex: var(--flex);
    min-width: 125px;
    background: var(--mantle);
    border-radius: 8px;
    padding: 12px;
  }

  .panel-title {
    font-size: 12px;
    font-weight: 600;
    color: var(--subtext0);
    margin: 0 0 12px 0;
  }

  .panel-content {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
</style>


================================================
FILE: src/lib/components/stats/StatsBar.svelte
================================================
<script lang="ts">
  import type { SystemStats } from "$lib/types";
  import {
    CpuPanel,
    MemoryPanel,
    StoragePanel,
    SystemPanel,
    NetworkPanel,
  } from "$lib/components";

  export let systemStats: SystemStats | null = null;
</script>

<div class="dashboard-stats">
  {#if systemStats}
    <div class="stats-layout">
      <CpuPanel cpuUsage={systemStats.cpu_usage} />

      <MemoryPanel
        memoryTotal={systemStats.memory_total}
        memoryUsed={systemStats.memory_used}
        memoryFree={systemStats.memory_free}
      />

      <StoragePanel
        diskTotalBytes={systemStats.disk_total_bytes}
        diskUsedBytes={systemStats.disk_used_bytes}
        diskFreeBytes={systemStats.disk_free_bytes}
      />

      <SystemPanel uptime={systemStats.uptime} loadAvg={systemStats.load_avg} />

      <NetworkPanel
        networkRxBytes={systemStats.network_rx_bytes}
        networkTxBytes={systemStats.network_tx_bytes}
      />
    </div>
  {/if}
</div>

<style>
  .dashboard-stats {
    padding: 0.5rem;
    overflow-x: auto;
  }

  .stats-layout {
    display: flex;
    gap: 0.75rem;
    width: 100%;
  }
</style>


================================================
FILE: src/lib/components/stats/StoragePanel.svelte
================================================
<script lang="ts">
  import { faHardDrive } from "@fortawesome/free-solid-svg-icons";
  import { PanelHeader, StatItem } from "$lib/components";
  import { formatBytes, formatPercentage } from "$lib/utils";

  export let diskTotalBytes: number;
  export let diskUsedBytes: number;
  export let diskFreeBytes: number;

  $: diskUsagePercentage = (diskUsedBytes / diskTotalBytes) * 100;
</script>

<div class="stat-panel">
  <PanelHeader
    icon={faHardDrive}
    title="Storage"
    usageValue={formatPercentage(diskUsagePercentage)}
  />
  <div class="stats-content">
    <StatItem label="Total" value={formatBytes(diskTotalBytes)} />
    <StatItem label="Used" value={formatBytes(diskUsedBytes)} />
    <StatItem label="Free" value={formatBytes(diskFreeBytes)} />
  </div>
</div>

<style>
  .stat-panel {
    flex: 0.8;
    min-width: 125px;
    background-color: var(--mantle);
    border-radius: 6px;
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
  }

  .stats-content {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
  }
</style>


================================================
FILE: src/lib/components/stats/SystemPanel.svelte
================================================
<script lang="ts">
  import { faServer } from "@fortawesome/free-solid-svg-icons";
  import { PanelHeader, StatItem } from "$lib/components";
  import { formatUptime } from "$lib/utils";

  export let uptime: number;
  export let loadAvg: [number, number, number];
</script>

<div class="stat-panel">
  <PanelHeader icon={faServer} title="System" />
  <div class="system-grid">
    <StatItem label="Uptime" value={formatUptime(uptime)} />
    <StatItem label="1m Load" value={loadAvg[0].toFixed(2)} />
    <StatItem label="5m Load" value={loadAvg[1].toFixed(2)} />
    <StatItem label="15m Load" value={loadAvg[2].toFixed(2)} />
  </div>
</div>

<style>
  .stat-panel {
    flex: 0.8;
    min-width: 125px;
    background-color: var(--mantle);
    border-radius: 6px;
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
  }

  .system-grid {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    flex: 1;
  }
</style>


================================================
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
================================================
<script lang="ts">
  import Fa from "svelte-fa";
  import {
    faChevronDown,
    faChevronRight,
    faChevronLeft,
  } from "@fortawesome/free-solid-svg-icons";
  import { settingsStore } from "$lib/stores/index";
  import { overlayStore } from "$lib/stores/overlay";
  import { onDestroy } from "svelte";

  export let columns: Array<{
    id: string;
    label: string;
    visible: boolean;
    required?: boolean;
  }>;

  let containerElement: HTMLDivElement;
  let overlayElement: HTMLDivElement;
  let optionsContainer: HTMLDivElement;
  let canScrollLeft = false;
  let canScrollRight = false;

  $: showColumnMenu = $overlayStore === "columns";

  function handleColumnVisibilityChange(columnId: string, visible: boolean) {
    settingsStore.updateConfig({
      appearance: {
        columnVisibility: {
          ...$settingsStore.appearance.columnVisibility,
          [columnId]: visible,
        },
      },
    });
  }

  function updateOverlayPosition() {
    if (overlayElement && containerElement) {
      const toolbarContent = containerElement.closest(".toolbar-content");
      if (toolbarContent) {
        const toolbarRect = toolbarContent.getBoundingClientRect();
        const containerRect = containerElement.getBoundingClientRect();

        const leftOffset = containerRect.left - toolbarRect.left;
        const rightOffset = toolbarRect.right - containerRect.right;
        const topOffset = containerRect.top - toolbarRect.top;

        overlayElement.style.left = `-${leftOffset}px`;
        overlayElement.style.right = `-${rightOffset}px`;
        overlayElement.style.top = `-${topOffset}px`;
      }
    }
  }

  function updateScrollButtons() {
    if (optionsContainer) {
      canScrollLeft = optionsContainer.scrollLeft > 0;
      canScrollRight =
        optionsContainer.scrollLeft <
        optionsContainer.scrollWidth - optionsContainer.clientWidth;
    }
  }

  function scrollLeft() {
    if (optionsContainer) {
      optionsContainer.scrollBy({ left: -200, behavior: "smooth" });
      setTimeout(updateScrollButtons, 100);
    }
  }

  function scrollRight() {
    if (optionsContainer) {
      optionsContainer.scrollBy({ left: 200, behavior: "smooth" });
      setTimeout(updateScrollButtons, 100);
    }
  }

  function toggleExpanded(event: Event) {
    event.stopPropagation();
    if (showColumnMenu) {
      overlayStore.close();
    } else {
      overlayStore.open("columns");
      setTimeout(() => {
        updateOverlayPosition();
        updateScrollButtons();
      }, 0);
    }
  }

  function handleClickOutside(event: MouseEvent) {
    if (
      showColumnMenu &&
      containerElement &&
      !containerElement.contains(event.target as Node)
    ) {
      overlayStore.close();
    }
  }

  function setupClickOutside() {
    if (typeof document !== "undefined") {
      document.addEventListener("click", handleClickOutside);
    }
  }

  function cleanupClickOutside() {
    if (typeof document !== "undefined") {
      document.removeEventListener("click", handleClickOutside);
    }
  }

  $: if (showColumnMenu) {
    setTimeout(setupClickOutside, 0);
  } else {
    cleanupClickOutside();
  }

  onDestroy(() => {
    cleanupClickOutside();
  });
</script>

<div class="column-toggle" bind:this={containerElement}>
  <button
    class="touchbar-trigger"
    class:active={showColumnMenu}
    on:click={toggleExpanded}
    aria-label="Toggle columns"
  >
    Columns
    <span class="icon">
      <Fa icon={showColumnMenu ? faChevronDown : faChevronRight} />
    </span>
  </button>

  {#if showColumnMenu}
    <div
      class="touchbar-full-overlay"
      bind:this={overlayElement}
      on:click={() => overlayStore.close()}
      on:keydown={(e) => e.key === "Escape" && overlayStore.close()}
      role="dialog"
      aria-label="Column visibility options"
      tabindex="-1"
    >
      {#if canScrollLeft}
        <button
          class="scroll-chevron scroll-left"
          on:click|stopPropagation={scrollLeft}
        >
          <Fa icon={faChevronLeft} />
        </button>
      {/if}

      <div
        class="touchbar-horizontal-options"
        bind:this={optionsContainer}
        on:scroll={updateScrollButtons}
      >
        {#each columns as column}
          <button
            class="touchbar-option"
            class:active={column.visible}
            class:disabled={column.required}
            on:click|stopPropagation={() =>
              !column.required &&
              handleColumnVisibilityChange(column.id, !column.visible)}
            title={column.required
              ? "Required column"
              : `Toggle ${column.label}`}
          >
            {column.label}
          </button>
        {/each}
      </div>

      {#if canScrollRight}
        <button
          class="scroll-chevron scroll-right"
          on:click|stopPropagation={scrollRight}
        >
          <Fa icon={faChevronRight} />
        </button>
      {/if}
    </div>
  {/if}
</div>

<style>
  .column-toggle {
    position: relative;
    flex: 1;
  }

  .touchbar-trigger {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 0 12px;
    height: 28px;
    font-size: 12px;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
    box-sizing: border-box;
  }

  .touchbar-trigger:hover {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-trigger.active {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .icon {
    font-size: 10px;
    color: var(--subtext0);
    transition: transform 0.2s ease;
  }

  .touchbar-full-overlay {
    position: absolute;
    top: -0px;
    height: 44px;
    background: var(--mantle);
    border: none;
    border-radius: 0;
    box-shadow: none;
    z-index: 1000;
    display: flex;
    align-items: center;
    padding: 0 8px;
    gap: 8px;
  }

  .scroll-chevron {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    color: var(--text);
    cursor: pointer;
    transition: all 0.15s ease;
    flex-shrink: 0;
    font-size: 10px;
    animation: optionSlideIn 0.2s ease-out;
    animation-fill-mode: both;
    animation-delay: 0ms;
  }

  .scroll-chevron:hover {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-horizontal-options {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
    padding: 0 4px;
  }

  .touchbar-horizontal-options::-webkit-scrollbar {
    display: none;
  }

  .touchbar-option {
    padding: 0 12px;
    height: 28px;
    font-size: 11px;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: fit-content;
    box-sizing: border-box;
    animation: optionSlideIn 0.2s ease-out;
    animation-fill-mode: both;
  }

  .touchbar-option:nth-child(1) {
    animation-delay: 20ms;
  }
  .touchbar-option:nth-child(2) {
    animation-delay: 40ms;
  }
  .touchbar-option:nth-child(3) {
    animation-delay: 60ms;
  }
  .touchbar-option:nth-child(4) {
    animation-delay: 80ms;
  }
  .touchbar-option:nth-child(5) {
    animation-delay: 100ms;
  }
  .touchbar-option:nth-child(6) {
    animation-delay: 120ms;
  }
  .touchbar-option:nth-child(7) {
    animation-delay: 140ms;
  }
  .touchbar-option:nth-child(8) {
    animation-delay: 160ms;
  }
  .touchbar-option:nth-child(9) {
    animation-delay: 180ms;
  }
  .touchbar-option:nth-child(10) {
    animation-delay: 200ms;
  }

  @keyframes optionSlideIn {
    from {
      opacity: 0;
      transform: translateY(-8px) scale(0.9);
    }
    to {
      opacity: 1;
      transform: translateY(0) scale(1);
    }
  }

  .touchbar-option:hover:not(.disabled) {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-option.active {
    background: var(--blue);
    color: var(--base);
    border-color: var(--blue);
  }

  .touchbar-option.disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
</style>


================================================
FILE: src/lib/components/toolbar/FilterToggle.svelte
================================================
<script lang="ts">
  import Fa from "svelte-fa";
  import { faFilter } from "@fortawesome/free-solid-svg-icons";
  import { overlayStore } from "$lib/stores/overlay";
  import { onDestroy } from "svelte";

  export let filters: {
    cpu: { operator: string; value: number; enabled: boolean };
    ram: { operator: string; value: number; enabled: boolean };
    runtime: { operator: string; value: number; enabled: boolean };
    status: { values: string[]; enabled: boolean };
  } = {
    cpu: { operator: ">", value: 50, enabled: false },
    ram: { operator: ">", value: 100, enabled: false },
    runtime: { operator: ">", value: 60, enabled: false },
    status: { values: [], enabled: false },
  };

  let containerElement: HTMLDivElement;
  let overlayElement: HTMLDivElement;

  $: showFilters = $overlayStore === "filters";
  $: hasActiveFilters = Object.values(filters).some((f) => f.enabled);
  $: activeFilterCount = Object.values(filters).filter((f) => f.enabled).length;

  const operators = [
    { value: ">", label: ">" },
    { value: "<", label: "<" },
  ];

  const statusOptions = [
    { value: "Running", label: "Running", color: "var(--green)" },
    { value: "Sleeping", label: "Sleeping", color: "var(--blue)" },
    { value: "Stopped", label: "Stopped", color: "var(--red)" },
    { value: "Zombie", label: "Zombie", color: "var(--yellow)" },
  ];

  function updateOverlayPosition() {
    if (overlayElement && containerElement) {
      const toolbarContent = containerElement.closest(".toolbar-content");
      if (toolbarContent) {
        const toolbarRect = toolbarContent.getBoundingClientRect();
        const containerRect = containerElement.getBoundingClientRect();

        const leftOffset = containerRect.left - toolbarRect.left;
        const rightOffset = toolbarRect.right - containerRect.right;
        const topOffset = containerRect.top - toolbarRect.top;

        overlayElement.style.left = `-${leftOffset}px`;
        overlayElement.style.right = `-${rightOffset}px`;
        overlayElement.style.top = `-${topOffset}px`;
      }
    }
  }

  function toggleFilters(event: Event) {
    event.stopPropagation();
    if (showFilters) {
      overlayStore.close();
    } else {
      overlayStore.open("filters");
      setTimeout(updateOverlayPosition, 0);
    }
  }

  function toggleFilter(type: keyof typeof filters) {
    filters[type].enabled = !filters[type].enabled;
    filters = { ...filters };
  }

  function updateFilter(
    type: keyof typeof filters,
    field: string,
    value: string | number,
  ) {
    if (type === "status" && field === "values") {
      // Handle status array toggle
      const statusValue = value as string;
      const currentValues = filters.status.values;
      if (currentValues.includes(statusValue)) {
        filters.status.values = currentValues.filter((v) => v !== statusValue);
      } else {
        filters.status.values = [...currentValues, statusValue];
      }
      filters.status.enabled = filters.status.values.length > 0;
    } else if (field === "operator") {
      (filters[type as keyof Omit<typeof filters, "status">] as any)[field] =
        value as string;
    } else if (field === "value") {
      (filters[type as keyof Omit<typeof filters, "status">] as any)[field] =
        value as number;
    }
    filters = { ...filters };
  }

  function clearAllFilters() {
    Object.keys(filters).forEach((key) => {
      const filterKey = key as keyof typeof filters;
      if (filterKey === "status") {
        filters[filterKey].values = [];
      }
      filters[filterKey].enabled = false;
    });
    filters = { ...filters };
  }

  function getFilterLabel(type: keyof typeof filters): string {
    const labels = {
      cpu: "CPU %",
      ram: "RAM MB",
      runtime: "Runtime min",
      status: "Status",
    };
    return labels[type];
  }

  function handleClickOutside(event: MouseEvent) {
    if (
      showFilters &&
      containerElement &&
      !containerElement.contains(event.target as Node)
    ) {
      overlayStore.close();
    }
  }

  function setupClickOutside() {
    if (typeof document !== "undefined") {
      document.addEventListener("click", handleClickOutside);
    }
  }

  function cleanupClickOutside() {
    if (typeof document !== "undefined") {
      document.removeEventListener("click", handleClickOutside);
    }
  }

  $: if (showFilters) {
    setTimeout(setupClickOutside, 0);
  } else {
    cleanupClickOutside();
  }

  onDestroy(() => {
    cleanupClickOutside();
  });
</script>

<div class="filter-toggle" bind:this={containerElement}>
  <button
    class="filter-button"
    class:active={showFilters}
    class:has-filters={hasActiveFilters}
    on:click={toggleFilters}
    aria-label="Toggle filters"
  >
    <Fa icon={faFilter} />
    Filters
    {#if hasActiveFilters}
      <span class="filter-count">{activeFilterCount}</span>
    {/if}
  </button>

  {#if showFilters}
    <div
      class="touchbar-full-overlay"
      bind:this={overlayElement}
      on:click={() => overlayStore.close()}
      on:keydown={(e) => e.key === "Escape" && overlayStore.close()}
      role="dialog"
      aria-label="Filter options overlay"
      tabindex="-1"
    >
      <div class="filter-content">
        <div class="filter-sections">
          <!-- Numeric Filters -->
          <div class="filter-section">
            <span class="section-label">Performance:</span>
            <div class="filter-controls">
              {#each [["cpu", "CPU %"], ["ram", "RAM MB"], ["runtime", "Runtime min"]] as [type, label]}
                {@const filterKey = type as "cpu" | "ram" | "runtime"}
                <div class="filter-control">
                  <button
                    class="filter-toggle-btn"
                    class:active={filters[filterKey].enabled}
                    on:click|stopPropagation={() => toggleFilter(filterKey)}
                  >
                    {label}
                  </button>
                  {#if filters[filterKey].enabled}
                    <select
                      class="operator-select"
                      bind:value={filters[filterKey].operator}
                      on:change={(e) =>
                        updateFilter(
                          filterKey,
                          "operator",
                          (e.target as HTMLSelectElement).value,
                        )}
                      on:click|stopPropagation
                    >
                      {#each operators as op}
                        <option value={op.value}>{op.label}</option>
                      {/each}
                    </select>
                    <input
                      type="number"
                      class="value-input"
                      bind:value={filters[filterKey].value}
                      on:input={(e) =>
                        updateFilter(
                          filterKey,
                          "value",
                          parseInt((e.target as HTMLInputElement).value),
                        )}
                      on:click|stopPropagation
                      on:focus|stopPropagation
                      placeholder={type === "cpu"
                        ? "50"
                        : type === "ram"
                          ? "100"
                          : "60"}
                    />
                    {#if type === "ram"}
                      <span class="unit">MB</span>
                    {:else if type === "runtime"}
                      <span class="unit">min</span>
                    {:else}
                      <span class="unit">%</span>
                    {/if}
                  {/if}
                </div>
              {/each}
            </div>
          </div>

          <!-- Status Filter -->
          <div class="filter-section">
            <span class="section-label">Status:</span>
            <div class="status-controls">
              {#each statusOptions as status}
                <button
                  class="status-toggle"
                  class:active={filters.status.values.includes(status.value)}
                  style="--status-color: {status.color}"
                  on:click|stopPropagation={() =>
                    updateFilter("status", "values", status.value)}
                >
                  {status.label}
                </button>
              {/each}
            </div>
          </div>

          <!-- Actions -->
          <div class="filter-actions">
            {#if hasActiveFilters}
              <button
                class="clear-all-btn"
                on:click|stopPropagation={clearAllFilters}
              >
                <Fa icon={faFilter} />
                Clear All
              </button>
            {/if}
            <div class="filter-summary">
              {#if hasActiveFilters}
                <span
                  >{activeFilterCount} filter{activeFilterCount > 1 ? "s" : ""} active</span
                >
              {:else}
                <span>No filters applied</span>
              {/if}
            </div>
          </div>
        </div>
      </div>
    </div>
  {/if}
</div>

<style>
  .filter-toggle {
    position: relative;
  }

  .filter-button {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 0 12px;
    height: 28px;
    font-size: 12px;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
    box-sizing: border-box;
  }

  .filter-button:hover {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .filter-button.active {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .filter-button.has-filters {
    border-color: var(--blue);
    background: var(--surface1);
  }

  .filter-count {
    background: var(--blue);
    color: var(--base);
    border-radius: 10px;
    padding: 2px 6px;
    font-size: 10px;
    font-weight: 600;
    min-width: 16px;
    text-align: center;
  }

  .touchbar-full-overlay {
    position: absolute;
    top: -0px;
    height: 44px;
    background: var(--mantle);
    border: none;
    border-radius: 0;
    box-shadow: none;
    z-index: 1000;
    display: flex;
    align-items: center;
    padding: 0 16px;
    gap: 16px;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  .touchbar-full-overlay::-webkit-scrollbar {
    display: none;
  }

  .filter-content {
    display: flex;
    align-items: center;
    gap: 32px;
    width: 100%;
    animation: slideIn 0.3s ease-out;
  }

  @keyframes slideIn {
    from {
      opacity: 0;
      transform: translateY(-8px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }

  .filter-sections {
    display: flex;
    align-items: center;
    gap: 32px;
    flex: 1;
  }

  .filter-section {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
  }

  .section-label {
    font-size: 12px;
    font-weight: 500;
    color: var(--subtext0);
    flex-shrink: 0;
  }

  .filter-controls {
    display: flex;
    gap: 8px;
    align-items: center;
  }

  .filter-control {
    display: flex;
    align-items: center;
    gap: 4px;
  }

  .filter-toggle-btn {
    padding: 4px 8px;
    height: 26px;
    font-size: 11px;
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 4px;
    color: var(--text);
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
  }

  .filter-toggle-btn:hover {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .filter-toggle-btn.active {
    background: var(--blue);
    color: var(--base);
    border-color: var(--blue);
  }

  .operator-select {
    padding: 4px 8px;
    height: 26px;
    font-size: 11px;
    border: 1px solid var(--surface1);
    border-radius: 4px;
    background: var(--surface0);
    color: var(--text);
    cursor: pointer;
    transition: all 0.15s ease;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    text-align: center;
    min-width: 32px;
  }

  .operator-select:hover {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .operator-select:focus {
    outline: none;
    border-color: var(--blue);
    background: var(--surface1);
  }

  .value-input {
    width: 50px;
    padding: 2px 6px;
    height: 22px;
    font-size: 11px;
    border: 1px solid var(--surface1);
    border-radius: 3px;
    background: var(--surface0);
    color: var(--text);
    text-align: center;
  }

  .value-input:focus {
    outline: none;
    border-color: var(--blue);
  }

  .unit {
    font-size: 10px;
    color: var(--subtext0);
  }

  .status-controls {
    display: flex;
    gap: 6px;
    align-items: center;
  }

  .status-toggle {
    padding: 4px 8px;
    height: 26px;
    font-size: 11px;
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 4px;
    color: var(--text);
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
  }

  .status-toggle:hover {
    background: var(--surface1);
    border-color: var(--status-color);
  }

  .status-toggle.active {
    background: var(--status-color);
    color: var(--base);
    border-color: var(--status-color);
  }

  .filter-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
  }

  .clear-all-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    height: 26px;
    font-size: 11px;
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 4px;
    color: var(--text);
    cursor: pointer;
    transition: all 0.15s ease;
  }

  .clear-all-btn:hover {
    background: var(--red);
    border-color: var(--red);
    color: var(--base);
  }

  .filter-summary {
    font-size: 11px;
    color: var(--subtext0);
  }
</style>


================================================
FILE: src/lib/components/toolbar/PaginationControls.svelte
================================================
<script lang="ts">
  import type { AppConfig } from "$lib/types";
  import { settingsStore } from "$lib/stores/index";
  import { overlayStore } from "$lib/stores/overlay";
  import { ITEMS_PER_PAGE_OPTIONS } from "$lib/constants";
  import { onDestroy } from "svelte";

  export let itemsPerPage: number;
  export let currentPage: number;
  export let totalPages: number;
  export let totalResults: number;

  let containerElement: HTMLDivElement;
  let overlayElement: HTMLDivElement;

  $: isExpanded = $overlayStore === "pagination";

  function changePage(page: number) {
    if (page >= 1 && page <= totalPages) {
      currentPage = page;
    }
  }

  function updateBehaviorConfig(key: keyof AppConfig["behavior"], value: any) {
    settingsStore.updateConfig({
      behavior: {
        ...$settingsStore.behavior,
        [key]: value,
      },
    });
  }

  function selectItemsPerPage(value: number) {
    itemsPerPage = value;
    updateBehaviorConfig("itemsPerPage", itemsPerPage);
    overlayStore.close();
  }

  function updateOverlayPosition() {
    if (overlayElement && containerElement) {
      const toolbarContent = containerElement.closest(".toolbar-content");
      if (toolbarContent) {
        const toolbarRect = toolbarContent.getBoundingClientRect();
        const containerRect = containerElement.getBoundingClientRect();

        const leftOffset = containerRect.left - toolbarRect.left;
        const rightOffset = toolbarRect.right - containerRect.right;
        const topOffset = containerRect.top - toolbarRect.top;

        overlayElement.style.left = `-${leftOffset}px`;
        overlayElement.style.right = `-${rightOffset}px`;
        overlayElement.style.top = `-${topOffset}px`;
      }
    }
  }

  function toggleExpanded(event: Event) {
    event.stopPropagation();
    if (isExpanded) {
      overlayStore.close();
    } else {
      overlayStore.open("pagination");
      setTimeout(updateOverlayPosition, 0);
    }
  }

  function handleClickOutside(event: MouseEvent) {
    if (
      isExpanded &&
      containerElement &&
      !containerElement.contains(event.target as Node)
    ) {
      overlayStore.close();
    }
  }

  function setupClickOutside() {
    if (typeof document !== "undefined") {
      document.addEventListener("click", handleClickOutside);
    }
  }

  function cleanupClickOutside() {
    if (typeof document !== "undefined") {
      document.removeEventListener("click", handleClickOutside);
    }
  }

  $: if (isExpanded) {
    setTimeout(setupClickOutside, 0);
  } else {
    cleanupClickOutside();
  }

  onDestroy(() => {
    cleanupClickOutside();
  });
</script>

<div class="pagination-controls">
  <div
    class="pagination-per-page"
    class:active={isExpanded}
    bind:this={containerElement}
  >
    <button
      class="touchbar-trigger"
      class:active={isExpanded}
      on:click={toggleExpanded}
    >
      {itemsPerPage} per page
    </button>

    {#if isExpanded}
      <div
        class="touchbar-full-overlay"
        bind:this={overlayElement}
        on:click={() => overlayStore.close()}
        on:keydown={(e) => e.key === "Escape" && overlayStore.close()}
        role="dialog"
        aria-label="Pagination controls"
        tabindex="-1"
      >
        <div class="touchbar-horizontal-options">
          {#each ITEMS_PER_PAGE_OPTIONS as option}
            <button
              class="touchbar-option"
              class:active={option === itemsPerPage}
              on:click|stopPropagation={() => selectItemsPerPage(option)}
            >
              {option}
            </button>
          {/each}
        </div>
      </div>
    {/if}
  </div>

  <div class="pagination">
    <button
      class="btn-page"
      disabled={currentPage === 1}
      on:click={() => changePage(1)}
    >
      ««
    </button>
    <button
      class="btn-page"
      disabled={currentPage === 1}
      on:click={() => changePage(currentPage - 1)}
    >
      «
    </button>
    <div class="page-info">
      <span>Page {currentPage} of {totalPages}</span>
      <span class="results-info">({totalResults} processes)</span>
    </div>
    <button
      class="btn-page"
      disabled={currentPage === totalPages}
      on:click={() => changePage(currentPage + 1)}
    >
      »
    </button>
    <button
      class="btn-page"
      disabled={currentPage === totalPages}
      on:click={() => changePage(totalPages)}
    >
      »»
    </button>
  </div>
</div>

<style>
  .pagination-controls {
    display: flex;
    align-items: center;
    gap: 12px;
  }

  .pagination-per-page {
    display: flex;
    align-items: center;
    position: relative;
  }

  .touchbar-trigger {
    padding: 6px 12px;
    height: 28px;
    font-size: 12px;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
    white-space: nowrap;
    display: flex;
    align-items: center;
    box-sizing: border-box;
  }

  .touchbar-trigger:hover {
    background-color: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-trigger.active {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-full-overlay {
    position: absolute;
    top: -0px;
    height: 44px;
    background: var(--mantle);
    border: none;
    border-radius: 0;
    box-shadow: none;
    z-index: 1000;
    display: flex;
    align-items: center;
    padding: 0 12px;
    gap: 12px;
  }

  .touchbar-horizontal-options {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  .touchbar-horizontal-options::-webkit-scrollbar {
    display: none;
  }

  .touchbar-option {
    padding: 0 12px;
    height: 28px;
    font-size: 12px;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: fit-content;
    box-sizing: border-box;
    animation: optionSlideIn 0.2s ease-out;
    animation-fill-mode: both;
  }

  .touchbar-option:nth-child(1) {
    animation-delay: 0ms;
  }
  .touchbar-option:nth-child(2) {
    animation-delay: 40ms;
  }
  .touchbar-option:nth-child(3) {
    animation-delay: 80ms;
  }
  .touchbar-option:nth-child(4) {
    animation-delay: 120ms;
  }

  @keyframes optionSlideIn {
    from {
      opacity: 0;
      transform: translateY(-8px) scale(0.9);
    }
    to {
      opacity: 1;
      transform: translateY(0) scale(1);
    }
  }

  .touchbar-option:hover:not(.disabled) {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-option.active {
    background: var(--blue);
    color: var(--base);
    border-color: var(--blue);
  }

  .pagination {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .btn-page {
    padding: 6px 10px;
    font-size: 12px;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
  }

  .btn-page:hover:not(:disabled) {
    background: var(--surface1);
  }

  .btn-page:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .page-info {
    font-size: 12px;
    color: var(--subtext0);
    display: flex;
    flex-direction: column;
    align-items: center;
    flex-shrink: 0;
  }

  .page-info span {
    display: block;
  }

  .results-info {
    color: var(--overlay0);
  }
</style>


================================================
FILE: src/lib/components/toolbar/RefreshControls.svelte
================================================
<script lang="ts">
  import Fa from "svelte-fa";
  import { faPlay, faPause } from "@fortawesome/free-solid-svg-icons";
  import type { AppConfig } from "$lib/types";
  import { settingsStore } from "$lib/stores/index";
  import { overlayStore } from "$lib/stores/overlay";
  import { REFRESH_RATE_OPTIONS } from "$lib/constants";
  import { onDestroy } from "svelte";

  export let refreshRate: number;
  export let isFrozen: boolean;

  let containerElement: HTMLDivElement;
  let overlayElement: HTMLDivElement;

  $: isExpanded = $overlayStore === "refresh";

  function updateBehaviorConfig(key: keyof AppConfig["behavior"], value: any) {
    settingsStore.updateConfig({
      behavior: {
        ...$settingsStore.behavior,
        [key]: value,
      },
    });
  }

  function selectRefreshRate(value: number) {
    refreshRate = value;
    updateBehaviorConfig("refreshRate", refreshRate);
    overlayStore.close();
  }

  function getCurrentLabel() {
    return (
      REFRESH_RATE_OPTIONS.find((opt) => opt.value === refreshRate)?.label ||
      "1s"
    );
  }

  function updateOverlayPosition() {
    if (overlayElement && containerElement) {
      const toolbarContent = containerElement.closest(".toolbar-content");
      if (toolbarContent) {
        const toolbarRect = toolbarContent.getBoundingClientRect();
        const containerRect = containerElement.getBoundingClientRect();

        const leftOffset = containerRect.left - toolbarRect.left;
        const rightOffset = toolbarRect.right - containerRect.right;
        const topOffset = containerRect.top - toolbarRect.top;

        overlayElement.style.left = `-${leftOffset}px`;
        overlayElement.style.right = `-${rightOffset}px`;
        overlayElement.style.top = `-${topOffset}px`;
      }
    }
  }

  function toggleExpanded(event: Event) {
    if (!isFrozen) {
      event.stopPropagation();
      if (isExpanded) {
        overlayStore.close();
      } else {
        overlayStore.open("refresh");
        setTimeout(updateOverlayPosition, 0);
      }
    }
  }

  function handleClickOutside(event: MouseEvent) {
    if (
      isExpanded &&
      containerElement &&
      !containerElement.contains(event.target as Node)
    ) {
      overlayStore.close();
    }
  }

  function setupClickOutside() {
    if (typeof document !== "undefined") {
      document.addEventListener("click", handleClickOutside);
    }
  }

  function cleanupClickOutside() {
    if (typeof document !== "undefined") {
      document.removeEventListener("click", handleClickOutside);
    }
  }

  $: if (isExpanded) {
    setTimeout(setupClickOutside, 0);
  } else {
    cleanupClickOutside();
  }

  onDestroy(() => {
    cleanupClickOutside();
  });
</script>

<div class="refresh-controls">
  <div
    class="refresh-rate"
    class:active={isExpanded}
    bind:this={containerElement}
  >
    <button
      class="touchbar-trigger"
      class:disabled={isFrozen}
      class:active={isExpanded}
      on:click={toggleExpanded}
    >
      {getCurrentLabel()}
    </button>

    {#if isExpanded}
      <div
        class="touchbar-full-overlay"
        bind:this={overlayElement}
        on:click={() => overlayStore.close()}
        on:keydown={(e) => e.key === "Escape" && overlayStore.close()}
        role="dialog"
        aria-label="Refresh rate controls"
        tabindex="-1"
      >
        <div class="touchbar-horizontal-options">
          {#each REFRESH_RATE_OPTIONS as option}
            <button
              class="touchbar-option"
              class:active={option.value === refreshRate}
              on:click|stopPropagation={() => selectRefreshRate(option.value)}
            >
              {option.label}
            </button>
          {/each}
        </div>
      </div>
    {/if}
  </div>

  <button
    class="btn-action"
    class:frozen={isFrozen}
    on:click={() => (isFrozen = !isFrozen)}
    title={isFrozen ? "Resume Updates" : "Pause Updates"}
  >
    {#if isFrozen}
      <Fa icon={faPlay} color="var(--red)" />
    {:else}
      <Fa icon={faPause} color="var(--subtext0)" />
    {/if}
  </button>
</div>

<style>
  .refresh-controls {
    display: flex;
    gap: 8px;
    align-items: center;
  }

  .refresh-controls :global(svg) {
    font-size: 14px;
    color: var(--subtext0);
  }

  .refresh-rate {
    display: flex;
    align-items: center;
    position: relative;
  }

  .touchbar-trigger {
    height: 28px;
    padding: 0 12px;
    border: 1px solid var(--surface1);
    border-radius: 6px;
    background: var(--surface0);
    color: var(--text);
    font-size: 13px;
    cursor: pointer;
    transition: all 0.2s ease;
    white-space: nowrap;
  }

  .touchbar-trigger:hover:not(.disabled) {
    background-color: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-trigger.active {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-trigger.disabled {
    opacity: 0.7;
    cursor: not-allowed;
  }

  .touchbar-full-overlay {
    position: absolute;
    top: -0px;
    height: 44px;
    background: var(--mantle);
    border: none;
    border-radius: 0;
    box-shadow: none;
    z-index: 1000;
    display: flex;
    align-items: center;
    padding: 0 12px;
    gap: 12px;
  }

  .touchbar-horizontal-options {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  .touchbar-horizontal-options::-webkit-scrollbar {
    display: none;
  }

  .touchbar-option {
    padding: 0 12px;
    height: 28px;
    font-size: 12px;
    color: var(--text);
    background: var(--surface0);
    border: 1px solid var(--surface1);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: fit-content;
    box-sizing: border-box;
    animation: optionSlideIn 0.2s ease-out;
    animation-fill-mode: both;
  }

  .touchbar-option:nth-child(1) {
    animation-delay: 0ms;
  }
  .touchbar-option:nth-child(2) {
    animation-delay: 40ms;
  }
  .touchbar-option:nth-child(3) {
    animation-delay: 80ms;
  }
  .touchbar-option:nth-child(4) {
    animation-delay: 120ms;
  }

  @keyframes optionSlideIn {
    from {
      opacity: 0;
      transform: translateY(-8px) scale(0.9);
    }
    to {
      opacity: 1;
      transform: translateY(0) scale(1);
    }
  }

  .touchbar-option:hover:not(.disabled) {
    background: var(--surface1);
    border-color: var(--blue);
  }

  .touchbar-option.active {
    background: var(--blue);
    color: var(--base);
    border-color: var(--blue);
  }

  .btn-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: none;
    background: var(--surface0);
    border: 1px solid var(--surface1);
    color: var(--text);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
  }

  .btn-action:hover {
    background: var(--surface1);
  }

  .btn-action.frozen {
    background: var(--yellow);
  }
</style>


================================================
FILE: src/lib/components/toolbar/SearchBox.svelte
================================================
<script lang="ts">
  import { overlayStore } from "$lib/stores/overlay";
  import { onDestroy, onMount } from "svelte";

  export let searchTerm: string;

  let containerElement: HTMLDivElement;
  let overlayElement: HTMLDivElement;
  let searchInputElement: HTMLInputElement;
  let placeholderIndex = 0;
  let placeholderInterval: NodeJS.Timeout;

  $: showHelp = $overlayStore === "searchHelp";
  $: hasActiveSearch = searchTerm.trim().length > 0;

  const searchExamples = [
    {
      query: "systemd, dbus",
      description: "Multiple terms (comma-separated)",
      type: "multi",
    },
    {
      query: "d$",
      description: "Processes ending with 'd' (daemons)",
      type: "regex",
    },
    {
      query: "^kernel",
      description: "Kernel processes",
      type: "regex",
    },
    {
      query: "ssh.*server",
      description: "SSH server processes",
      type: "regex",
    },
    {
      query: "1234",
      description: "Search by PID",
      type: "pid",
    },
    {
      query: "python, node, nginx",
      description: "Find web/app server processes",
      type: "multi",
    },
    {
      query: "docker, containerd",
      description: "Container processes",
      type: "multi",
    },
    {
      query: "gnome, plasma",
      description: "Desktop environment processes",
      type: "multi",
    },
  ];

  const placeholders = [
    "Search processes...",
    "Try: systemd, dbus",
    "Try: d$ (daemons)",
    "Try: ^kernel (regex)",
    "Search by name, command, or PID",
    "Try: docker, nginx",
  ];

  function rotatePlaceholder() {
    placeholderIndex = (placeholderIndex + 1) % placeholders.length;
  }

  function updateOverlayPosition() {
    if (overlayElement && containerElement) {
      const toolbarContent = containerElement.closest(".toolbar-content");
      if (toolbarContent) {
        const toolbarRect = toolbarContent.getBoundingClientRect();
        const containerRect = containerElement.getBoundingClientRect();

        const leftOffset = containerRect.left - toolbarRect.left;
        const rightOffset = toolbarRect.right - containerRect.right;
        const topOffset = containerRect.top - toolbarRect.top;

        overlayElement.style.left = `-${leftOffset}px`;
        overlayElement.style.right = `-${rightOffset}px`;
        overlayElement.style.top = `-${topOffset}px`;
      }
    }
  }

  function handleFocus() {
    overlayStore.open("searchHelp");
    setTimeout(() => {
      updateOverlayPosition();
      // Focus the enhanced search input in the overlay
      const overlayInput = overlayElement?.querySelector(
        ".overlay-search-input",
      ) as HTMLInputElement;
      if (overlayInput) {
        overlayInput.focus();
      }
    }, 0);
  }

  function handleBlur(event: FocusEvent) {
    // Small delay to allow clicking on examples
    setTimeout(() => {
      const activeElement = document.activeElement;
      if (!containerElement?.contains(activeElement as Node)) {
        overlayStore.close();
      }
    }, 150);
  }

  function useExample(example: string) {
    searchTerm = example;
    // Keep focus on the overlay search input
    const overlayInput = overlayElement?.querySelector(
      ".overlay-search-input",
    ) as HTMLInputElement;
    if (overlayInput) {
      overlayInput.focus();
      // Position cursor at end
      setTimeout(() => {
        overlayInput.setSelectionRange(example.length, example.length);
      }, 0);
    }
  }

  function handleClickOutside(event: MouseEvent) {
    if (
      showHelp &&
      containerElement &&
      !containerElement.contains(event.target as Node)
    ) {
      overlayStore.close();
    }
  }

  function setupClickOutside() {
    if (typeof document !== "undefined") {
      document.addEventListener("click", handleClickOutside);
    }
  }
Download .txt
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
Download .txt
SYMBOL INDEX (72 symbols across 17 files)

FILE: docs/main.js
  function setTheme (line 7) | function setTheme(isDark) {
  function fetchDownloadStats (line 36) | async function fetchDownloadStats() {
  function updateVersion (line 58) | async function updateVersion() {
  function updateDownloadLinks (line 74) | function updateDownloadLinks(versionNumber) {

FILE: src-tauri/build.rs
  function main (line 1) | fn main() {

FILE: src-tauri/src/commands.rs
  function get_processes (line 29) | pub async fn get_processes(
  function kill_process (line 64) | pub async fn kill_process(pid: u32, state: State<'_, AppState>) -> Resul...

FILE: src-tauri/src/main.rs
  function main (line 23) | fn main() {

FILE: src-tauri/src/monitoring/process_monitor.rs
  function os_string_vec_to_string_vec (line 13) | fn os_string_vec_to_string_vec(v: &[OsString]) -> Vec<String> {
  type ProcessMonitor (line 21) | pub struct ProcessMonitor {
    method new (line 28) | pub fn new() -> Self {
    method collect_processes (line 43) | pub fn collect_processes(&mut self, sys: &sysinfo::System) -> Result<V...
    method kill_process (line 59) | pub fn kill_process(sys: &sysinfo::System, pid: u32) -> bool {
    method get_current_time (line 66) | fn get_current_time() -> Result<u64, String> {
    method collect_process_data (line 74) | fn collect_process_data(&self, sys: &sysinfo::System, current_time: u6...
    method build_process_info (line 108) | fn build_process_info(&mut self, processes: Vec<ProcessData>) -> Vec<P...
    method format_status (line 144) | pub fn format_status(status: ProcessStatus) -> String {
  function test_process_monitor_creation (line 162) | fn test_process_monitor_creation() {
  function test_process_collection (line 169) | fn test_process_collection() {

FILE: src-tauri/src/monitoring/system_monitor.rs
  type SystemMonitor (line 14) | pub struct SystemMonitor {
    method new (line 25) | pub fn new(networks: &Networks) -> Self {
    method collect_stats (line 46) | pub fn collect_stats(
    method filter_disks (line 75) | fn filter_disks(disks: &[Disk]) -> impl Iterator<Item = &Disk> {
    method filter_disks (line 83) | fn filter_disks(disks: &[Disk]) -> impl Iterator<Item = &Disk> {
    method calculate_network_stats (line 88) | fn calculate_network_stats(&mut self, networks: &Networks) -> (u64, u6...
    method calculate_disk_stats (line 108) | fn calculate_disk_stats(&self, disks: &Disks) -> (u64, u64, u64) {
  function test_system_monitor_creation (line 126) | fn test_system_monitor_creation() {
  function test_stats_collection (line 135) | fn test_stats_collection() {

FILE: src-tauri/src/monitoring/types.rs
  type ProcessData (line 8) | pub(crate) struct ProcessData {
  type ProcessStaticInfo (line 44) | pub struct ProcessStaticInfo {
  type ProcessInfo (line 56) | pub struct ProcessInfo {
  type SystemStats (line 94) | pub struct SystemStats {

FILE: src-tauri/src/state.rs
  type AppState (line 14) | pub struct AppState {
    method new (line 35) | pub fn new() -> Self {

FILE: src-tauri/src/ui/window.rs
  function setup_window_effects (line 14) | pub fn setup_window_effects(window: &WebviewWindow) -> Result<(), Box<dy...
  function setup_window_effects (line 21) | pub fn setup_window_effects(window: &WebviewWindow) -> Result<(), Box<dy...
  function setup_window_effects (line 33) | pub fn setup_window_effects(_window: &WebviewWindow) -> Result<(), Box<d...

FILE: src/lib/constants/index.ts
  constant ASCII_ART (line 1) | const ASCII_ART = `
  constant APP_INFO (line 10) | const APP_INFO = {
  constant ITEMS_PER_PAGE_OPTIONS (line 17) | const ITEMS_PER_PAGE_OPTIONS = [15, 25, 50, 100, 250, 500];
  constant REFRESH_RATE_OPTIONS (line 19) | const REFRESH_RATE_OPTIONS = [
  constant STATUS_OPTIONS (line 27) | const STATUS_OPTIONS = [
  constant THEME_GROUPS (line 35) | const THEME_GROUPS = [

FILE: src/lib/definitions/settings.ts
  constant DEFAULT_CONFIG (line 3) | const DEFAULT_CONFIG: AppConfig = {

FILE: src/lib/stores/overlay.ts
  type OverlayType (line 3) | type OverlayType =
  function createOverlayStore (line 13) | function createOverlayStore() {

FILE: src/lib/stores/processes.ts
  type ProcessStore (line 5) | interface ProcessStore {
  function createProcessStore (line 47) | function createProcessStore() {

FILE: src/lib/stores/settings.ts
  function createSettingsStore (line 5) | function createSettingsStore() {

FILE: src/lib/stores/theme.ts
  function createThemeStore (line 4) | function createThemeStore() {
  function applyTheme (line 49) | function applyTheme(theme: Theme) {

FILE: src/lib/types/index.ts
  type Process (line 2) | interface Process {
  type SystemStats (line 21) | interface SystemStats {
  type Column (line 36) | interface Column {
  type Theme (line 44) | interface Theme {
  type AppConfig (line 72) | interface AppConfig {
  type ColumnDefinition (line 83) | interface ColumnDefinition {
  type StatusOption (line 90) | interface StatusOption {
  type RefreshRateOption (line 95) | interface RefreshRateOption {
  type ToolBarProps (line 100) | interface ToolBarProps {
  type SortConfig (line 112) | interface SortConfig {

FILE: src/lib/utils/index.ts
  type ProcessStatus (line 4) | interface ProcessStatus {
  function formatMemorySize (line 10) | function formatMemorySize(bytes: number): string {
  function formatPercentage (line 15) | function formatPercentage(value: number): string {
  function formatUptime (line 19) | function formatUptime(seconds: number): string {
  function getUsageClass (line 26) | function getUsageClass(percentage: number): string {
  function formatBytes (line 33) | function formatBytes(bytes: number): string {
  function formatDate (line 46) | function formatDate(timestamp: number) {
  function debounce (line 51) | function debounce<T extends (...args: any[]) => any>(
  function filterProcesses (line 65) | function filterProcesses(
  function compareValue (line 166) | function compareValue(
  function sortProcesses (line 190) | function sortProcesses(
Condensed preview — 95 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (304K chars).
[
  {
    "path": ".github/CODEOWNERS",
    "chars": 13,
    "preview": "* @Abdenasser"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 885,
    "preview": "# Contributing to NeoHtop\n\nThank you for considering contributing to NeoHtop! We welcome contributions from the communit"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 19,
    "preview": "github: abdenasser\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 635,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 181,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Support\n    url: https://github.com/Abdenasser/neohtop/discussions\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 604,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 1244,
    "preview": "## Description\n\nPlease include a summary of the changes and the related issue. Please also include relevant motivation a"
  },
  {
    "path": ".github/workflows/build-check.yml",
    "chars": 2743,
    "preview": "name: Build Check\n\non:\n  pull_request:\n    branches: [main]\n    paths:\n      - \"src-tauri/**\"\n      - \".github/workflows"
  },
  {
    "path": ".github/workflows/format-check.yml",
    "chars": 486,
    "preview": "name: Format Check\n\non:\n  pull_request:\n    branches: [ main ]\n\njobs:\n  format:\n    runs-on: ubuntu-latest\n    steps:\n  "
  },
  {
    "path": ".github/workflows/linux-aarch64-nightly.yml",
    "chars": 7340,
    "preview": "name: Linux (aarch64) Nightly Build\n\non:\n  workflow_dispatch:\n    inputs:\n      release_upload_url:\n        description:"
  },
  {
    "path": ".github/workflows/linux-x86_64-nightly.yml",
    "chars": 4261,
    "preview": "name: Linux (x86_64) Nightly Build\n\non:\n  workflow_dispatch:\n    inputs:\n      release_upload_url:\n        description: "
  },
  {
    "path": ".github/workflows/macos-nightly.yml",
    "chars": 4187,
    "preview": "name: MacOS (Intel/Apple Silicon) Nightly Build\n\non:\n  workflow_dispatch:\n    inputs:\n      release_upload_url:\n        "
  },
  {
    "path": ".github/workflows/test-release.yml",
    "chars": 1464,
    "preview": "name: Test Release Build\n\non:\n  workflow_dispatch: # Manual trigger\n\njobs:\n  create-draft:\n    runs-on: ubuntu-latest\n  "
  },
  {
    "path": ".github/workflows/windows-nightly.yml",
    "chars": 2040,
    "preview": "name: Windows (x86_64) Nightly Build\n\non:\n  workflow_dispatch:\n    inputs:\n      release_upload_url:\n        description"
  },
  {
    "path": ".gitignore",
    "chars": 158,
    "preview": ".DS_Store\nnode_modules\n/build\n/.svelte-kit\n/package\n.env\n.env.*\n!.env.example\nvite.config.js.timestamp-*\nvite.config.ts."
  },
  {
    "path": ".husky/pre-commit",
    "chars": 74,
    "preview": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpm exec lint-staged\n"
  },
  {
    "path": ".prettierrc",
    "chars": 121,
    "preview": "{\n  \"plugins\": [\"prettier-plugin-svelte\"],\n  \"overrides\": [{ \"files\": \"*.svelte\", \"options\": { \"parser\": \"svelte\" } }]\n}"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 120,
    "preview": "{\n  \"recommendations\": [\n    \"svelte.svelte-vscode\",\n    \"tauri-apps.tauri-vscode\",\n    \"rust-lang.rust-analyzer\"\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 38,
    "preview": "{\n  \"svelte.enable-ts-plugin\": true\n}\n"
  },
  {
    "path": "LICENSE",
    "chars": 1067,
    "preview": "MIT License\n\nCopyright (c) 2024 Abdenasser\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
  },
  {
    "path": "README.md",
    "chars": 5738,
    "preview": "\n<div align=\"center\">\n  <img src=\"app-icon.png\" alt=\"NeoHtop Logo\" width=\"120\" />\n  <h1>NeoHtop</h1>\n  <p>A modern, cros"
  },
  {
    "path": "docs/index.html",
    "chars": 24418,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, i"
  },
  {
    "path": "docs/main.js",
    "chars": 5770,
    "preview": "// ===============================\n// Theme Management\n// ===============================\nconst themeToggle = document.g"
  },
  {
    "path": "docs/styles.css",
    "chars": 25505,
    "preview": ":root {\n  --primary-color: #6366f1;\n  --secondary-color: #818cf8;\n  --background: #ffffff;\n  --text-primary: #1f2937;\n  "
  },
  {
    "path": "jsconfig.json",
    "chars": 675,
    "preview": "{\n  \"extends\": \"./.svelte-kit/tsconfig.json\",\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"checkJs\": true,\n    \"esMo"
  },
  {
    "path": "package.json",
    "chars": 1512,
    "preview": "{\n  \"name\": \"neohtop\",\n  \"version\": \"1.2.0\",\n  \"description\": \"\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite de"
  },
  {
    "path": "src/App.svelte",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/app.css",
    "chars": 2779,
    "preview": ":root {\n  /* Default theme values will be overridden by theme store */\n  --base: #1e1e2e;\n  --mantle: #181825;\n  --crust"
  },
  {
    "path": "src/app.html",
    "chars": 371,
    "preview": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"icon\" href=\"%sveltekit.assets%/fav"
  },
  {
    "path": "src/lib/components/AppInfo.svelte",
    "chars": 5852,
    "preview": "<script lang=\"ts\">\n  import { getVersion } from \"@tauri-apps/api/app\";\n  import { onMount } from \"svelte\";\n  import { Th"
  },
  {
    "path": "src/lib/components/ThemeSwitcher.svelte",
    "chars": 9789,
    "preview": "<script lang=\"ts\">\n  import { themeStore } from \"$lib/stores\";\n  import { overlayStore } from \"$lib/stores/overlay\";\n  i"
  },
  {
    "path": "src/lib/components/TitleBar.svelte",
    "chars": 846,
    "preview": "<script lang=\"ts\">\n</script>\n\n<div class=\"title-bar\" data-tauri-drag-region>\n  <div class=\"title\">\n    <img src=\"/32x32."
  },
  {
    "path": "src/lib/components/index.ts",
    "chars": 284,
    "preview": "export * from \"./toolbar\";\nexport * from \"./process\";\nexport * from \"./stats\";\nexport * from \"./modals\";\nexport { defaul"
  },
  {
    "path": "src/lib/components/modals/KillProcessModal.svelte",
    "chars": 2460,
    "preview": "<script lang=\"ts\">\n  import { Modal } from \"$lib/components\";\n\n  interface Process {\n    pid: number;\n    name: string;\n"
  },
  {
    "path": "src/lib/components/modals/Modal.svelte",
    "chars": 1604,
    "preview": "<script lang=\"ts\">\n  export let show = false;\n  export let maxWidth = \"600px\";\n  export let title: string;\n  export let "
  },
  {
    "path": "src/lib/components/modals/ProcessDetailsModal.svelte",
    "chars": 12721,
    "preview": "<script lang=\"ts\">\n  import { Modal } from \"$lib/components\";\n  import { formatBytes } from \"$lib/utils\";\n  import type "
  },
  {
    "path": "src/lib/components/modals/index.ts",
    "chars": 203,
    "preview": "export { default as Modal } from \"./Modal.svelte\";\nexport { default as ProcessDetailsModal } from \"./ProcessDetailsModal"
  },
  {
    "path": "src/lib/components/process/ActionButtons.svelte",
    "chars": 3162,
    "preview": "<script lang=\"ts\">\n  import {\n    faThumbtack,\n    faInfoCircle,\n    faXmark,\n  } from \"@fortawesome/free-solid-svg-icon"
  },
  {
    "path": "src/lib/components/process/ProcessIcon.svelte",
    "chars": 2444,
    "preview": "<script lang=\"ts\">\n  import * as SimpleIcons from \"simple-icons\";\n\n  export let processName: string;\n  export let size: "
  },
  {
    "path": "src/lib/components/process/ProcessRow.svelte",
    "chars": 1814,
    "preview": "<script lang=\"ts\">\n  import type { Process, Column } from \"$lib/types\";\n  import { ProcessIcon, ActionButtons } from \"$l"
  },
  {
    "path": "src/lib/components/process/ProcessTable.svelte",
    "chars": 1969,
    "preview": "<script lang=\"ts\">\n  import type { Process, Column } from \"$lib/types\";\n  import { TableHeader, ProcessRow } from \"$lib/"
  },
  {
    "path": "src/lib/components/process/TableHeader.svelte",
    "chars": 1646,
    "preview": "<script lang=\"ts\">\n  import type { Process, Column } from \"$lib/types\";\n\n  export let columns: Column[];\n  export let so"
  },
  {
    "path": "src/lib/components/process/index.ts",
    "chars": 319,
    "preview": "export { default as ProcessTable } from \"./ProcessTable.svelte\";\nexport { default as ProcessRow } from \"./ProcessRow.sve"
  },
  {
    "path": "src/lib/components/stats/CpuPanel.svelte",
    "chars": 1285,
    "preview": "<script lang=\"ts\">\n  import { faMicrochip } from \"@fortawesome/free-solid-svg-icons\";\n  import { PanelHeader, ProgressBa"
  },
  {
    "path": "src/lib/components/stats/MemoryPanel.svelte",
    "chars": 1360,
    "preview": "<script lang=\"ts\">\n  import { faMemory } from \"@fortawesome/free-solid-svg-icons\";\n  import { PanelHeader, ProgressBar, "
  },
  {
    "path": "src/lib/components/stats/NetworkPanel.svelte",
    "chars": 846,
    "preview": "<script lang=\"ts\">\n  import { faNetworkWired } from \"@fortawesome/free-solid-svg-icons\";\n  import { PanelHeader, StatIte"
  },
  {
    "path": "src/lib/components/stats/PanelHeader.svelte",
    "chars": 997,
    "preview": "<script lang=\"ts\">\n  import Fa from \"svelte-fa\";\n  import type { IconDefinition } from \"@fortawesome/free-solid-svg-icon"
  },
  {
    "path": "src/lib/components/stats/ProgressBar.svelte",
    "chars": 1603,
    "preview": "<script lang=\"ts\">\n  export let label: string;\n  export let value: number;\n  export let labelWidth = \"2.5rem\";\n  export "
  },
  {
    "path": "src/lib/components/stats/StatItem.svelte",
    "chars": 503,
    "preview": "<script lang=\"ts\">\n  export let label: string;\n  export let value: string;\n</script>\n\n<div class=\"stat-item\">\n  <span>{l"
  },
  {
    "path": "src/lib/components/stats/StatPanel.svelte",
    "chars": 586,
    "preview": "<script lang=\"ts\">\n  export let title: string;\n  export let flex = 1;\n</script>\n\n<div class=\"stat-panel\" style=\"--flex: "
  },
  {
    "path": "src/lib/components/stats/StatsBar.svelte",
    "chars": 1152,
    "preview": "<script lang=\"ts\">\n  import type { SystemStats } from \"$lib/types\";\n  import {\n    CpuPanel,\n    MemoryPanel,\n    Storag"
  },
  {
    "path": "src/lib/components/stats/StoragePanel.svelte",
    "chars": 1075,
    "preview": "<script lang=\"ts\">\n  import { faHardDrive } from \"@fortawesome/free-solid-svg-icons\";\n  import { PanelHeader, StatItem }"
  },
  {
    "path": "src/lib/components/stats/SystemPanel.svelte",
    "chars": 949,
    "preview": "<script lang=\"ts\">\n  import { faServer } from \"@fortawesome/free-solid-svg-icons\";\n  import { PanelHeader, StatItem } fr"
  },
  {
    "path": "src/lib/components/stats/index.ts",
    "chars": 553,
    "preview": "export { default as StatsBar } from \"./StatsBar.svelte\";\nexport { default as CpuPanel } from \"./CpuPanel.svelte\";\nexport"
  },
  {
    "path": "src/lib/components/toolbar/ColumnToggle.svelte",
    "chars": 8502,
    "preview": "<script lang=\"ts\">\n  import Fa from \"svelte-fa\";\n  import {\n    faChevronDown,\n    faChevronRight,\n    faChevronLeft,\n  "
  },
  {
    "path": "src/lib/components/toolbar/FilterToggle.svelte",
    "chars": 14023,
    "preview": "<script lang=\"ts\">\n  import Fa from \"svelte-fa\";\n  import { faFilter } from \"@fortawesome/free-solid-svg-icons\";\n  impor"
  },
  {
    "path": "src/lib/components/toolbar/PaginationControls.svelte",
    "chars": 7739,
    "preview": "<script lang=\"ts\">\n  import type { AppConfig } from \"$lib/types\";\n  import { settingsStore } from \"$lib/stores/index\";\n "
  },
  {
    "path": "src/lib/components/toolbar/RefreshControls.svelte",
    "chars": 7212,
    "preview": "<script lang=\"ts\">\n  import Fa from \"svelte-fa\";\n  import { faPlay, faPause } from \"@fortawesome/free-solid-svg-icons\";\n"
  },
  {
    "path": "src/lib/components/toolbar/SearchBox.svelte",
    "chars": 11533,
    "preview": "<script lang=\"ts\">\n  import { overlayStore } from \"$lib/stores/overlay\";\n  import { onDestroy, onMount } from \"svelte\";\n"
  },
  {
    "path": "src/lib/components/toolbar/StatusFilter.svelte",
    "chars": 6007,
    "preview": "<script lang=\"ts\">\n  import { STATUS_OPTIONS } from \"$lib/constants\";\n  import type { AppConfig } from \"$lib/types\";\n  i"
  },
  {
    "path": "src/lib/components/toolbar/ToolBar.svelte",
    "chars": 2783,
    "preview": "<script lang=\"ts\">\n  import {\n    AppInfo,\n    SearchBox,\n    RefreshControls,\n    PaginationControls,\n    ColumnToggle,"
  },
  {
    "path": "src/lib/components/toolbar/index.ts",
    "chars": 457,
    "preview": "export { default as ToolBar } from \"./ToolBar.svelte\";\nexport { default as SearchBox } from \"./SearchBox.svelte\";\nexport"
  },
  {
    "path": "src/lib/constants/index.ts",
    "chars": 1779,
    "preview": "export const ASCII_ART = `\n███╗   ██╗███████╗ ██████╗ ██╗  ██╗████████╗ ██████╗ ██████╗ \n████╗  ██║██╔════╝██╔═══██╗██║ "
  },
  {
    "path": "src/lib/definitions/columns.ts",
    "chars": 1901,
    "preview": "import type { Column } from \"$lib/types\";\nimport { formatMemorySize } from \"$lib/utils\";\n\nexport let column_definitions:"
  },
  {
    "path": "src/lib/definitions/index.ts",
    "chars": 81,
    "preview": "export * from \"./columns\";\nexport * from \"./settings\";\nexport * from \"./themes\";\n"
  },
  {
    "path": "src/lib/definitions/settings.ts",
    "chars": 568,
    "preview": "import type { AppConfig } from \"$lib/types\";\n\nexport const DEFAULT_CONFIG: AppConfig = {\n  appearance: {\n    columnVisib"
  },
  {
    "path": "src/lib/definitions/themes.ts",
    "chars": 18638,
    "preview": "import type { Theme } from \"$lib/types\";\n\nexport const themes: Record<string, Theme> = {\n  catppuccin: {\n    name: \"catp"
  },
  {
    "path": "src/lib/stores/index.ts",
    "chars": 109,
    "preview": "export * from \"./processes\";\nexport * from \"./theme\";\nexport * from \"./settings\";\nexport * from \"./overlay\";\n"
  },
  {
    "path": "src/lib/stores/overlay.ts",
    "chars": 655,
    "preview": "import { writable } from \"svelte/store\";\n\ntype OverlayType =\n  | \"pagination\"\n  | \"refresh\"\n  | \"columns\"\n  | \"theme\"\n  "
  },
  {
    "path": "src/lib/stores/processes.ts",
    "chars": 5289,
    "preview": "import { writable, derived } from \"svelte/store\";\nimport type { Process, SystemStats } from \"$lib/types\";\nimport { invok"
  },
  {
    "path": "src/lib/stores/settings.ts",
    "chars": 1065,
    "preview": "import { writable } from \"svelte/store\";\nimport type { AppConfig } from \"$lib/types\";\nimport { DEFAULT_CONFIG } from \"$l"
  },
  {
    "path": "src/lib/stores/theme.ts",
    "chars": 1732,
    "preview": "import { writable } from \"svelte/store\";\nimport { themes } from \"$lib/definitions/themes\";\nimport type { Theme } from \"$"
  },
  {
    "path": "src/lib/types/index.ts",
    "chars": 2219,
    "preview": "// Create a new types file to centralize interfaces\nexport interface Process {\n  pid: number;\n  ppid: number;\n  name: st"
  },
  {
    "path": "src/lib/utils/index.ts",
    "chars": 7250,
    "preview": "import type { Process } from \"$lib/types\";\nimport type { SortConfig } from \"$lib/types\";\n\nexport interface ProcessStatus"
  },
  {
    "path": "src/routes/+layout.js",
    "chars": 242,
    "preview": "// Tauri doesn't have a Node.js server to do proper SSR\n// so we will use adapter-static to prerender the app (SSG)\n// S"
  },
  {
    "path": "src/routes/+layout.svelte",
    "chars": 52,
    "preview": "<script>\n  import \"../app.css\";\n</script>\n\n<slot />\n"
  },
  {
    "path": "src/routes/+page.svelte",
    "chars": 7285,
    "preview": "<script lang=\"ts\">\n  import { onMount, onDestroy } from \"svelte\";\n  import { debounce } from \"$lib/utils\";\n  import {\n  "
  },
  {
    "path": "src-tauri/.cargo/config.toml",
    "chars": 224,
    "preview": "[target.x86_64-apple-darwin]\nrustflags = [\n  \"-C\", \"link-arg=-undefined\",\n  \"-C\", \"link-arg=dynamic_lookup\",\n]\n\n[target."
  },
  {
    "path": "src-tauri/.gitignore",
    "chars": 166,
    "preview": "# Generated by Cargo\n# will have compiled files and executables\n/target/\n\n# Generated by Tauri\n# will have schema files "
  },
  {
    "path": "src-tauri/Cargo.toml",
    "chars": 1025,
    "preview": "[package]\nname = \"neohtop\"\nversion = \"1.2.0\"\ndescription = \"A cross-platform system monitor\"\nauthors = [\"you\"]\nedition ="
  },
  {
    "path": "src-tauri/build.rs",
    "chars": 39,
    "preview": "fn main() {\n    tauri_build::build()\n}\n"
  },
  {
    "path": "src-tauri/capabilities/default.json",
    "chars": 395,
    "preview": "{\n  \"$schema\": \"../gen/schemas/desktop-schema.json\",\n  \"identifier\": \"default\",\n  \"description\": \"Capability for the mai"
  },
  {
    "path": "src-tauri/src/commands.rs",
    "chars": 2155,
    "preview": "//! Tauri command handlers\n//!\n//! This module contains the command handlers that are exposed to the frontend\n//! throug"
  },
  {
    "path": "src-tauri/src/main.rs",
    "chars": 1352,
    "preview": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")]\n//! NeoHtop - A modern system monitor built with Taur"
  },
  {
    "path": "src-tauri/src/monitoring/mod.rs",
    "chars": 432,
    "preview": "//! System monitoring functionality\n//!\n//! This module provides types and functionality for monitoring system resources"
  },
  {
    "path": "src-tauri/src/monitoring/process_monitor.rs",
    "chars": 6189,
    "preview": "//! Process monitoring functionality\n//!\n//! This module handles monitoring and managing system processes, including\n//!"
  },
  {
    "path": "src-tauri/src/monitoring/system_monitor.rs",
    "chars": 4902,
    "preview": "//! System statistics monitoring\n//!\n//! This module handles collection and monitoring of system-wide statistics\n//! inc"
  },
  {
    "path": "src-tauri/src/monitoring/types.rs",
    "chars": 3678,
    "preview": "use serde::Serialize;\nuse std::fmt::Debug;\nuse sysinfo::{DiskUsage, ProcessStatus};\n\n/// Internal representation of proc"
  },
  {
    "path": "src-tauri/src/state.rs",
    "chars": 1520,
    "preview": "//! Application state management\n//!\n//! This module handles the global application state, including system monitoring\n/"
  },
  {
    "path": "src-tauri/src/ui/mod.rs",
    "chars": 213,
    "preview": "//! User interface functionality\n//!\n//! This module handles UI-specific functionality, including window effects\n//! and"
  },
  {
    "path": "src-tauri/src/ui/window.rs",
    "chars": 1112,
    "preview": "//! Window effects and customization\n//!\n//! Provides platform-specific window effects like transparency and vibrancy.\n\n"
  },
  {
    "path": "src-tauri/tauri.conf.json",
    "chars": 1172,
    "preview": "{\n  \"$schema\": \"../node_modules/@tauri-apps/cli/config.schema.json\",\n  \"build\": {\n    \"beforeBuildCommand\": \"npm run bui"
  },
  {
    "path": "svelte.config.js",
    "chars": 497,
    "preview": "// Tauri doesn't have a Node.js server to do proper SSR\n// so we will use adapter-static to prerender the app (SSG)\n// S"
  },
  {
    "path": "vite.config.js",
    "chars": 836,
    "preview": "import { defineConfig } from \"vite\";\nimport { sveltekit } from \"@sveltejs/kit/vite\";\n\nconst host = process.env.TAURI_DEV"
  }
]

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

About this extraction

This page contains the full source code of the Abdenasser/neohtop GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 95 files (276.9 KB), approximately 79.4k tokens, and a symbol index with 72 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!