mirror of
https://github.com/dmduran12/pymc_console-dist.git
synced 2026-08-09 18:32:48 +02:00
Release v0.9.224
Automated sync from private repository. Built with obfuscation enabled.
This commit is contained in:
@@ -3,32 +3,78 @@
|
||||
[](https://github.com/dmduran12/pymc_console-dist/releases)
|
||||
[](LICENSE)
|
||||
|
||||
A modern web dashboard for monitoring and managing your [MeshCore](https://meshcore.co.uk/) LoRa mesh repeater.
|
||||
A real-time web dashboard for [MeshCore](https://meshcore.co.uk/) LoRa mesh repeaters.
|
||||
|
||||
Built on [pyMC_Repeater](https://github.com/rightup/pyMC_Repeater) by [RightUp](https://github.com/rightup), pyMC Console provides real-time visibility into your mesh network with an intuitive, feature-rich interface.
|
||||
pyMC Console gives you full visibility into your mesh network — packet flow, topology, signal quality, RF metrics, and radio configuration — through a single browser tab. It layers on top of [pyMC_Repeater](https://github.com/rightup/pyMC_Repeater) without modifying it.
|
||||
|
||||
---
|
||||
|
||||
## How It All Fits Together
|
||||
## Quick Start
|
||||
|
||||
The MeshCore Python ecosystem has three components:
|
||||
### Requirements
|
||||
|
||||
- Raspberry Pi (3, 4, 5, or Zero 2 W)
|
||||
- LoRa radio module (SX1262 or SX1276)
|
||||
- Raspberry Pi OS (Bookworm recommended)
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
git clone https://github.com/dmduran12/pymc_console-dist.git pymc_console
|
||||
cd pymc_console
|
||||
sudo bash manage.sh install
|
||||
```
|
||||
|
||||
During installation you'll be prompted to select a pyMC_Repeater branch — **choose `dev`** (the default). The `dev` branch has the latest features and is the recommended target.
|
||||
|
||||
The installer handles everything:
|
||||
|
||||
1. System dependencies (Python, pip, SPI tools)
|
||||
2. Clones and installs [pyMC_Repeater](https://github.com/rightup/pyMC_Repeater) + [pyMC_core](https://github.com/rightup/pyMC_core)
|
||||
3. Deploys the Console web dashboard
|
||||
4. Configures and starts the systemd service
|
||||
|
||||
Once complete, open `http://<your-pi-ip>:8000` in a browser.
|
||||
|
||||
### Upgrade
|
||||
|
||||
```bash
|
||||
cd pymc_console
|
||||
sudo bash manage.sh upgrade
|
||||
```
|
||||
|
||||
Choose **Console only** (updates just the dashboard) or **Full Stack** (updates Console + pyMC_Repeater + pyMC_core). Console-only upgrades are safe and fast — your repeater config is untouched.
|
||||
|
||||
### Uninstall
|
||||
|
||||
```bash
|
||||
cd pymc_console
|
||||
sudo bash manage.sh
|
||||
```
|
||||
|
||||
Select **Uninstall** from the menu. Removes all installed files, config, logs, and the systemd service.
|
||||
|
||||
---
|
||||
|
||||
## How It Fits Together
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ pyMC Console │
|
||||
│ (this repo - web dashboard UI) │
|
||||
│ (this repo — web dashboard UI) │
|
||||
│ │
|
||||
│ • React dashboard served on port 8000 │
|
||||
│ • Visualizes packets, topology, stats │
|
||||
│ • React SPA served on port 8000 │
|
||||
│ • Real-time packets, topology, stats, radio config │
|
||||
│ • manage.sh installer handles everything │
|
||||
└─────────────────────┬───────────────────────────────────────┘
|
||||
│ uses API from
|
||||
┌─────────────────────▼───────────────────────────────────────┐
|
||||
│ pyMC_Repeater │
|
||||
│ (RightUp's repeater daemon) │
|
||||
│ pyMC_Repeater │
|
||||
│ (RightUp's repeater daemon) │
|
||||
│ │
|
||||
│ • Python daemon that runs the repeater │
|
||||
│ • Provides REST API on port 8000 │
|
||||
│ • Handles packet forwarding, logging, config │
|
||||
│ • Python daemon running the LoRa repeater │
|
||||
│ • REST API + WebSocket on port 8000 │
|
||||
│ • Packet forwarding, logging, radio control │
|
||||
└─────────────────────┬───────────────────────────────────────┘
|
||||
│ built on
|
||||
┌─────────────────────▼───────────────────────────────────────┐
|
||||
@@ -42,150 +88,99 @@ The MeshCore Python ecosystem has three components:
|
||||
```
|
||||
|
||||
**Key points:**
|
||||
- **Console does NOT replace Repeater** — they work together
|
||||
- Console's `manage.sh` installs both Repeater and Console side-by-side
|
||||
- Console provides the web UI; Repeater provides the backend API and radio control
|
||||
- You can upgrade Console independently without touching Repeater/Core
|
||||
- Console does **not** replace Repeater — they work together
|
||||
- `manage.sh` installs both Repeater and Console side-by-side
|
||||
- You can upgrade Console independently without touching Repeater
|
||||
|
||||
## Feature Highlights
|
||||
---
|
||||
|
||||
### 🗺️ Mesh Topology Analysis
|
||||
## Features
|
||||
|
||||
The topology analyzer reconstructs your network's structure from packet paths using a **Viterbi HMM decoder** — resolving prefix collisions with physics-based constraints and observation evidence.
|
||||
### Topology Analysis
|
||||
|
||||
Reconstructs your network's structure from packet paths using a **Viterbi HMM decoder** — resolving prefix collisions with physics-based RF constraints and real-world observation evidence.
|
||||
|
||||

|
||||
|
||||
- **Deep Analysis** — One-click full topology rebuild from 75K packets
|
||||
- **Viterbi HMM decoding** — Hidden Markov Model resolves prefix collisions using geographic distance and LoRa range constraints
|
||||
- **Ghost node discovery** — Detects unknown repeaters when no known candidate is geographically plausible
|
||||
- **3D terrain mode** — Real-world topographic terrain with hillshading; markers and edges drape onto the landscape
|
||||
- **3D arc edges** — Topology edges rendered as elevated arcs via deck.gl (GPU-accelerated)
|
||||
- **Edge confidence visualization** — Line thickness scales with observation count; color indicates certainty
|
||||
- **Wardriving overlay** — H3 hexagonal tiles from coverage servers with SNR-based coloring
|
||||
- **One-click Deep Analysis** from up to 14 days of packet history
|
||||
- **Viterbi HMM decoding** — resolves 2-char prefix collisions using geographic distance and LoRa range constraints
|
||||
- **Ghost node discovery** — detects unknown repeaters when no known candidate fits
|
||||
- **7-phase topology pipeline** — directional edges, betweenness centrality, mobile detection, TX delay recommendations, path health scoring
|
||||
- **3D terrain** — AWS Terrarium elevation with hillshading; markers and edges drape onto the landscape
|
||||
- **Wardriving overlay** — H3 hexagonal coverage tiles with SNR-based coloring
|
||||
- **Edge confidence** — line thickness scales with observation count; color indicates certainty
|
||||
|
||||
### 📡 Link Quality Radar
|
||||
### Link Quality Radar
|
||||
|
||||
The polar chart visualizes all contacts at their actual compass bearing and distance from your node.
|
||||
Polar chart placing all contacts at their actual compass bearing and distance from your node.
|
||||
|
||||

|
||||
|
||||
- **Zero-hop neighbors** — Colored by SNR quality (green → yellow → orange → red)
|
||||
- **Non-neighbors** — Rendered at 33% opacity to distinguish direct RF contacts
|
||||
- **Hover interaction** — Full opacity with detailed signal metrics tooltip
|
||||
- **Zero-hop neighbors** colored by SNR (green → yellow → orange → red)
|
||||
- **Multi-hop contacts** at 33% opacity to distinguish direct RF from relayed
|
||||
- **Hover tooltips** with full signal metrics (RSSI, SNR, distance, last seen)
|
||||
|
||||
### 📊 Statistics Dashboard
|
||||
### Statistics Dashboard
|
||||
|
||||
Comprehensive RF metrics and network composition analysis.
|
||||
|
||||

|
||||
|
||||
- **Airtime utilization** — RX/TX spectrum with peak and mean metrics
|
||||
- **Packet types treemap** — Distribution of ADVERT, TXT_MSG, ACK, RESPONSE, etc.
|
||||
- **Network composition** — Breakdown of repeaters, companions, and room servers
|
||||
- **RF noise floor** — Heatmap showing interference patterns over time
|
||||
- **Prefix disambiguation health** — Confidence metrics for topology inference
|
||||
- **Airtime utilization** — RX/TX stacked area charts with peak and mean metrics
|
||||
- **Packet type distribution** — treemap of ADVERT, TXT_MSG, ACK, TRACE, etc.
|
||||
- **Network composition** — repeater / companion / room server breakdown
|
||||
- **Noise floor heatmap** — interference patterns over time
|
||||
- **Disambiguation health** — Viterbi confidence metrics and ghost node stats
|
||||
- **TX delay recommendations** — slot-based timing optimization per node role
|
||||
|
||||
### 🛤️ Packet Path Tracing
|
||||
### Packet Path Tracing
|
||||
|
||||
Click any packet to visualize its route through the mesh with hop-by-hop confidence indicators.
|
||||
Click any packet to visualize its route through the mesh with hop-by-hop confidence.
|
||||
|
||||

|
||||
|
||||
- **Confidence coloring** — Green (100%), Yellow (50-99%), Orange (25-49%), Red (<25%), Gray (ghost node)
|
||||
- **Interactive map** — Shows the resolved path with all intermediate hops
|
||||
- **Signal details** — RSSI, SNR, and timing for each packet
|
||||
- **Confidence coloring** — Green (100%), Yellow (50–99%), Orange (25–49%), Red (<25%), Gray (ghost)
|
||||
- **Interactive map** showing resolved path with intermediate hops
|
||||
- **Signal details** — RSSI, SNR, and timing per packet
|
||||
- **Byte-level breakdown** — header fields, payload structure, raw hex
|
||||
|
||||
### 🎨 Themes & Terminal
|
||||
### Themes & Terminal
|
||||
|
||||
Six color schemes and a built-in terminal for direct repeater interaction.
|
||||
Two polished color schemes and a built-in CLI for direct repeater interaction.
|
||||
|
||||

|
||||
|
||||
- **Color schemes** — Seoul256, Gruvbox, Deus, Gotham, Sonokai, Kanagawa
|
||||
- **Background images** — Multiple ambient backgrounds with adjustable brightness
|
||||
- **Terminal** — Direct CLI access to the repeater for advanced operations
|
||||
- **Live logs** — Stream from repeater daemon with DEBUG/INFO toggle
|
||||
- **Breeze Dark / Breeze Light** — KDE Breeze-inspired themes with full design token system
|
||||
- **Terminal** — interactive CLI mapped to API endpoints (get/set radio, ping, diagnostics)
|
||||
- **Packet capture** — `start cap` / `end cap` / `export cap` for timed diagnostic snapshots
|
||||
- **Live logs** — streaming from repeater with DEBUG/INFO toggle
|
||||
|
||||
---
|
||||
|
||||
## All Features
|
||||
## All Pages at a Glance
|
||||
|
||||
### Dashboard
|
||||
- **Live packet metrics** — Received, forwarded, dropped packets with sparkline charts
|
||||
- **TX Delay Recommendations** — Slot-based delay optimization with network role classification (edge/relay/hub/backbone)
|
||||
- **LBT Insights widgets** — Channel health, collision risk, noise floor, link quality at a glance
|
||||
- **Time range selector** — View stats from 20 minutes to 14 days
|
||||
- **Recent packets** — Live feed of incoming traffic with centralized polling (every 3s)
|
||||
| Page | Route | What it does |
|
||||
|------|-------|-------------|
|
||||
| **Dashboard** | `/` | Live packet counters, sparkline trends, LBT widgets, recent packets |
|
||||
| **Packets** | `/packets` | Searchable packet history with detail modal, path visualization, byte breakdown |
|
||||
| **Contacts** | `/contacts` | MapLibre GL map with topology edges, ghost nodes, terrain, wardriving overlay |
|
||||
| **Statistics** | `/statistics` | µPlot charts — airtime, packet types, noise floor, signal scatter, network composition |
|
||||
| **Mesh Graph** | `/meshgraph` | GPU-accelerated force-directed graph (Cosmograph) |
|
||||
| **System** | `/system` | CPU, memory, disk, temperature, processes, network I/O |
|
||||
| **Logs** | `/logs` | Live log stream with level filtering |
|
||||
| **Terminal** | `/terminal` | Interactive CLI — MeshCore commands, ping, diagnostics, captures |
|
||||
| **Configuration** | `/configuration` | Radio settings, TX delays, transport keys, identity, theme, stealth location |
|
||||
|
||||
### Contacts & Topology
|
||||
- **Interactive map** — MapLibre GL with dark theme, smooth animations
|
||||
- **Filter toggles** — Solo view for hub nodes, direct neighbors, or traffic-based filtering
|
||||
- **Loop detection** — Identifies redundant paths (double-line rendering)
|
||||
- **High-contrast markers** — Light fill with dark outline ensures visibility against any overlay
|
||||
- **Path health panel** — Health scores, weakest links, and latency estimates
|
||||
- **Mobile node detection** — Identifies volatile nodes that appear/disappear frequently
|
||||
---
|
||||
|
||||
### Packets
|
||||
- **Searchable history** — Filter by type, route, time range
|
||||
- **Packet details** — Hash, path, payload, signal info, duplicates
|
||||
- **Path visualization** — Interactive map showing packet route with hop confidence
|
||||
## Management
|
||||
|
||||
### Settings
|
||||
- **Mode toggle** — Forward (repeating) or Monitor (RX only)
|
||||
- **Duty cycle** — Enable/disable enforcement
|
||||
- **Radio config** — Live frequency, power, SF, bandwidth changes
|
||||
|
||||
### System & Logs
|
||||
- **System resources** — 20-minute rolling CPU and memory utilization chart
|
||||
- **Disk usage** — Storage utilization with progress bar
|
||||
- **Temperature** — Multi-sensor temperature gauges with threshold coloring
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Requirements
|
||||
|
||||
- Raspberry Pi (3, 4, 5, or Zero 2 W)
|
||||
- LoRa module (SX1262 or SX1276 based)
|
||||
- Raspbian/Raspberry Pi OS (Bookworm recommended)
|
||||
|
||||
### Installation
|
||||
### manage.sh Menu
|
||||
|
||||
```bash
|
||||
# Clone this repository
|
||||
git clone https://github.com/dmduran12/pymc_console-dist.git pymc_console
|
||||
cd pymc_console
|
||||
|
||||
# Run the installer (requires sudo)
|
||||
sudo bash manage.sh install
|
||||
sudo bash manage.sh
|
||||
```
|
||||
|
||||
> **Note: Branch Selection**
|
||||
>
|
||||
> During installation, you'll be asked to select a pyMC_Repeater branch. **Select `dev`** (the default/recommended option). The `dev` branch contains the latest features and improvements.
|
||||
>
|
||||
|
||||
|
||||
The installer will:
|
||||
1. Install all system dependencies (Python, pip, etc.)
|
||||
2. Clone and install [pyMC_Repeater](https://github.com/rightup/pyMC_Repeater) as a sibling directory
|
||||
3. Install [pyMC_core](https://github.com/rightup/pyMC_core) (the protocol library)
|
||||
4. Deploy the Console web dashboard
|
||||
5. Configure and start the systemd service
|
||||
|
||||
**After installation, your directory structure looks like:**
|
||||
```
|
||||
~/pymc_console/ ← Console (this repo)
|
||||
~/pyMC_Repeater/ ← Repeater daemon (cloned by installer)
|
||||
/opt/pymc_repeater/ ← Installed repeater files
|
||||
/opt/pymc_console/ ← Installed console files
|
||||
```
|
||||
|
||||
Once complete, access your dashboard at `http://<your-pi-ip>:8000`
|
||||
|
||||
## Management Menu
|
||||
|
||||
After installation, run `sudo bash manage.sh` to access the management menu:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────┐
|
||||
│ pyMC Console │
|
||||
@@ -200,15 +195,13 @@ After installation, run `sudo bash manage.sh` to access the management menu:
|
||||
└────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Menu Options
|
||||
- **Install** — Full Stack or Console-only
|
||||
- **Upgrade** — Console-only (fast, safe) or Full Stack (Console + Repeater + Core)
|
||||
- **Uninstall** — Removes everything (installation, config, logs, service)
|
||||
- **Status** — Installed versions of Core, Repeater, Console
|
||||
- **Logs** — Live journal output from the repeater
|
||||
|
||||
- **Install** — Fresh installation (Full Stack or Console-only)
|
||||
- **Upgrade** — Update Console dashboard and/or full pyMC stack
|
||||
- **Uninstall** — Remove Console, Repeater, and cloned repos
|
||||
- **Status** — Show installed versions (Core, Repeater, Console)
|
||||
- **Logs** — Live log output from the repeater
|
||||
|
||||
### Radio/GPIO Configuration
|
||||
### Radio & GPIO Configuration
|
||||
|
||||
Radio and GPIO settings are managed by upstream pyMC_Repeater:
|
||||
|
||||
@@ -216,225 +209,208 @@ Radio and GPIO settings are managed by upstream pyMC_Repeater:
|
||||
cd ~/pyMC_Repeater && sudo ./manage.sh
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
To update to the latest version:
|
||||
|
||||
```bash
|
||||
cd pymc_console
|
||||
sudo bash manage.sh upgrade
|
||||
```
|
||||
|
||||
Or use the interactive menu and select **Upgrade**, then choose:
|
||||
- **Console only** — Updates just the web dashboard (quick, safe)
|
||||
- **Full Stack** — Updates Console + pulls latest pyMC_Repeater/pyMC_core
|
||||
|
||||
**Note:** You can safely upgrade Console without affecting your Repeater installation. This is useful when you want new dashboard features but your repeater is running stable.
|
||||
|
||||
The script auto-updates itself before upgrading, so you always get the latest installer.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Radio Settings
|
||||
|
||||
Radio configuration is handled by upstream pyMC_Repeater. Use their management script:
|
||||
|
||||
```bash
|
||||
cd ~/pyMC_Repeater && sudo ./manage.sh
|
||||
```
|
||||
|
||||
Or edit the config directly:
|
||||
|
||||
```yaml
|
||||
# /etc/pymc_repeater/config.yaml
|
||||
radio:
|
||||
frequency: 927875000 # Frequency in Hz
|
||||
spreading_factor: 7 # SF7-SF12
|
||||
bandwidth: 62500 # Bandwidth in Hz
|
||||
tx_power: 28 # TX power in dBm
|
||||
coding_rate: 6 # 4/5, 4/6, 4/7, or 4/8
|
||||
frequency: 927875000 # Hz
|
||||
spreading_factor: 7 # SF7–SF12
|
||||
bandwidth: 62500 # Hz
|
||||
tx_power: 28 # dBm
|
||||
coding_rate: 6 # 4/5, 4/6, 4/7, 4/8
|
||||
```
|
||||
|
||||
### Advanced: DIO2 and DIO3 Configuration
|
||||
> You can also change radio settings live from the **Configuration** page in the dashboard — no SSH required.
|
||||
|
||||
Some LoRa modules require specific DIO pin configurations. These are **independent settings** that serve different purposes:
|
||||
### DIO2 / DIO3 Pin Configuration
|
||||
|
||||
- **DIO3 (TCXO)** — Temperature Compensated Crystal Oscillator control. Set `use_dio3_tcxo: true` if your module has a TCXO that needs DIO3 for voltage supply.
|
||||
- **DIO2 (RF Switch)** — Antenna RF switch control for TX/RX path switching. Set `use_dio2_rf: true` if your module requires DIO2 to control an external RF switch.
|
||||
Some LoRa modules need specific DIO pin settings. These are **independent** — enabling one does not affect the other:
|
||||
|
||||
> **Note:** These settings are available on the pyMC_Repeater `dev` branch (which uses pyMC_core `dev`). The `main` branch hardcodes `setDio2RfSwitch(False)`.
|
||||
- **DIO3 (TCXO)** — set `use_dio3_tcxo: true` if your module has a temperature-compensated oscillator on DIO3
|
||||
- **DIO2 (RF Switch)** — set `use_dio2_rf: true` if your module uses DIO2 for TX/RX antenna switching
|
||||
|
||||
Example for modules requiring both:
|
||||
```yaml
|
||||
radio:
|
||||
use_dio3_tcxo: true # Enable TCXO via DIO3
|
||||
use_dio2_rf: true # Enable RF switch via DIO2 (dev branch only)
|
||||
use_dio3_tcxo: true
|
||||
use_dio2_rf: true # dev branch only
|
||||
```
|
||||
|
||||
**Important:** Setting `use_dio3_tcxo: true` does NOT automatically enable DIO2. They are independent configurations for different hardware features.
|
||||
|
||||
### Service Management
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
sudo systemctl status pymc-repeater
|
||||
|
||||
# Start/stop/restart
|
||||
sudo systemctl start pymc-repeater
|
||||
sudo systemctl stop pymc-repeater
|
||||
sudo systemctl restart pymc-repeater
|
||||
|
||||
# View live logs
|
||||
sudo journalctl -u pymc-repeater -f
|
||||
sudo systemctl status pymc-repeater # Check status
|
||||
sudo systemctl restart pymc-repeater # Restart
|
||||
sudo journalctl -u pymc-repeater -f # Live logs
|
||||
```
|
||||
|
||||
## Hardware Requirements
|
||||
---
|
||||
|
||||
- **Raspberry Pi** (3, 4, 5, or Zero 2 W recommended)
|
||||
- **LoRa Module** — SX1262 or SX1276 based (e.g., Waveshare SX1262, LILYGO T3S3)
|
||||
- **SPI Connection** — Module connected via SPI with GPIO for reset/busy/DIO1
|
||||
## Directory Layout
|
||||
|
||||
### Tested Modules
|
||||
After installation:
|
||||
|
||||
```
|
||||
~/pymc_console/ ← This repo (cloned by you)
|
||||
~/pyMC_Repeater/ ← Repeater daemon (cloned by manage.sh)
|
||||
|
||||
/opt/pymc_repeater/ ← Installed repeater
|
||||
/opt/pymc_console/web/html/ ← Installed dashboard
|
||||
/etc/pymc_repeater/config.yaml ← Radio + repeater configuration
|
||||
/var/log/pymc_repeater/ ← Log files
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hardware
|
||||
|
||||
### Supported Boards
|
||||
|
||||
- Raspberry Pi 3, 4, 5
|
||||
- Raspberry Pi Zero 2 W
|
||||
- Any SBC with SPI and GPIO (untested but likely works)
|
||||
|
||||
### Tested Radio Modules
|
||||
|
||||
- Waveshare SX1262 HAT
|
||||
- LILYGO T3S3 (via USB serial)
|
||||
- Ebyte E22 modules
|
||||
- LILYGO T3S3 (via USB serial)
|
||||
- Heltec LoRa 32
|
||||
|
||||
### Connection
|
||||
|
||||
LoRa module connects via **SPI** with GPIO pins for reset, busy, and DIO1. Pin mapping is configured during installation via pyMC_Repeater's manage.sh.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Error 200" or Login Issues
|
||||
### Dashboard won't load
|
||||
|
||||
This can occur with older installations or mismatched versions.
|
||||
1. **Is the service running?**
|
||||
```bash
|
||||
sudo systemctl status pymc-repeater
|
||||
```
|
||||
2. **Is port 8000 responding?**
|
||||
```bash
|
||||
curl -s http://localhost:8000/api/stats | head -c 100
|
||||
```
|
||||
3. **Check for errors:**
|
||||
```bash
|
||||
sudo journalctl -u pymc-repeater -n 50
|
||||
```
|
||||
|
||||
### Login fails / "Error 200"
|
||||
|
||||
Usually caused by a version mismatch between Console and Repeater.
|
||||
|
||||
**To fix:**
|
||||
```bash
|
||||
cd pymc_console
|
||||
sudo bash manage.sh upgrade
|
||||
```
|
||||
|
||||
Select **Full pyMC Stack** upgrade to update pyMC_Repeater and pyMC_core to the latest versions.
|
||||
Select **Full pyMC Stack** to update everything to compatible versions.
|
||||
|
||||
### No packets being received
|
||||
|
||||
1. **SPI enabled?**
|
||||
```bash
|
||||
ls /dev/spidev*
|
||||
```
|
||||
If no devices listed, enable SPI via `raspi-config` → Interface Options → SPI.
|
||||
|
||||
2. **GPIO correct?** Run pyMC_Repeater's manage.sh → Configure GPIO and verify pin assignments match your wiring.
|
||||
|
||||
3. **Frequency match?** Confirm your radio frequency matches the rest of your mesh network.
|
||||
|
||||
4. **Check the logs:**
|
||||
```bash
|
||||
sudo journalctl -u pymc-repeater -n 100 | grep -i "error\|fail\|radio"
|
||||
```
|
||||
|
||||
### Service won't start
|
||||
|
||||
```bash
|
||||
# Check for errors
|
||||
sudo journalctl -u pymc-repeater -n 50
|
||||
# Check for config syntax errors
|
||||
python3 -c "import yaml; yaml.safe_load(open('/etc/pymc_repeater/config.yaml'))"
|
||||
|
||||
# Verify config syntax
|
||||
cat /etc/pymc_repeater/config.yaml
|
||||
# Check for Python dependency issues
|
||||
pip3 show pymc-repeater pymc-core
|
||||
```
|
||||
|
||||
### No packets being received
|
||||
### "Radio presets file not found" during install
|
||||
|
||||
1. Verify SPI is enabled: `ls /dev/spidev*`
|
||||
2. Check GPIO configuration in manage.sh → Configure GPIO
|
||||
3. Confirm frequency matches your network
|
||||
Non-fatal warning. The installer fetches presets from an API; if unavailable, it falls back to common defaults. Installation continues normally.
|
||||
|
||||
### Dashboard not loading
|
||||
### Dashboard loads but shows no data
|
||||
|
||||
1. Verify service is running: `sudo systemctl status pymc-repeater`
|
||||
2. Check if port 8000 is accessible: `curl http://localhost:8000/api/stats`
|
||||
- The dashboard requires authentication. If you see the login page, use the credentials you set during pyMC_Repeater installation.
|
||||
- If you're on a fresh install, allow 30–60 seconds for the repeater to initialize and begin receiving packets.
|
||||
- Check that WebSocket is connecting: open browser DevTools → Network → WS. You should see an active `/ws/packets` connection.
|
||||
|
||||
### "Radio presets file not found" warning
|
||||
### Upgrade didn't take effect
|
||||
|
||||
This warning during installation is non-fatal. The installer will continue and you can configure radio settings manually. The presets are fetched from an API; if the API is unavailable, common presets are offered as fallback options.
|
||||
Hard-refresh your browser (`Cmd+Shift+R` / `Ctrl+Shift+R`) to clear the cached SPA bundle. Vite hashes filenames, but the browser may still cache `index.html`.
|
||||
|
||||
## Uninstalling
|
||||
---
|
||||
|
||||
```bash
|
||||
cd pymc_console
|
||||
sudo bash manage.sh
|
||||
```
|
||||
## Under the Hood
|
||||
|
||||
Select **Uninstall** from the menu. This removes:
|
||||
- `/opt/pymc_repeater` (installation)
|
||||
- `/etc/pymc_repeater` (configuration)
|
||||
- `/var/log/pymc_repeater` (logs)
|
||||
- The systemd service
|
||||
### Viterbi Path Disambiguation
|
||||
|
||||
## How It Works
|
||||
|
||||
### Mesh Topology Analysis
|
||||
|
||||
The dashboard reconstructs network topology from packet paths. MeshCore packets contain 2-character hex prefixes representing the route through the mesh:
|
||||
MeshCore packets contain 2-character hex prefixes representing the route through the mesh:
|
||||
|
||||
```
|
||||
Packet path: ["FA", "79", "24", "19"]
|
||||
Origin → Hop1 → Hop2 → Local
|
||||
Path: ["FA", "79", "24", "19"]
|
||||
Origin → Hop1 → Hop2 → Local
|
||||
```
|
||||
|
||||
**The Challenge**: Multiple nodes may share the same 2-char prefix (1 in 256 collision chance). The system uses a **Viterbi HMM decoder** to find the most likely sequence of actual nodes:
|
||||
Multiple nodes can share the same 2-char prefix (1-in-256 collision). The system uses a **Viterbi HMM decoder** to find the most probable sequence of actual nodes:
|
||||
|
||||
#### Viterbi Path Decoding
|
||||
- **States** — all candidate nodes matching each prefix, plus a "ghost" state for unknowns
|
||||
- **Priors** — recency-weighted (recently-seen nodes are more likely)
|
||||
- **Transitions** — physics-based costs using geographic distance and LoRa range constraints
|
||||
- **Key principle** — when edge observations have ≥80% confidence, real-world evidence overrides physics
|
||||
|
||||
Inspired by [d40cht/meshcore-connectivity-analysis](https://github.com/d40cht/meshcore-connectivity-analysis), the decoder treats path disambiguation as a Hidden Markov Model problem:
|
||||
Before Viterbi decoding, candidates are scored using **four-factor analysis**:
|
||||
|
||||
- **States** — All candidate nodes matching each prefix, plus a "ghost" state for unknown nodes
|
||||
- **Priors** — Based on recency (recently-seen nodes are more likely) and disambiguation confidence
|
||||
- **Transitions** — Physics-based costs using geographic distance and LoRa range constraints
|
||||
1. **Position (15%)** — typical path positions for this prefix
|
||||
2. **Co-occurrence (15%)** — which prefixes appear adjacent
|
||||
3. **Geographic (35%)** — distance to dual-hop anchor points
|
||||
4. **Recency (35%)** — exponential decay `e^(-hours/12)`
|
||||
|
||||
**Key principle: Observation beats theory.** When edge observations have ≥80% confidence, real-world evidence overrides physics-based costs.
|
||||
### Ghost Node Discovery
|
||||
|
||||
#### Ghost Node Discovery
|
||||
When no known candidate is geographically plausible, the decoder selects a "ghost" state. These are clustered and classified into four tiers:
|
||||
|
||||
When no known candidate is geographically plausible for a prefix, the decoder selects a "ghost" state. These are aggregated to discover unknown repeaters:
|
||||
- **Confirmed** — very high observation count, consistent neighbors, plausible location
|
||||
- **Likely** — strong evidence, probably a real undiscovered repeater
|
||||
- **Possible** — moderate evidence, worth investigating
|
||||
- **Noise** — low evidence, likely path artifacts
|
||||
|
||||
- **Clustering** — Ghost observations grouped by prefix
|
||||
- **Location estimation** — Weighted centroid of anchor node midpoints
|
||||
- **Confidence scoring** — Based on observation count, common neighbors, and location variance
|
||||
- **UI panel** — Shows likely-real ghost nodes with estimated coordinates and adjacent known nodes
|
||||
Ghost clusters include RF-constrained location estimates, temporal consistency analysis, and collision detection against known nodes.
|
||||
|
||||
#### Four-Factor Scoring (Pre-Viterbi)
|
||||
### 7-Phase Topology Pipeline
|
||||
|
||||
Before Viterbi decoding, candidates are scored using four-factor analysis inspired by [meshcore-bot](https://github.com/agessaman/meshcore-bot):
|
||||
1. **Directional edge tracking** — forward/reverse counts, symmetry ratio
|
||||
2. **Path sequence registry** — all observed paths, canonical detection
|
||||
3. **Flood vs direct classification** — per-edge routing type
|
||||
4. **Edge betweenness centrality** — backbone identification
|
||||
5. **Mobile repeater detection** — path volatility analysis
|
||||
6. **TX delay recommendations** — slot-based timing optimization
|
||||
7. **Path health scoring** — combined health, weakest link, latency
|
||||
|
||||
1. **Position (15%)** — Where in paths does this prefix typically appear?
|
||||
2. **Co-occurrence (15%)** — Which prefixes appear adjacent to this one?
|
||||
3. **Geographic (35%)** — How close is the candidate to anchor points?
|
||||
4. **Recency (35%)** — How recently was this node seen?
|
||||
### Protocol Library
|
||||
|
||||
**Key techniques:**
|
||||
The frontend includes a complete TypeScript port of the MeshCore protocol — binary packet parsing, header bit-field extraction, per-type payload decoders, channel key derivation, and GRP_TXT decryption (SHA-256, AES-ECB, pure JS — no native dependencies).
|
||||
|
||||
- **Recency scoring** — Exponential decay `e^(-hours/12)` favors recently-active nodes
|
||||
- **Age filtering** — Nodes not seen in 14 days are excluded from consideration
|
||||
- **Dual-hop anchoring** — Candidates scored by distance to both previous and next hops (a relay must be within RF range of both neighbors)
|
||||
- **Score-weighted redistribution** — Appearance counts redistributed proportionally by combined score
|
||||
---
|
||||
|
||||
The system maintains up to 500,000 packets in session memory for comprehensive topology evidence, with tiered loading (24h initial → 3d → 7d → 14d on demand).
|
||||
## Standalone UI Installation
|
||||
|
||||
### Edge Rendering
|
||||
If you already have pyMC_Repeater running and just want the dashboard, see [INSTALL.md](INSTALL.md) for manual tar.gz installation without manage.sh.
|
||||
|
||||
Topology edges are rendered with visual cues indicating confidence:
|
||||
|
||||
- **Line thickness** — Scales from 1.5px (5 validations) to 10px (100+ validations)
|
||||
- **Validation threshold** — Edges require 5+ certain observations to render
|
||||
- **Certainty conditions** — An edge is "certain" when:
|
||||
- Both endpoints have ≥0.6 confidence (HIGH threshold), OR
|
||||
- The destination has ≥0.9 confidence (VERY_HIGH threshold), OR
|
||||
- It's the last hop to local node
|
||||
- **Inclusion threshold** — Edges require ≥0.4 confidence (MEDIUM threshold) for topology
|
||||
- **Trace animation** — Edges "draw" from point A to B when topology is enabled
|
||||
- **Fade animation** — Edges smoothly fade out when topology is disabled
|
||||
- **Loop edges** — Redundant paths rendered as parallel double-lines in accent color
|
||||
|
||||
### 3D Terrain & Visualization
|
||||
|
||||
The Contacts map supports full 3D terrain rendering:
|
||||
|
||||
- **Terrain tiles** — AWS Terrarium elevation data (free, no API key)
|
||||
- **Hillshading** — Visual depth tuned for dark map themes
|
||||
- **3D arcs** — Topology edges and neighbor lines rendered as elevated arcs via deck.gl
|
||||
- **GPU acceleration** — deck.gl PathLayer and IconLayer for smooth pan/zoom/tilt
|
||||
- **Automatic draping** — All markers and edges align to terrain elevation
|
||||
|
||||
### Path Visualization
|
||||
|
||||
Clicking a packet shows its route on a map with confidence indicators:
|
||||
|
||||
- **Green** — 100% confidence (unique prefix, no collision)
|
||||
- **Yellow** — 50-99% confidence (high certainty)
|
||||
- **Orange** — 25-49% confidence (medium certainty)
|
||||
- **Red** — 1-24% confidence (low certainty)
|
||||
- **Gray/Ghost** — Unknown prefix resolved to ghost node
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
@@ -442,11 +418,11 @@ MIT — See [LICENSE](LICENSE)
|
||||
|
||||
## Credits
|
||||
|
||||
Built on the excellent work of:
|
||||
Built on the work of:
|
||||
|
||||
- **[RightUp](https://github.com/rightup)** — Creator of pyMC_Repeater, pymc_core, and the MeshCore ecosystem
|
||||
- **[pyMC_Repeater](https://github.com/rightup/pyMC_Repeater)** — Core repeater daemon for LoRa communication and mesh routing
|
||||
- **[pymc_core](https://github.com/rightup/pyMC_core)** — Underlying mesh protocol library
|
||||
- **[d40cht/meshcore-connectivity-analysis](https://github.com/d40cht/meshcore-connectivity-analysis)** — Viterbi HMM approach for path disambiguation and ghost node discovery
|
||||
- **[meshcore-bot](https://github.com/agessaman/meshcore-bot)** — Inspiration for recency scoring and dual-hop anchor disambiguation
|
||||
- **[RightUp](https://github.com/rightup)** — Creator of pyMC_Repeater, pymc_core, and the MeshCore Python ecosystem
|
||||
- **[pyMC_Repeater](https://github.com/rightup/pyMC_Repeater)** — Core repeater daemon
|
||||
- **[pymc_core](https://github.com/rightup/pyMC_core)** — Protocol library
|
||||
- **[MeshCore](https://meshcore.co.uk/)** — The MeshCore project and community
|
||||
- **[d40cht/meshcore-connectivity-analysis](https://github.com/d40cht/meshcore-connectivity-analysis)** — Viterbi HMM approach for path disambiguation
|
||||
- **[meshcore-bot](https://github.com/agessaman/meshcore-bot)** — Recency scoring and dual-hop anchor disambiguation
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
0.9.223
|
||||
0.9.224
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{j as e,c as s}from"./vendor-react-BKIq8k5U.js";import{B as l}from"./index-DnB5FOpE.js";function t({title:t,icon:r,badge:i,badgeColor:a="teal",subtitle:c,actions:n,iconColor:x="text-icon-card-title",largeTitle:d=!1,listHeader:m=!1,stackActionsOnMobile:o=!1}){return o&&n?e.jsxs("div",{className:s("flex flex-col gap-1 flex-shrink-0",m?"px-4 py-3 border-b border-border-subtle bg-bg-elevated/20":"mb-3"),children:[e.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 min-h-[32px]",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[r&&e.jsx("span",{className:s("icon-md flex items-center justify-center",x),children:r}),e.jsx("span",{className:s(d?"type-subheading text-text-primary":"type-micro"),children:t}),i&&e.jsx(l,{color:a,children:i})]}),e.jsx("div",{className:"flex items-center gap-2 flex-wrap",children:n})]}),c&&e.jsx("p",{className:"type-label text-text-muted ml-8",children:c})]}):e.jsxs("div",{className:s("flex flex-col gap-1 flex-shrink-0",m?"px-4 py-3 border-b border-border-subtle bg-bg-elevated/20":"mb-3"),children:[e.jsxs("div",{className:"flex items-center justify-between gap-2 min-h-[32px]",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[r&&e.jsx("span",{className:s("icon-md flex items-center justify-center",x),children:r}),e.jsx("span",{className:s(d?"type-subheading text-text-primary":"type-micro"),children:t}),i&&e.jsx(l,{color:a,children:i})]}),n&&e.jsx("div",{className:"flex items-center gap-2 flex-wrap",children:n})]}),c&&e.jsx("p",{className:"type-label text-text-muted ml-8",children:c})]})}function r({children:l,centered:t,className:r}){return e.jsx("div",{className:s("flex-1 min-h-0",t&&"flex items-center justify-center",r),children:l})}export{t as C,r as a};
|
||||
import{j as e,c as s}from"./vendor-react-Z6a030H9.js";import{B as l}from"./index-CIoxFkCL.js";function t({title:t,icon:r,badge:i,badgeColor:a="teal",subtitle:c,actions:n,iconColor:x="text-icon-card-title",largeTitle:d=!1,listHeader:m=!1,stackActionsOnMobile:o=!1}){return o&&n?e.jsxs("div",{className:s("flex flex-col gap-1 flex-shrink-0",m?"px-4 py-3 border-b border-border-subtle bg-bg-elevated/20":"mb-3"),children:[e.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 min-h-[32px]",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[r&&e.jsx("span",{className:s("icon-md flex items-center justify-center",x),children:r}),e.jsx("span",{className:s(d?"type-subheading text-text-primary":"type-micro"),children:t}),i&&e.jsx(l,{color:a,children:i})]}),e.jsx("div",{className:"flex items-center gap-2 flex-wrap",children:n})]}),c&&e.jsx("p",{className:"type-label text-text-muted ml-8",children:c})]}):e.jsxs("div",{className:s("flex flex-col gap-1 flex-shrink-0",m?"px-4 py-3 border-b border-border-subtle bg-bg-elevated/20":"mb-3"),children:[e.jsxs("div",{className:"flex items-center justify-between gap-2 min-h-[32px]",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[r&&e.jsx("span",{className:s("icon-md flex items-center justify-center",x),children:r}),e.jsx("span",{className:s(d?"type-subheading text-text-primary":"type-micro"),children:t}),i&&e.jsx(l,{color:a,children:i})]}),n&&e.jsx("div",{className:"flex items-center gap-2 flex-wrap",children:n})]}),c&&e.jsx("p",{className:"type-label text-text-muted ml-8",children:c})]})}function r({children:l,centered:t,className:r}){return e.jsx("div",{className:s("flex-1 min-h-0",t&&"flex items-center justify-center",r),children:l})}export{t as C,r as a};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
||||
import{c as e,H as t,cg as n,I as a,ch as s}from"./index-DnB5FOpE.js";import{r,j as o,c as i}from"./vendor-react-BKIq8k5U.js";import{T as c}from"./triangle-alert-C1y0bFKn.js";const d=e("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]),l=e("trash-2",[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]]),m={danger:{icon:"text-accent-danger",button:"bg-accent-danger hover:brightness-110 active:brightness-90"},warning:{icon:"text-accent-secondary",button:"bg-accent-secondary hover:brightness-110 active:brightness-90 text-bg-body"},default:{icon:"text-accent-primary",button:"bg-accent-primary hover:brightness-110 active:brightness-90 text-bg-body"}},h=r.memo(function({isOpen:e,title:r="Confirm",message:d,confirmLabel:l="Confirm",cancelLabel:h="Cancel",variant:b="default",onConfirm:x,onCancel:p}){const g=m[b];return o.jsxs(t,{open:e,onClose:p,size:"sm",children:[o.jsx(n,{icon:o.jsx(c,{className:i("w-5 h-5",g.icon)}),title:r,onClose:p}),o.jsx(a,{children:o.jsx("p",{className:"text-sm text-text-secondary",children:d})}),o.jsxs(s,{children:[o.jsx("button",{onClick:p,className:"flex-1 px-4 py-2.5 radius-inner text-sm font-medium text-text-secondary bg-bg-subtle hover-bg border-control transition-base",children:h}),o.jsx("button",{onClick:x,className:i("flex-1 px-4 py-2.5 radius-inner text-sm font-medium text-text-primary transition-base",g.button),children:l})]})]})});export{h as C,d as P,l as T};
|
||||
import{c as e,G as t,cl as n,H as a,cm as s}from"./index-CIoxFkCL.js";import{r,j as o,c as i}from"./vendor-react-Z6a030H9.js";import{T as c}from"./triangle-alert-DpsWNEPX.js";const d=e("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]),l=e("trash-2",[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]]),m={danger:{icon:"text-accent-danger",button:"bg-accent-danger hover:brightness-110 active:brightness-90"},warning:{icon:"text-accent-secondary",button:"bg-accent-secondary hover:brightness-110 active:brightness-90 text-bg-body"},default:{icon:"text-accent-primary",button:"bg-accent-primary hover:brightness-110 active:brightness-90 text-bg-body"}},h=r.memo(function({isOpen:e,title:r="Confirm",message:d,confirmLabel:l="Confirm",cancelLabel:h="Cancel",variant:b="default",onConfirm:x,onCancel:p}){const g=m[b];return o.jsxs(t,{open:e,onClose:p,size:"sm",children:[o.jsx(n,{icon:o.jsx(c,{className:i("w-5 h-5",g.icon)}),title:r,onClose:p}),o.jsx(a,{children:o.jsx("p",{className:"text-sm text-text-secondary",children:d})}),o.jsxs(s,{children:[o.jsx("button",{onClick:p,className:"flex-1 px-4 py-2.5 radius-inner text-sm font-medium text-text-secondary bg-bg-subtle hover-bg border-control transition-base",children:h}),o.jsx("button",{onClick:x,className:i("flex-1 px-4 py-2.5 radius-inner text-sm font-medium text-text-primary transition-base",g.button),children:l})]})]})});export{h as C,d as P,l as T};
|
||||
-2
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{r as e,j as t,c as o}from"./vendor-react-BKIq8k5U.js";import{c}from"./index-DnB5FOpE.js";const a=c("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]),n=c("copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]),s={standard:"w-3 h-3",compact:"w-2.5 h-2.5",responsive:"w-2.5 h-2.5 sm:w-3 sm:h-3"};function r({children:c,copyValue:r,copy:i=!1,size:l="standard",className:p,title:d,color:u,truncate:m}){const[h,x]=e.useState(!1),[y,w]=e.useState(!1),f=e.useRef(null),v=r??("string"==typeof c?c:""),b=s[l],g=(()=>{if(!m||"string"!=typeof c)return c;const e=c,[t,o]=!0===m?[6,4]:m;return e.length<=t+o+3?e:`${e.slice(0,t)}…${e.slice(-o)}`})(),j=e.useCallback(e=>{i&&(e.preventDefault(),e.stopPropagation(),f.current&&clearTimeout(f.current),function(e){var t;if("undefined"!=typeof window&&(window.isSecureContext||"https:"===window.location.protocol||"localhost"===window.location.hostname)&&(null==(t=navigator.clipboard)?void 0:t.writeText))return navigator.clipboard.writeText(e).catch(()=>{}),!0;try{const{scrollX:t,scrollY:o}=window,c=document.createElement("textarea");c.value=e,c.style.cssText="position:fixed;left:-9999px;top:-9999px;opacity:0;pointer-events:none",c.setAttribute("readonly",""),document.body.appendChild(c),c.focus({preventScroll:!0}),c.select(),c.setSelectionRange(0,e.length);const a=document.execCommand("copy");return document.body.removeChild(c),window.scrollTo(t,o),a}catch(o){return!1}}(v),x(!0),f.current=setTimeout(()=>x(!1),2e3))},[i,v]),C="compact"===l?"data-box-compact":"responsive"===l?"data-box-responsive":"",k=i?d||`Click to copy: ${v}`:d;return i?t.jsxs("button",{type:"button",onClick:j,onMouseEnter:()=>w(!0),onMouseLeave:()=>w(!1),className:o("data-box interactive",C,"cursor-pointer select-none gap-1","hover-bg",u,p),title:k,"aria-label":`Copy ${v}`,children:[t.jsx("span",{className:o("transition-colors duration-200",h?"text-accent-success":""),children:g}),t.jsx("span",{className:o("flex items-center justify-center","compact"===l?"w-3 h-3":"w-3.5 h-3.5"),children:h?t.jsx(a,{className:o("text-accent-success",b)}):t.jsx(n,{className:o("transition-opacity duration-200",y?"opacity-70":"opacity-40",b)})})]}):t.jsx("span",{className:o("data-box",C,u,p),title:k,children:g})}export{a as C,r as D,n as a};
|
||||
import{r as e,j as t,c as o}from"./vendor-react-Z6a030H9.js";import{c}from"./index-CIoxFkCL.js";const a=c("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]),n=c("copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]),s={standard:"w-3 h-3",compact:"w-2.5 h-2.5",responsive:"w-2.5 h-2.5 sm:w-3 sm:h-3"};function r({children:c,copyValue:r,copy:i=!1,size:l="standard",className:p,title:d,color:u,truncate:m}){const[h,x]=e.useState(!1),[y,w]=e.useState(!1),f=e.useRef(null),v=r??("string"==typeof c?c:""),b=s[l],g=(()=>{if(!m||"string"!=typeof c)return c;const e=c,[t,o]=!0===m?[6,4]:m;return e.length<=t+o+3?e:`${e.slice(0,t)}…${e.slice(-o)}`})(),j=e.useCallback(e=>{i&&(e.preventDefault(),e.stopPropagation(),f.current&&clearTimeout(f.current),function(e){var t;if("undefined"!=typeof window&&(window.isSecureContext||"https:"===window.location.protocol||"localhost"===window.location.hostname)&&(null==(t=navigator.clipboard)?void 0:t.writeText))return navigator.clipboard.writeText(e).catch(()=>{}),!0;try{const{scrollX:t,scrollY:o}=window,c=document.createElement("textarea");c.value=e,c.style.cssText="position:fixed;left:-9999px;top:-9999px;opacity:0;pointer-events:none",c.setAttribute("readonly",""),document.body.appendChild(c),c.focus({preventScroll:!0}),c.select(),c.setSelectionRange(0,e.length);const a=document.execCommand("copy");return document.body.removeChild(c),window.scrollTo(t,o),a}catch(o){return!1}}(v),x(!0),f.current=setTimeout(()=>x(!1),2e3))},[i,v]),C="compact"===l?"data-box-compact":"responsive"===l?"data-box-responsive":"",k=i?d||`Click to copy: ${v}`:d;return i?t.jsxs("button",{type:"button",onClick:j,onMouseEnter:()=>w(!0),onMouseLeave:()=>w(!1),className:o("data-box interactive",C,"cursor-pointer select-none gap-1","hover-bg",u,p),title:k,"aria-label":`Copy ${v}`,children:[t.jsx("span",{className:o("transition-colors duration-200",h?"text-accent-success":""),children:g}),t.jsx("span",{className:o("flex items-center justify-center","compact"===l?"w-3 h-3":"w-3.5 h-3.5"),children:h?t.jsx(a,{className:o("text-accent-success",b)}):t.jsx(n,{className:o("transition-opacity duration-200",y?"opacity-70":"opacity-40",b)})})]}):t.jsx("span",{className:o("data-box",C,u,p),title:k,children:g})}export{a as C,r as D,n as a};
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{c as e,aw as t,cW as a,cX as s,H as i,cY as n,I as c,m as r,at as o}from"./index-DnB5FOpE.js";import{r as l,j as d,c as m}from"./vendor-react-BKIq8k5U.js";import{e as p,d as x}from"./config-C7QO3CEB.js";import{C as h}from"./DataBox-CKFj3pZZ.js";import{G as g}from"./git-branch-CpPTX3yO.js";import{D as y}from"./database-Ce-Kxrrl.js";import{L as f}from"./loader-circle-BM8PbYu3.js";const u=e("download",[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]]),j=e("maximize-2",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"m21 3-7 7",key:"1l2asr"}],["path",{d:"m3 21 7-7",key:"tjx5ai"}],["path",{d:"M9 21H3v-6",key:"wtvkvv"}]]),b=e("minimize-2",[["path",{d:"m14 10 7-7",key:"oa77jy"}],["path",{d:"M20 10h-6V4",key:"mjg0md"}],["path",{d:"m3 21 7-7",key:"tjx5ai"}],["path",{d:"M4 14h6v6",key:"rmj7iw"}]]),v={hidden:{opacity:0,scale:.9},visible:{opacity:1,scale:1,transition:{type:"spring",stiffness:400,damping:15}},exit:{opacity:0,scale:.9,transition:{duration:x.fast}}},N={hidden:{opacity:0,y:"100%"},visible:{opacity:1,y:0,transition:{type:"spring",stiffness:400,damping:30}},exit:{opacity:0,y:"100%",transition:{duration:x.medium,ease:p.easeIn}}};function k({label:e,icon:t,status:a,detail:s}){return d.jsxs("div",{className:m("flex items-center gap-3 py-3 px-4 radius-inset transition-base","active"===a&&"bg-accent-primary/10","complete"===a&&"bg-accent-primary/10 ring-2 ring-inset ring-accent-primary","pending"===a&&"opacity-40"),children:[d.jsx("div",{className:m("w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 transition-all duration-300","active"===a&&"bg-accent-primary/20","complete"===a&&"bg-accent-primary/20","pending"===a&&"bg-subtle-fill"),children:"complete"===a?d.jsx(h,{className:"w-4 h-4 text-accent-primary"}):"active"===a?d.jsx(f,{className:"w-4 h-4 animate-spin text-accent-primary"}):d.jsx("span",{className:"text-text-muted",children:t})}),d.jsxs("div",{className:"flex-1 min-w-0",children:[d.jsx("div",{className:m("text-sm font-medium transition-colors","active"===a&&"text-accent-primary","complete"===a&&"text-accent-primary","pending"===a&&"text-text-muted"),children:e}),s&&"pending"!==a&&d.jsx("div",{className:"text-xs text-text-muted mt-0.5 truncate",children:s})]})]})}const w=l.memo(function({isOpen:e,onClose:m}){const{packetCacheState:p,triggerDeepAnalysis:x}=t(),f=a(),j=s(),b=p.packetCount,[N,w]=l.useState("fetching");l.useEffect(()=>{e&&x()},[e,x]),l.useEffect(()=>{if(e)if(p.isTopologyLoading)w("fetching");else if(f)w("analyzing"),setTimeout(()=>w("building"),500),setTimeout(()=>w("discovering"),1e3);else if(j>0&&!p.isTopologyLoading){w("complete");const e=setTimeout(()=>{m()},1500);return()=>clearTimeout(e)}},[e,p.isTopologyLoading,f,j,m]);const M="complete"===N,T=p.loadProgress,z=l.useMemo(()=>T&&T.target>0?`Loading... ${(T.loaded/1e3).toFixed(1)}k / ${(T.target/1e3).toFixed(1)}k packets (${T.percent}%)`:b>0?`${b.toLocaleString()} packets loaded`:"Connecting to database...",[T,b]),C=e=>{const t=["fetching","analyzing","building","discovering","complete"],a=t.indexOf(N),s=t.indexOf(e);return s<a?"complete":s===a?"active":"pending"},D=M?m:()=>{};return d.jsx(i,{open:e,onClose:D,size:"sm",bottomSheet:!1,children:d.jsx(n,{isLoading:!M,borderRadius:16,children:d.jsx(c,{className:"p-6 radius-card overflow-hidden",children:M?d.jsxs("div",{className:"flex flex-col items-center py-6",children:[d.jsx(r.div,{variants:v,initial:"hidden",animate:"visible",className:"w-16 h-16 rounded-full flex items-center justify-center mb-4 bg-accent-primary/20",children:d.jsx(h,{className:"w-8 h-8 text-accent-primary"})}),d.jsx("h3",{className:"text-lg font-semibold text-accent-primary",children:"Ready!"})]}):d.jsxs(d.Fragment,{children:[d.jsxs("div",{className:"flex items-center gap-3 mb-5",children:[d.jsx("div",{className:"w-10 h-10 radius-inset flex items-center justify-center bg-accent-primary/15",children:d.jsx(g,{className:"w-5 h-5 text-accent-primary"})}),d.jsxs("div",{children:[d.jsx("h3",{className:"text-base font-semibold text-text-primary",children:"Deep Analysis"}),d.jsx("p",{className:"text-xs text-text-muted",children:"Building mesh topology"})]})]}),d.jsxs("div",{className:"space-y-2",children:[d.jsx(k,{label:"Fetching Packets",icon:d.jsx(u,{className:"w-4 h-4"}),status:C("fetching"),detail:z}),d.jsx(k,{label:"Analyzing Database",icon:d.jsx(y,{className:"w-4 h-4"}),status:C("analyzing"),detail:"Processing packet paths"}),d.jsx(k,{label:"Building Topology",icon:d.jsx(g,{className:"w-4 h-4"}),status:C("building"),detail:"Computing mesh edges"}),d.jsx(k,{label:"Discovering Nodes",icon:d.jsx(o,{className:"w-4 h-4"}),status:C("discovering"),detail:"Viterbi HMM ghost detection"})]}),d.jsx("p",{className:"text-xs text-text-muted text-center mt-5",children:"This may take a few seconds..."})]})})})})});export{w as D,b as M,j as a,u as b,N as s};
|
||||
@@ -0,0 +1 @@
|
||||
import{c as e}from"./index-CIoxFkCL.js";import{r as t,j as a}from"./vendor-react-Z6a030H9.js";import{A as s}from"./index-DC-GJ6vK.js";import{t as n}from"./config-TTDcOBVH.js";const r=e("house",[["path",{d:"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8",key:"5wwlr5"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",key:"r6nss1"}]]);function i({value:e,format:r,prefix:i,className:o,style:m}){const[c,f]=t.useState("waiting");if(t.useEffect(()=>{const e=setTimeout(()=>f("mounted"),400);return()=>clearTimeout(e)},[]),t.useEffect(()=>{if("mounted"===c){const e=requestAnimationFrame(()=>f("animating"));return()=>cancelAnimationFrame(e)}},[c]),"waiting"===c){const e=(0).toLocaleString(void 0,r);return a.jsxs("span",{className:o,style:m,children:[i,e]})}return a.jsx(s,{className:o,style:m,format:r,prefix:i,transition:n.numberReveal,children:"animating"===c?e:0})}export{i as D,r as H};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{r as o,j as e,c as l}from"./vendor-react-BKIq8k5U.js";const t={hero:{mobile:12},"hero-tall":{mobile:12},"hero-auto":{mobile:12},panel:{mobile:12,md:6},feature:{mobile:12,md:6,lg:4},standard:{mobile:12,sm:6,lg:4,xl:3},compact:{mobile:6,lg:3},widget:{mobile:6,sm:4,md:3,lg:2},auto:{mobile:12}},a={hero:"bento-row-hero","hero-tall":"bento-row-hero-tall","hero-auto":"bento-row-hero-auto",panel:"bento-row-panel",feature:"bento-row-feature",standard:"bento-row-standard",compact:"bento-row-compact",widget:"bento-row-widget",auto:"bento-row-auto"};function n({template:n,children:r,className:s,gap:c}){const i=t[n],b=a[n],d="none"===c?"gap-0":"sm"===c?"bento-gap-sm":"lg"===c?"bento-gap-lg":"bento-gap",p=o.Children.map(r,l=>o.isValidElement(l)?l.type===m?l:e.jsx(m,{span:i.mobile,sm:i.sm,md:i.md,lg:i.lg,xl:i.xl,children:l}):l);return e.jsx("div",{className:l("bento-row",b,d,s),children:p})}function r(o,e=""){const l=e?`${e}:`:"";return"full"===o?`${l}col-span-full`:"auto"===o?`${l}col-auto`:1.5===o?`${l}bento-col-1-5`:`${l}bento-col-${o}`}function m({children:o,span:t,sm:a,md:n,lg:m,xl:s,className:c}){const i=l("bento-cell",r(t),a&&r(a,"sm"),n&&r(n,"md"),m&&r(m,"lg"),s&&r(s,"xl"),c);return e.jsx("div",{className:i,children:o})}export{m as C,n as R};
|
||||
import{r as o,j as e,c as l}from"./vendor-react-Z6a030H9.js";const t={hero:{mobile:12},"hero-tall":{mobile:12},"hero-auto":{mobile:12},panel:{mobile:12,md:6},feature:{mobile:12,md:6,lg:4},standard:{mobile:12,sm:6,lg:4,xl:3},compact:{mobile:6,lg:3},widget:{mobile:6,sm:4,md:3,lg:2},auto:{mobile:12}},a={hero:"bento-row-hero","hero-tall":"bento-row-hero-tall","hero-auto":"bento-row-hero-auto",panel:"bento-row-panel",feature:"bento-row-feature",standard:"bento-row-standard",compact:"bento-row-compact",widget:"bento-row-widget",auto:"bento-row-auto"};function n({template:n,children:r,className:s,gap:c}){const i=t[n],b=a[n],d="none"===c?"gap-0":"sm"===c?"bento-gap-sm":"lg"===c?"bento-gap-lg":"bento-gap",p=o.Children.map(r,l=>o.isValidElement(l)?l.type===m?l:e.jsx(m,{span:i.mobile,sm:i.sm,md:i.md,lg:i.lg,xl:i.xl,children:l}):l);return e.jsx("div",{className:l("bento-row",b,d,s),children:p})}function r(o,e=""){const l=e?`${e}:`:"";return"full"===o?`${l}col-span-full`:"auto"===o?`${l}col-auto`:1.5===o?`${l}bento-col-1-5`:`${l}bento-col-${o}`}function m({children:o,span:t,sm:a,md:n,lg:m,xl:s,className:c}){const i=l("bento-cell",r(t),a&&r(a,"sm"),n&&r(n,"md"),m&&r(m,"lg"),s&&r(s,"xl"),c);return e.jsx("div",{className:i,children:o})}export{m as C,n as R};
|
||||
@@ -0,0 +1 @@
|
||||
import{c as e}from"./index-CIoxFkCL.js";import{r as t,j as n}from"./vendor-react-Z6a030H9.js";const r=e("message-square",[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}]]),i=e("trending-down",[["path",{d:"M16 17h6v-6",key:"t6n2it"}],["path",{d:"m22 17-8.5-8.5-5 5L2 7",key:"x473p"}]]),a=e("trending-up",[["path",{d:"M16 7h6v6",key:"box55l"}],["path",{d:"m22 7-8.5 8.5-5-5L2 17",key:"1t1m79"}]]),s="var(--sparkline-critical)",o="var(--sparkline-poor)",h="var(--sparkline-fair)",d="var(--sparkline-good)",l="var(--sparkline-excellent)",c=t.memo(function({data:e,width:r=48,height:i=16,color:a,isLoading:c=!1,className:x=""}){const u=t.useMemo(()=>function(e,t,n,r=2){if(e.length<2)return"";const i=e.map(e=>e.count),a=Math.max(...i,1),s=Math.min(...i),o=a-s||1,h=t-2*r,d=n-2*r,l=i.map((e,t)=>({x:r+t/(i.length-1)*h,y:r+d-(e-s)/o*d}));let c=`M ${l[0].x.toFixed(1)} ${l[0].y.toFixed(1)}`;for(let x=1;x<l.length;x++){const e=l[x-1],t=l[x];c+=` Q ${((e.x+t.x)/2).toFixed(1)} ${e.y.toFixed(1)} ${t.x.toFixed(1)} ${t.y.toFixed(1)}`}return c}(e,r,i),[e,r,i]),m=t.useMemo(()=>a??(e.length>0?function(e){if(0===e.length)return s;const t=e.slice(-4),n=t.reduce((e,t)=>e+t.count,0),r=n/t.length,i=e.reduce((e,t)=>e+t.count,0)/e.length;if(0===n)return s;if(i>0){const e=r/i;return e>=1.2?l:e>=.8?d:e>=.4?h:e>=.1?o:s}return n>=10?l:n>=5?d:n>=2?h:n>=1?o:s}(e):"var(--text-muted)"),[a,e]);return c&&e.length<2?n.jsx("div",{className:`flex items-center justify-center ${x}`,style:{width:r,height:i},children:n.jsx("div",{className:"rounded animate-pulse bg-subtle-fill-hover",style:{width:r-8,height:i-6}})}):e.length<2?n.jsx("svg",{width:r,height:i,viewBox:`0 0 ${r} ${i}`,className:x,"aria-hidden":"true",children:n.jsx("line",{x1:4,y1:i/2,x2:r-4,y2:i/2,stroke:s,strokeWidth:1.5,strokeDasharray:"3,2"})}):n.jsx("svg",{width:r,height:i,viewBox:`0 0 ${r} ${i}`,preserveAspectRatio:"none",className:x,"aria-hidden":"true",style:{maxWidth:"100%"},children:n.jsx("path",{d:u,fill:"none",stroke:m,strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round",vectorEffect:"non-scaling-stroke"})})});export{c as L,r as M,a as T,i as a};
|
||||
@@ -1 +0,0 @@
|
||||
import{c as e}from"./index-DnB5FOpE.js";import{r as t,j as n}from"./vendor-react-BKIq8k5U.js";const a=e("message-square",[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}]]),r=e("minus",[["path",{d:"M5 12h14",key:"1ays0h"}]]),i=e("trending-down",[["path",{d:"M16 17h6v-6",key:"t6n2it"}],["path",{d:"m22 17-8.5-8.5-5 5L2 7",key:"x473p"}]]),s=e("trending-up",[["path",{d:"M16 7h6v6",key:"box55l"}],["path",{d:"m22 7-8.5 8.5-5-5L2 17",key:"1t1m79"}]]),o="var(--sparkline-critical)",h="var(--sparkline-poor)",d="var(--sparkline-fair)",l="var(--sparkline-good)",c="var(--sparkline-excellent)",x=t.memo(function({data:e,width:a=48,height:r=16,color:i,isLoading:s=!1,className:x=""}){const u=t.useMemo(()=>function(e,t,n,a=2){if(e.length<2)return"";const r=e.map(e=>e.count),i=Math.max(...r,1),s=Math.min(...r),o=i-s||1,h=t-2*a,d=n-2*a,l=r.map((e,t)=>({x:a+t/(r.length-1)*h,y:a+d-(e-s)/o*d}));let c=`M ${l[0].x.toFixed(1)} ${l[0].y.toFixed(1)}`;for(let x=1;x<l.length;x++){const e=l[x-1],t=l[x];c+=` Q ${((e.x+t.x)/2).toFixed(1)} ${e.y.toFixed(1)} ${t.x.toFixed(1)} ${t.y.toFixed(1)}`}return c}(e,a,r),[e,a,r]),m=t.useMemo(()=>i??(e.length>0?function(e){if(0===e.length)return o;const t=e.slice(-4),n=t.reduce((e,t)=>e+t.count,0),a=n/t.length,r=e.reduce((e,t)=>e+t.count,0)/e.length;if(0===n)return o;if(r>0){const e=a/r;return e>=1.2?c:e>=.8?l:e>=.4?d:e>=.1?h:o}return n>=10?c:n>=5?l:n>=2?d:n>=1?h:o}(e):"var(--text-muted)"),[i,e]);return s&&e.length<2?n.jsx("div",{className:`flex items-center justify-center ${x}`,style:{width:a,height:r},children:n.jsx("div",{className:"rounded animate-pulse bg-white/[0.06]",style:{width:a-8,height:r-6}})}):e.length<2?n.jsx("svg",{width:a,height:r,viewBox:`0 0 ${a} ${r}`,className:x,"aria-hidden":"true",children:n.jsx("line",{x1:4,y1:r/2,x2:a-4,y2:r/2,stroke:o,strokeWidth:1.5,strokeDasharray:"3,2"})}):n.jsx("svg",{width:a,height:r,viewBox:`0 0 ${a} ${r}`,preserveAspectRatio:"none",className:x,"aria-hidden":"true",style:{maxWidth:"100%"},children:n.jsx("path",{d:u,fill:"none",stroke:m,strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round",vectorEffect:"non-scaling-stroke"})})});export{x as L,r as M,s as T,i as a,a as b};
|
||||
-1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
-1
@@ -1 +0,0 @@
|
||||
import{r as e,j as s,c as a}from"./vendor-react-BKIq8k5U.js";import{c as t,b9 as l,ba as r,h as c,bb as i,bc as n,bd as o,b1 as m,be as x}from"./index-DnB5FOpE.js";import{u as d}from"./usePolling-_rZ6EKji.js";import{P as h,b as p,B as g,a as j,L as u}from"./PageLayout-DdvwgBp8.js";import{C as b}from"./Card-BRttWJMn.js";import"./deckgl-DTsmDcfs.js";import"./vendor-core-D1aqi_5O.js";const y=t("circle-play",[["path",{d:"M9 9.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997A1 1 0 0 1 9 14.996z",key:"kmsa83"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]]),f=t("circle-stop",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["rect",{x:"9",y:"9",width:"6",height:"6",rx:"1",key:"1ssd4o"}]]),v=e.memo(function({log:e}){return s.jsx("div",{className:"p-3 radius-card bg-subtle-fill hover:bg-subtle-fill-strong transition-base",children:s.jsxs("div",{className:"flex items-baseline gap-3",children:[s.jsx("span",{className:a("type-data-sm w-14 shrink-0",x(e.level)),children:e.level}),s.jsxs("div",{className:"flex-1 min-w-0",children:[s.jsx("p",{className:"type-data-sm text-text-primary break-words whitespace-pre-wrap",children:e.message}),s.jsx("p",{className:"type-data-xs text-text-muted mt-1",children:new Date(e.timestamp).toLocaleString()})]})]})})});function w({showDebug:e,onToggle:t}){return s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("span",{className:a("type-data-sm",e?"text-accent-tertiary":"text-accent-primary"),children:e?"DEBUG":"INFO"}),s.jsx("button",{onClick:t,className:a("relative w-10 h-5 rounded-full transition-base",e?"bg-accent-tertiary":"bg-accent-primary"),role:"switch","aria-checked":e,children:s.jsx("span",{className:a("absolute top-[2px] left-[2px] w-4 h-4 bg-white rounded-full shadow-sm transition-transform duration-200",e?"translate-x-5":"translate-x-0")})})]})}function N(){const t=l(),x=r(),N=c(),k=i(),C=n(),[D,L]=e.useState(!1),P=e.useCallback(()=>{L(e=>!e)},[]),B=D?t:t.filter(e=>"DEBUG"!==e.level);return d(k,m.logs,N),s.jsxs(h,{children:[s.jsx(p,{title:"System Logs",icon:s.jsx(o,{}),controls:s.jsx(w,{showDebug:D,onToggle:P})}),s.jsx(g,{children:s.jsxs(j,{noPadding:!0,children:[s.jsx(b,{listHeader:!0,icon:s.jsx(o,{className:"icon-sm"}),title:"Log Entries",actions:s.jsx("button",{onClick:()=>C(!N),className:a("transition-base",N?"text-signal-critical hover:text-signal-critical/80":"text-accent-primary hover:text-accent-primary/80 animate-pulse-slow"),title:N?"Pause":"Resume",children:N?s.jsx(f,{className:"w-5 h-5"}):s.jsx(y,{className:"w-5 h-5"})})}),s.jsx("div",{className:"space-y-2 max-h-[calc(100vh-300px)] sm:max-h-[calc(100vh-250px)] overflow-y-auto p-4",children:x&&0===t.length?s.jsx(u,{count:10}):0===B.length?s.jsx("div",{className:"text-center py-12 text-text-muted",children:0===t.length?"No logs available":"No logs match selected filters"}):B.map((e,a)=>s.jsx(v,{log:e},`${e.timestamp}-${a}`))})]})})]})}export{N as default};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{r as e,j as s,c as a}from"./vendor-react-Z6a030H9.js";import{c as t,bg as l,bh as r,h as i,bi as c,bj as n,bk as o,b8 as m,bl as x}from"./index-CIoxFkCL.js";import{u as d}from"./usePolling-BFaWogqT.js";import{P as h,b as p,B as g,a as j,L as u}from"./PageLayout-yL7WP6lH.js";import{C as b}from"./Card-CkMpWBXF.js";import"./maplibre-gl-b91ci4Kr.js";import"./vendor-core-DqX91EBH.js";const y=t("circle-play",[["path",{d:"M9 9.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997A1 1 0 0 1 9 14.996z",key:"kmsa83"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]]),f=t("circle-stop",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["rect",{x:"9",y:"9",width:"6",height:"6",rx:"1",key:"1ssd4o"}]]),v=e.memo(function({log:e}){return s.jsx("div",{className:"p-3 radius-card bg-subtle-fill hover:bg-subtle-fill-strong transition-base",children:s.jsxs("div",{className:"flex items-baseline gap-3",children:[s.jsx("span",{className:a("type-data-sm w-14 shrink-0",x(e.level)),children:e.level}),s.jsxs("div",{className:"flex-1 min-w-0",children:[s.jsx("p",{className:"type-data-sm text-text-primary break-words whitespace-pre-wrap",children:e.message}),s.jsx("p",{className:"type-data-xs text-text-muted mt-1",children:new Date(e.timestamp).toLocaleString()})]})]})})});function w({showDebug:e,onToggle:t}){return s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("span",{className:a("type-data-sm",e?"text-accent-tertiary":"text-accent-primary"),children:e?"DEBUG":"INFO"}),s.jsx("button",{onClick:t,className:a("relative w-10 h-5 rounded-full transition-base",e?"bg-accent-tertiary":"bg-accent-primary"),role:"switch","aria-checked":e,children:s.jsx("span",{className:a("absolute top-[2px] left-[2px] w-4 h-4 bg-white rounded-full shadow-sm transition-transform duration-200",e?"translate-x-5":"translate-x-0")})})]})}function N(){const t=l(),x=r(),N=i(),k=c(),P=n(),[C,D]=e.useState(!1),L=e.useCallback(()=>{D(e=>!e)},[]),B=C?t:t.filter(e=>"DEBUG"!==e.level);return d(k,m.logs,N),s.jsxs(h,{children:[s.jsx(p,{title:"System Logs",icon:s.jsx(o,{}),controls:s.jsx(w,{showDebug:C,onToggle:L})}),s.jsx(g,{children:s.jsxs(j,{noPadding:!0,children:[s.jsx(b,{listHeader:!0,icon:s.jsx(o,{className:"icon-sm"}),title:"Log Entries",actions:s.jsx("button",{onClick:()=>P(!N),className:a("transition-base",N?"text-signal-critical hover:text-signal-critical/80":"text-accent-primary hover:text-accent-primary/80 animate-pulse-slow"),title:N?"Pause":"Resume",children:N?s.jsx(f,{className:"w-5 h-5"}):s.jsx(y,{className:"w-5 h-5"})})}),s.jsx("div",{className:"space-y-2 max-h-[calc(100vh-300px)] sm:max-h-[calc(100vh-250px)] overflow-y-auto p-4",children:x&&0===t.length?s.jsx(u,{count:10}):0===B.length?s.jsx("div",{className:"text-center py-12 text-text-muted",children:0===t.length?"No logs available":"No logs match selected filters"}):B.map((e,a)=>s.jsx(v,{log:e},`${e.timestamp}-${a}`))})]})})]})}export{N as default};
|
||||
+1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{j as e}from"./vendor-react-BKIq8k5U.js";import{a as s}from"./PageLayout-DdvwgBp8.js";import{D as t}from"./DataBox-CKFj3pZZ.js";import{R as a,bf as r}from"./index-DnB5FOpE.js";import{C as l}from"./Card-BRttWJMn.js";function c({nodeName:c,repeaterVersion:m,coreVersion:i,localHash:n,publicKey:d}){return e.jsxs(s,{children:[e.jsx(l,{icon:e.jsx(a,{}),title:"Node Information",largeTitle:!0}),e.jsxs("div",{className:"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-3 sm:gap-4",children:[e.jsxs("div",{className:"min-w-0 col-span-2 sm:col-span-1",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Node Name"}),e.jsx("p",{className:"type-body text-text-primary mt-0.5 sm:mt-1 truncate",title:c,children:c})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Repeater"}),e.jsxs("p",{className:"type-data text-text-primary mt-0.5 sm:mt-1 truncate",title:`v${m}`,children:["v",m]})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Core"}),e.jsxs("p",{className:"type-data text-text-primary mt-0.5 sm:mt-1 truncate",title:`v${i}`,children:["v",i]})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Console"}),e.jsxs("p",{className:"type-data text-text-primary mt-0.5 sm:mt-1 truncate",title:`v${r}`,children:["v",r]})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Local Hash"}),e.jsx("div",{className:"mt-0.5 sm:mt-1",children:n?e.jsx(t,{copy:!0,size:"compact",children:n}):e.jsx("span",{className:"type-data-sm text-text-secondary",children:"N/A"})})]})]}),d&&e.jsxs("div",{className:"mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-border-subtle",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Public Key"}),e.jsx("div",{className:"mt-0.5 sm:mt-1",children:e.jsx(t,{copy:!0,size:"responsive",children:d})})]})]})}export{c as N};
|
||||
import{j as e}from"./vendor-react-Z6a030H9.js";import{a as s}from"./PageLayout-yL7WP6lH.js";import{D as t}from"./DataBox-Cckr4A6l.js";import{R as a,aY as r}from"./index-CIoxFkCL.js";import{C as l}from"./Card-CkMpWBXF.js";function c({nodeName:c,repeaterVersion:m,coreVersion:i,localHash:n,publicKey:d}){return e.jsxs(s,{children:[e.jsx(l,{icon:e.jsx(a,{}),title:"Node Information",largeTitle:!0}),e.jsxs("div",{className:"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-3 sm:gap-4",children:[e.jsxs("div",{className:"min-w-0 col-span-2 sm:col-span-1",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Node Name"}),e.jsx("p",{className:"type-body text-text-primary mt-0.5 sm:mt-1 truncate",title:c,children:c})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Repeater"}),e.jsxs("p",{className:"type-data text-text-primary mt-0.5 sm:mt-1 truncate",title:`v${m}`,children:["v",m]})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Core"}),e.jsxs("p",{className:"type-data text-text-primary mt-0.5 sm:mt-1 truncate",title:`v${i}`,children:["v",i]})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Console"}),e.jsxs("p",{className:"type-data text-text-primary mt-0.5 sm:mt-1 truncate",title:`v${r}`,children:["v",r]})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Local Hash"}),e.jsx("div",{className:"mt-0.5 sm:mt-1",children:n?e.jsx(t,{copy:!0,size:"compact",children:n}):e.jsx("span",{className:"type-data-sm text-text-secondary",children:"N/A"})})]})]}),d&&e.jsxs("div",{className:"mt-3 sm:mt-4 pt-3 sm:pt-4 border-t border-border-subtle",children:[e.jsx("span",{className:"type-label text-text-secondary",children:"Public Key"}),e.jsx("div",{className:"mt-0.5 sm:mt-1",children:e.jsx(t,{copy:!0,size:"responsive",children:d})})]})]})}export{c as N};
|
||||
+2
File diff suppressed because one or more lines are too long
-2
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{j as s,c as e}from"./vendor-react-BKIq8k5U.js";import"./index-DnB5FOpE.js";const a=[45,72,33,58,80,42,65,28,55,75,38,62];function l({className:a,style:l}){return s.jsx("div",{className:e("animate-pulse bg-subtle-fill radius-badge",a),style:l})}function c(){return s.jsx("div",{className:"p-3 radius-inner border-card bg-bg-subtle",children:s.jsxs("div",{className:"flex items-start gap-3",children:[s.jsx(l,{className:"w-14 h-6 rounded shrink-0"}),s.jsxs("div",{className:"flex-1 min-w-0 space-y-2",children:[s.jsx(l,{className:"h-4 w-full"}),s.jsx(l,{className:"h-4 w-3/4"}),s.jsx(l,{className:"h-3 w-32 mt-1"})]})]})})}function r({count:e=8}){return s.jsx("div",{className:"space-y-2",children:Array.from({length:e}).map((e,a)=>s.jsx(c,{},a))})}function i(){return s.jsxs("div",{className:"flex flex-col gap-3 h-full","aria-hidden":"true",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(l,{className:"w-5 h-5"}),s.jsx(l,{className:"h-4 w-24"})]}),s.jsx(l,{className:"h-8 w-20"}),s.jsxs("div",{className:"flex-1 flex flex-col justify-end gap-2",children:[s.jsx(l,{className:"h-3 w-full"}),s.jsx(l,{className:"h-3 w-3/4"})]})]})}function n(){return s.jsxs("div",{className:"flex flex-col gap-3 h-full","aria-hidden":"true",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(l,{className:"w-5 h-5"}),s.jsx(l,{className:"h-4 w-24"})]}),s.jsx("div",{className:"flex-1 flex items-end gap-1",children:a.slice(0,8).map((e,a)=>s.jsx(l,{className:"flex-1",style:{height:`${e}%`}},a))})]})}function t({rows:e=5}){return s.jsxs("div",{className:"flex flex-col h-full","aria-hidden":"true",children:[s.jsx("div",{className:"pb-3 border-b border-border-subtle",children:s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(l,{className:"w-5 h-5"}),s.jsx(l,{className:"h-4 w-24"})]})}),s.jsx("div",{className:"flex-1 pt-3 flex flex-col gap-3",children:Array.from({length:e}).map((e,a)=>s.jsx(l,{className:"h-6 w-full"},a))})]})}function d({children:a,className:l}){return s.jsx("div",{className:e("section-gap",l),children:a})}function x({children:a,className:l}){return s.jsx("div",{className:e("bento-section",l),children:a})}function m({title:e,icon:a,controls:l,subtitle:c}){return s.jsxs("div",{className:"flex flex-col gap-2",children:[s.jsxs("div",{className:"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3",children:[s.jsxs("h1",{className:"type-title text-text-primary flex items-center gap-2 sm:gap-3 h-9 min-w-0",children:[a&&s.jsx("span",{className:"w-5 h-5 sm:w-6 sm:h-6 text-icon-page-title flex-shrink-0",children:a}),s.jsx("span",{className:"truncate",children:e})]}),l&&s.jsx("div",{className:"flex items-center gap-2 sm:gap-3 h-9 flex-shrink-0",children:l})]}),c&&s.jsx("div",{children:c})]})}const h={sm:"card-sm",md:"card-md",lg:"card-lg",hero:"card-hero",auto:"card-auto"};function f({children:a,size:l,compact:c,noPadding:r,className:d,style:x,onClick:m,isLoaded:f=!0,skeletonType:o="card"}){return s.jsxs("div",{className:e("surface-base rounded-2xl h-full flex flex-col relative",!r&&(c?"card-padding-sm":"card-padding"),l&&h[l],d),style:x,onClick:m,children:[a,!f&&(()=>{switch(o){case"chart":return s.jsx(n,{});case"list":return s.jsx(t,{});default:return s.jsx(i,{})}})()]})}export{x as B,i as C,r as L,d as P,l as S,f as a,m as b};
|
||||
import{j as s,c as e}from"./vendor-react-Z6a030H9.js";import"./index-CIoxFkCL.js";const a=[45,72,33,58,80,42,65,28,55,75,38,62];function l({className:a,style:l}){return s.jsx("div",{className:e("animate-pulse bg-subtle-fill radius-badge",a),style:l})}function c(){return s.jsx("div",{className:"p-3 radius-inner border-card bg-bg-subtle",children:s.jsxs("div",{className:"flex items-start gap-3",children:[s.jsx(l,{className:"w-14 h-6 rounded shrink-0"}),s.jsxs("div",{className:"flex-1 min-w-0 space-y-2",children:[s.jsx(l,{className:"h-4 w-full"}),s.jsx(l,{className:"h-4 w-3/4"}),s.jsx(l,{className:"h-3 w-32 mt-1"})]})]})})}function r({count:e=8}){return s.jsx("div",{className:"space-y-2",children:Array.from({length:e}).map((e,a)=>s.jsx(c,{},a))})}function i(){return s.jsxs("div",{className:"flex flex-col gap-3 h-full","aria-hidden":"true",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(l,{className:"w-5 h-5"}),s.jsx(l,{className:"h-4 w-24"})]}),s.jsx(l,{className:"h-8 w-20"}),s.jsxs("div",{className:"flex-1 flex flex-col justify-end gap-2",children:[s.jsx(l,{className:"h-3 w-full"}),s.jsx(l,{className:"h-3 w-3/4"})]})]})}function n(){return s.jsxs("div",{className:"flex flex-col gap-3 h-full","aria-hidden":"true",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(l,{className:"w-5 h-5"}),s.jsx(l,{className:"h-4 w-24"})]}),s.jsx("div",{className:"flex-1 flex items-end gap-1",children:a.slice(0,8).map((e,a)=>s.jsx(l,{className:"flex-1",style:{height:`${e}%`}},a))})]})}function t({rows:e=5}){return s.jsxs("div",{className:"flex flex-col h-full","aria-hidden":"true",children:[s.jsx("div",{className:"pb-3 border-b border-border-subtle",children:s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(l,{className:"w-5 h-5"}),s.jsx(l,{className:"h-4 w-24"})]})}),s.jsx("div",{className:"flex-1 pt-3 flex flex-col gap-3",children:Array.from({length:e}).map((e,a)=>s.jsx(l,{className:"h-6 w-full"},a))})]})}function d({children:a,className:l}){return s.jsx("div",{className:e("section-gap",l),children:a})}function x({children:a,className:l}){return s.jsx("div",{className:e("bento-section",l),children:a})}function m({title:e,icon:a,controls:l,subtitle:c}){return s.jsxs("div",{className:"flex flex-col gap-2",children:[s.jsxs("div",{className:"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3",children:[s.jsxs("h1",{className:"type-title text-text-primary flex items-center gap-2 sm:gap-3 h-9 min-w-0",children:[a&&s.jsx("span",{className:"w-5 h-5 sm:w-6 sm:h-6 text-icon-page-title flex-shrink-0",children:a}),s.jsx("span",{className:"truncate",children:e})]}),l&&s.jsx("div",{className:"flex items-center gap-2 sm:gap-3 h-9 flex-shrink-0",children:l})]}),c&&s.jsx("div",{children:c})]})}const h={sm:"card-sm",md:"card-md",lg:"card-lg",hero:"card-hero",auto:"card-auto"};function f({children:a,size:l,compact:c,noPadding:r,className:d,style:x,onClick:m,isLoaded:f=!0,skeletonType:o="card"}){return s.jsxs("div",{className:e("surface-base rounded-2xl h-full flex flex-col relative",!r&&(c?"card-padding-sm":"card-padding"),l&&h[l],d),style:x,onClick:m,children:[a,!f&&(()=>{switch(o){case"chart":return s.jsx(n,{});case"list":return s.jsx(t,{});default:return s.jsx(i,{})}})()]})}export{x as B,i as C,r as L,d as P,l as S,f as a,m as b};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{c as e,aX as s}from"./index-CIoxFkCL.js";import{r as a,j as o,c as t}from"./vendor-react-Z6a030H9.js";import{A as r}from"./index-DC-GJ6vK.js";import{t as i}from"./config-TTDcOBVH.js";const n=e("ear-off",[["path",{d:"M6 18.5a3.5 3.5 0 1 0 7 0c0-1.57.92-2.52 2.04-3.46",key:"1qngmn"}],["path",{d:"M6 8.5c0-.75.13-1.47.36-2.14",key:"b06bma"}],["path",{d:"M8.8 3.15A6.5 6.5 0 0 1 19 8.5c0 1.63-.44 2.81-1.09 3.76",key:"g10hsz"}],["path",{d:"M12.5 6A2.5 2.5 0 0 1 15 8.5M10 13a2 2 0 0 0 1.82-1.18",key:"ygzou7"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22",key:"a6p6uj"}]]),c=a.memo(function({value:e,format:t,prefix:n,suffix:c,className:m,priority:l="medium"}){const[f,u]=a.useState(!1),[d,p]=a.useState(e);a.useEffect(()=>s(()=>{u(!0),p(0)},l),[l]),a.useEffect(()=>{f&&p(e)},[f,e]);const g=`${n??""}${e.toLocaleString(void 0,t)}${c??""}`;return f?o.jsx(r,{className:`${m??""} overflow-hidden`,format:t,prefix:n,suffix:c,transition:i.numberTicker,children:d}):o.jsx("span",{className:m,children:g})}),m=a.memo(function({ranges:e,selectedIndex:s,onSelect:a,isPending:r,size:i="default"}){const n="sm"===i?"toggle-group-sm":"micro"===i?"toggle-group-micro":"";return o.jsx("div",{className:t("toggle-group flex-shrink-0 overflow-x-auto",n,r&&"opacity-70"),children:e.map((e,r)=>o.jsx("button",{onClick:()=>a(r),className:t("toggle-group-item",s===r&&"active"),children:e.label},e.label))})});export{c as A,n as E,m as T};
|
||||
@@ -1 +0,0 @@
|
||||
import{c as e,aY as a}from"./index-DnB5FOpE.js";import{r as s,j as o,c as t}from"./vendor-react-BKIq8k5U.js";import{A as i}from"./index-4YcwiUNl.js";const r=e("ear-off",[["path",{d:"M6 18.5a3.5 3.5 0 1 0 7 0c0-1.57.92-2.52 2.04-3.46",key:"1qngmn"}],["path",{d:"M6 8.5c0-.75.13-1.47.36-2.14",key:"b06bma"}],["path",{d:"M8.8 3.15A6.5 6.5 0 0 1 19 8.5c0 1.63-.44 2.81-1.09 3.76",key:"g10hsz"}],["path",{d:"M12.5 6A2.5 2.5 0 0 1 15 8.5M10 13a2 2 0 0 0 1.82-1.18",key:"ygzou7"}],["line",{x1:"2",x2:"22",y1:"2",y2:"22",key:"a6p6uj"}]]),n={type:"spring",visualDuration:.4,bounce:.15,opacity:{duration:.15,ease:"linear"}},c=s.memo(function({value:e,format:t,prefix:r,suffix:c,className:l,priority:m="medium"}){const[u,f]=s.useState(!1),[p,d]=s.useState(e);s.useEffect(()=>a(()=>{f(!0),d(0)},m),[m]),s.useEffect(()=>{u&&d(e)},[u,e]);const g=`${r??""}${e.toLocaleString(void 0,t)}${c??""}`;return u?o.jsx(i,{className:`${l??""} overflow-hidden`,format:t,prefix:r,suffix:c,transition:n,children:p}):o.jsx("span",{className:l,children:g})}),l=s.memo(function({ranges:e,selectedIndex:a,onSelect:s,isPending:i,size:r="default"}){const n="sm"===r?"toggle-group-sm":"micro"===r?"toggle-group-micro":"";return o.jsx("div",{className:t("toggle-group flex-shrink-0 overflow-x-auto",n,i&&"opacity-70"),children:e.map((e,i)=>o.jsx("button",{onClick:()=>s(i),className:t("toggle-group-item",a===i&&"active"),children:e.label},e.label))})});export{c as A,r as E,l as T};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{c as a}from"./index-DnB5FOpE.js";const t=a("activity",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]]);export{t as A};
|
||||
import{c as a}from"./index-CIoxFkCL.js";const t=a("activity",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]]);export{t as A};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{c as a}from"./index-DnB5FOpE.js";const e=a("arrow-down",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]),r=a("arrow-up",[["path",{d:"m5 12 7-7 7 7",key:"hav0vg"}],["path",{d:"M12 19V5",key:"x0mq9r"}]]);export{e as A,r as a};
|
||||
import{c as a}from"./index-CIoxFkCL.js";const e=a("arrow-down",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]),r=a("arrow-up",[["path",{d:"m5 12 7-7 7 7",key:"hav0vg"}],["path",{d:"M12 19V5",key:"x0mq9r"}]]);export{e as A,r as a};
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
import{c as t}from"./index-CIoxFkCL.js";const h=t("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);export{h as C};
|
||||
@@ -1 +0,0 @@
|
||||
import{c as t}from"./index-DnB5FOpE.js";const h=t("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);export{h as C};
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
import{c}from"./index-DnB5FOpE.js";const e=c("circle-check",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]);export{e as C};
|
||||
import{c}from"./index-CIoxFkCL.js";const e=c("circle-check",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]);export{e as C};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{c as a}from"./index-DnB5FOpE.js";const c=a("compass",[["path",{d:"m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z",key:"9ktpf1"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]]);export{c as C};
|
||||
-1
@@ -1 +0,0 @@
|
||||
const s={snappy:{type:"spring",stiffness:500,damping:30},smooth:{type:"spring",stiffness:300,damping:30},gentle:{type:"spring",stiffness:200,damping:25}},n={fast:.15,medium:.3},e={easeIn:[.4,0,1,1]},a={normal:.05};export{s as a,n as d,e,a as s};
|
||||
+1
@@ -0,0 +1 @@
|
||||
const s={snappy:{type:"spring",stiffness:500,damping:30},smooth:{type:"spring",stiffness:300,damping:30},gentle:{type:"spring",stiffness:200,damping:25}},e={fast:.15,medium:.3},a={easeIn:[.4,0,1,1]},n={numberTicker:{type:"spring",visualDuration:.4,bounce:.15,opacity:{duration:.15,ease:"linear"}},numberReveal:{type:"spring",visualDuration:.6,bounce:.25,opacity:{duration:.2,ease:"linear"}}},i={normal:.05};export{s as a,e as d,a as e,i as s,n as t};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{c as e}from"./index-DnB5FOpE.js";const s=e("database",[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3",key:"msslwz"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5",key:"1wlel7"}],["path",{d:"M3 12A9 3 0 0 0 21 12",key:"mv7ke4"}]]);export{s as D};
|
||||
import{c as e}from"./index-CIoxFkCL.js";const s=e("database",[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3",key:"msslwz"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5",key:"1wlel7"}],["path",{d:"M3 12A9 3 0 0 0 21 12",key:"mv7ke4"}]]);export{s as D};
|
||||
-1
@@ -1 +0,0 @@
|
||||
function e(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}const t={},n=function(e,n,r){let o=Promise.resolve();if(n&&n.length>0){let e=function(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:"fulfilled",value:e}),e=>({status:"rejected",reason:e}))))};document.getElementsByTagName("link");const r=document.querySelector("meta[property=csp-nonce]"),l=(null==r?void 0:r.nonce)||(null==r?void 0:r.getAttribute("nonce"));o=e(n.map(e=>{if((e=function(e){return"/"+e}(e))in t)return;t[e]=!0;const n=e.endsWith(".css"),r=n?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${e}"]${r}`))return;const o=document.createElement("link");return o.rel=n?"stylesheet":"modulepreload",n||(o.as="script"),o.crossOrigin="",o.href=e,l&&o.setAttribute("nonce",l),document.head.appendChild(o),n?new Promise((t,n)=>{o.addEventListener("load",t),o.addEventListener("error",()=>n(new Error(`Unable to preload CSS for ${e}`)))}):void 0}))}function l(e){const t=new Event("vite:preloadError",{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return o.then(t=>{for(const e of t||[])"rejected"===e.status&&l(e.reason);return e().catch(l)})};export{n as _,e as g};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{c as a,cy as s}from"./index-CIoxFkCL.js";const c=a("compass",[["path",{d:"m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z",key:"9ktpf1"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]]),e=s(.65,0,.35,1),m=s(.33,1,.68,1),o=s(.33,.67,.67,1),p=s(.76,0,.24,1);export{c as C,m as a,e as b,o as c,p as e};
|
||||
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,2 @@
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/latin-400-BcR6IdUc.css","assets/latin-500-DCnAHIrw.css","assets/latin-600-CnkPk-vb.css","assets/latin-700-UC1i-VSI.css"])))=>i.map(i=>d[i]);
|
||||
import{_}from"./vendor-react-Z6a030H9.js";let t=!1,a=null;async function i(){if(!t)return a||(a=(async()=>{try{await Promise.all([_(()=>Promise.resolve({}),__vite__mapDeps([0])),_(()=>Promise.resolve({}),__vite__mapDeps([1])),_(()=>Promise.resolve({}),__vite__mapDeps([2])),_(()=>Promise.resolve({}),__vite__mapDeps([3]))]),t=!0}catch(a){}})(),a)}export{i as l};
|
||||
@@ -1,2 +0,0 @@
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/latin-400-CvIR-I2l.css","assets/latin-400-CePoWEYT.css","assets/latin-400-dmanHIEB.css","assets/latin-500-DwzRh_fR.css","assets/latin-400-CNx1qH9H.css","assets/latin-400-C7jFTHrn.css","assets/latin-500-CvoqYoKF.css","assets/latin-600-CdtqNIPt.css","assets/latin-400-BcR6IdUc.css","assets/latin-500-DCnAHIrw.css","assets/latin-600-CnkPk-vb.css","assets/latin-700-UC1i-VSI.css"])))=>i.map(i=>d[i]);
|
||||
import{_}from"./deckgl-DTsmDcfs.js";let t=!1,i=null;async function E(){if(!t)return i||(i=(async()=>{try{await Promise.all([_(()=>Promise.resolve({}),__vite__mapDeps([0])),_(()=>Promise.resolve({}),__vite__mapDeps([1])),_(()=>Promise.resolve({}),__vite__mapDeps([2])),_(()=>Promise.resolve({}),__vite__mapDeps([3])),_(()=>Promise.resolve({}),__vite__mapDeps([4])),_(()=>Promise.resolve({}),__vite__mapDeps([5])),_(()=>Promise.resolve({}),__vite__mapDeps([6])),_(()=>Promise.resolve({}),__vite__mapDeps([7])),_(()=>Promise.resolve({}),__vite__mapDeps([8])),_(()=>Promise.resolve({}),__vite__mapDeps([9])),_(()=>Promise.resolve({}),__vite__mapDeps([10])),_(()=>Promise.resolve({}),__vite__mapDeps([11]))]),t=!0}catch(i){}})(),i)}export{E as l};
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{c}from"./index-DnB5FOpE.js";const e=c("git-branch",[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]]);export{e as G};
|
||||
import{c}from"./index-CIoxFkCL.js";const e=c("git-branch",[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]]);export{e as G};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{c as a}from"./index-DnB5FOpE.js";const s=a("house",[["path",{d:"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8",key:"5wwlr5"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",key:"r6nss1"}]]);export{s as H};
|
||||
Binary file not shown.
Binary file not shown.
-1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{c as e}from"./index-DnB5FOpE.js";const c=e("info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);export{c as I};
|
||||
import{c as e}from"./index-CIoxFkCL.js";const c=e("info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);export{c as I};
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
@font-face{font-family:Instrument Sans;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/instrument-sans-latin-400-normal-DRC__1Mx.woff2) format("woff2"),url(/assets/instrument-sans-latin-400-normal-D1W7dsQl.woff) format("woff")}
|
||||
-1
@@ -1 +0,0 @@
|
||||
@font-face{font-family:"Instrument Serif";font-style:normal;font-display:swap;font-weight:400;src:url(/assets/instrument-serif-latin-400-normal-DnYpCC2O.woff2) format("woff2"),url(/assets/instrument-serif-latin-400-normal-BVbkICAY.woff) format("woff")}
|
||||
-1
@@ -1 +0,0 @@
|
||||
@font-face{font-family:"Jacquard 12";font-style:normal;font-display:swap;font-weight:400;src:url(/assets/jacquard-12-latin-400-normal-CFgZUtB9.woff2) format("woff2"),url(/assets/jacquard-12-latin-400-normal-DUTP1itc.woff) format("woff")}
|
||||
-1
@@ -1 +0,0 @@
|
||||
@font-face{font-family:Rubik Mono One;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/rubik-mono-one-latin-400-normal-U2Afr9Di.woff2) format("woff2"),url(/assets/rubik-mono-one-latin-400-normal-Cd19iSlZ.woff) format("woff")}
|
||||
-1
@@ -1 +0,0 @@
|
||||
@font-face{font-family:Cantora One;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/cantora-one-latin-400-normal-BBwF22QI.woff2) format("woff2"),url(/assets/cantora-one-latin-400-normal-DwNspYVg.woff) format("woff")}
|
||||
-1
@@ -1 +0,0 @@
|
||||
@font-face{font-family:Instrument Sans;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/instrument-sans-latin-500-normal-Dk9ku72i.woff2) format("woff2"),url(/assets/instrument-sans-latin-500-normal-Z6ESRlEs.woff) format("woff")}
|
||||
-1
@@ -1 +0,0 @@
|
||||
@font-face{font-family:IBM Plex Mono;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2) format("woff2"),url(/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff) format("woff")}
|
||||
-1
@@ -1 +0,0 @@
|
||||
@font-face{font-family:Instrument Sans;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/instrument-sans-latin-600-normal-B7fBEWYG.woff2) format("woff2"),url(/assets/instrument-sans-latin-600-normal-B9e8oLYv.woff) format("woff")}
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{c as a}from"./index-CIoxFkCL.js";const l=a("layers",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);export{l as L};
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
import{c as t,r as e,cq as n}from"./index-DnB5FOpE.js";const o=t("moon",[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]]),a=t("sun",[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]]);function s(t){if(Array.isArray(t))return t;if("string"==typeof t&&t.startsWith("["))try{const e=JSON.parse(t);return Array.isArray(e)?e:[]}catch{return[]}return[]}function r(t,o,a){const r=new Map;for(const n of o){const t=e(n);r.has(t)||r.set(t,n)}const c=new Map,h=new Map;for(const e of t){const t=e.route??e.route_type;if(!n(t))continue;const o=s(e.original_path);if(0===o.length)continue;const i=o.map(t=>t.toUpperCase());if(!e.transmitted&&i.length>=2){const t=i[i.length-2];if(t){const e=r.get(t);e&&h.set(e,(h.get(e)??0)+1)}}if(i.includes(a))for(const e of i){if(e===a)continue;const t=r.get(e);t&&c.set(t,(c.get(t)??0)+1)}}let i=0,f=0;for(const e of o)i=Math.max(i,c.get(e)??0),f=Math.max(f,h.get(e)??0);const p=new Map;let u=0,d=0,m=0;for(const e of o){const t=c.get(e)??0,n=h.get(e)??0,o=i>0?Math.round(t/i*100):0,a=f>0?Math.round(n/f*100):0,s=o+a;p.set(e,{hash:e,listenerCount:t,loudCount:n,listenerScore:o,loudScore:a,blendedScore:s}),u=Math.max(u,o),d=Math.max(d,a),m=Math.max(m,s)}return{scores:p,maxListenerScore:u,maxLoudScore:d,maxBlendedScore:m}}const c={YELLOW:"#FFB224",GREEN:"#46A758",RED:"#E5484D",GRAY:"#505050"};export{c as L,o as M,a as S,r as c};
|
||||
import{c as t,r as e,a_ as n}from"./index-CIoxFkCL.js";const o=t("moon",[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]]),a=t("sun",[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]]);function s(t){if(Array.isArray(t))return t;if("string"==typeof t&&t.startsWith("["))try{const e=JSON.parse(t);return Array.isArray(e)?e:[]}catch{return[]}return[]}function r(t,o,a){const r=new Map;for(const n of o){const t=e(n);r.has(t)||r.set(t,n)}const c=new Map,h=new Map;for(const e of t){const t=e.route??e.route_type;if(!n(t))continue;const o=s(e.original_path);if(0===o.length)continue;const i=o.map(t=>t.toUpperCase());if(!e.transmitted&&i.length>=2){const t=i[i.length-2];if(t){const e=r.get(t);e&&h.set(e,(h.get(e)??0)+1)}}if(i.includes(a))for(const e of i){if(e===a)continue;const t=r.get(e);t&&c.set(t,(c.get(t)??0)+1)}}let i=0,f=0;for(const e of o)i=Math.max(i,c.get(e)??0),f=Math.max(f,h.get(e)??0);const p=new Map;let u=0,d=0,m=0;for(const e of o){const t=c.get(e)??0,n=h.get(e)??0,o=i>0?Math.round(t/i*100):0,a=f>0?Math.round(n/f*100):0,s=o+a;p.set(e,{hash:e,listenerCount:t,loudCount:n,listenerScore:o,loudScore:a,blendedScore:s}),u=Math.max(u,o),d=Math.max(d,a),m=Math.max(m,s)}return{scores:p,maxListenerScore:u,maxLoudScore:d,maxBlendedScore:m}}const c={YELLOW:"#FFB224",GREEN:"#46A758",RED:"#E5484D",GRAY:"#505050"};export{c as L,o as M,a as S,r as c};
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
import{c as a}from"./index-DnB5FOpE.js";const e=a("loader-circle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);export{e as L};
|
||||
import{c as a}from"./index-CIoxFkCL.js";const e=a("loader-circle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);export{e as L};
|
||||
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 258 KiB |
+1
-1
@@ -1 +1 @@
|
||||
import{c}from"./index-DnB5FOpE.js";const r=c("map-pin",[["path",{d:"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0",key:"1r0f0z"}],["circle",{cx:"12",cy:"10",r:"3",key:"ilqhr7"}]]);export{r as M};
|
||||
import{c}from"./index-CIoxFkCL.js";const r=c("map-pin",[["path",{d:"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0",key:"1r0f0z"}],["circle",{cx:"12",cy:"10",r:"3",key:"ilqhr7"}]]);export{r as M};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
import{c as s}from"./index-CIoxFkCL.js";const a=s("minus",[["path",{d:"M5 12h14",key:"1ays0h"}]]);export{a as M};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user