Compare commits
5
Commits
0.3.0
...
cf2cc9f8ed
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf2cc9f8ed | ||
|
|
fd8bb16d40 | ||
|
|
ed9ed59de9 | ||
|
|
56038b7a7e | ||
|
|
7a8465b7fb |
+1
-1
@@ -625,7 +625,7 @@ state the exclusion of warranty; and each file should have at least
|
|||||||
the "copyright" line and a pointer to where the full notice is found.
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
Argus, an all in one network utility
|
Argus, an all in one network utility
|
||||||
Copyright (C) 2026 Jamse Brotosky
|
Copyright (C) 2026 James Brotosky
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
cd src-tauri && cargo clean
|
||||||
|
cargo clippy --fix --all-targets --allow-dirty
|
||||||
|
cargo fmt
|
||||||
|
cargo tauri build
|
||||||
|
cargo tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc
|
||||||
@@ -16,4 +16,4 @@ pub async fn check_privileges() -> Result<bool, String> {
|
|||||||
// On Windows, check if running as administrator
|
// On Windows, check if running as administrator
|
||||||
Ok(is_elevated::is_elevated())
|
Ok(is_elevated::is_elevated())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
use pulldown_cmark::{Options, Parser, html};
|
use pulldown_cmark::{html, Options, Parser};
|
||||||
|
|
||||||
const CHANGELOG_MD: &str = include_str!("../../../CHANGELOG.md");
|
const CHANGELOG_MD: &str = include_str!("../../../CHANGELOG.md");
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_changelog() -> Result<String, String> {
|
pub async fn get_changelog() -> Result<String, String> {
|
||||||
let options = Options::ENABLE_HEADING_ATTRIBUTES
|
let options =
|
||||||
| Options::ENABLE_STRIKETHROUGH
|
Options::ENABLE_HEADING_ATTRIBUTES | Options::ENABLE_STRIKETHROUGH | Options::ENABLE_TABLES;
|
||||||
| Options::ENABLE_TABLES;
|
|
||||||
|
|
||||||
let parser = Parser::new_ext(CHANGELOG_MD, options);
|
let parser = Parser::new_ext(CHANGELOG_MD, options);
|
||||||
let mut html_output = String::new();
|
let mut html_output = String::new();
|
||||||
html::push_html(&mut html_output, parser);
|
html::push_html(&mut html_output, parser);
|
||||||
|
|
||||||
Ok(html_output)
|
Ok(html_output)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ pub struct ExportRequest {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn export_csv(request: ExportRequest) -> Result<(), String> {
|
pub async fn export_csv(request: ExportRequest) -> Result<(), String> {
|
||||||
let file = File::create(&request.path)
|
let file = File::create(&request.path).map_err(|e| format!("Failed to create file: {}", e))?;
|
||||||
.map_err(|e| format!("Failed to create file: {}", e))?;
|
|
||||||
|
|
||||||
let mut writer = csv::Writer::from_writer(file);
|
let mut writer = csv::Writer::from_writer(file);
|
||||||
|
|
||||||
@@ -43,4 +42,4 @@ pub async fn export_csv(request: ExportRequest) -> Result<(), String> {
|
|||||||
tracing::info!("Exported {} hosts to {}", request.hosts.len(), request.path);
|
tracing::info!("Exported {} hosts to {}", request.hosts.len(), request.path);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,16 @@ pub async fn start_scan(app: AppHandle, config: ScanConfig) -> Result<(), String
|
|||||||
let port_timeout_ms = config.port_timeout_ms.unwrap_or(1000);
|
let port_timeout_ms = config.port_timeout_ms.unwrap_or(1000);
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
ping_sweep(&app, &ips, timeout_ms, cancel, port_scan, ports, port_timeout_ms).await;
|
ping_sweep(
|
||||||
|
&app,
|
||||||
|
&ips,
|
||||||
|
timeout_ms,
|
||||||
|
cancel,
|
||||||
|
port_scan,
|
||||||
|
ports,
|
||||||
|
port_timeout_ms,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ pub enum HostStatus {
|
|||||||
Up,
|
Up,
|
||||||
Down,
|
Down,
|
||||||
Filtered,
|
Filtered,
|
||||||
Unknown
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -42,7 +42,7 @@ pub struct PortResult {
|
|||||||
pub struct NetbiosInfo {
|
pub struct NetbiosInfo {
|
||||||
pub computer_name: Option<String>,
|
pub computer_name: Option<String>,
|
||||||
pub workgroup: Option<String>,
|
pub workgroup: Option<String>,
|
||||||
pub mac_from_netbios: Option<String>
|
pub mac_from_netbios: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -79,7 +79,7 @@ impl HostResult {
|
|||||||
updated_at: now_ms(),
|
updated_at: now_ms(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn down(ip: IpAddr) -> Self {
|
pub fn down(ip: IpAddr) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ip,
|
ip,
|
||||||
@@ -93,4 +93,4 @@ impl HostResult {
|
|||||||
updated_at: now_ms(),
|
updated_at: now_ms(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
pub mod host;
|
pub mod host;
|
||||||
pub mod monitor;
|
pub mod monitor;
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ use std::net::IpAddr;
|
|||||||
/// Perform reverse DNS lookup on a single IP.
|
/// Perform reverse DNS lookup on a single IP.
|
||||||
/// Returns the hostname if found, None otherwise.
|
/// Returns the hostname if found, None otherwise.
|
||||||
pub async fn reverse_lookup(ip: IpAddr) -> Option<String> {
|
pub async fn reverse_lookup(ip: IpAddr) -> Option<String> {
|
||||||
let result = tokio::task::spawn_blocking(move || {
|
let result = tokio::task::spawn_blocking(move || dns_lookup::lookup_addr(&ip)).await;
|
||||||
dns_lookup::lookup_addr(&ip)
|
|
||||||
}).await;
|
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(Ok(hostname)) => {
|
Ok(Ok(hostname)) => {
|
||||||
@@ -17,4 +15,4 @@ pub async fn reverse_lookup(ip: IpAddr) -> Option<String> {
|
|||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
|
pub mod dns;
|
||||||
pub mod range;
|
pub mod range;
|
||||||
pub mod dns;
|
|
||||||
@@ -140,4 +140,4 @@ mod tests {
|
|||||||
fn test_reversed_range() {
|
fn test_reversed_range() {
|
||||||
assert!(parse_ip_range("192.168.1.10-192.168.1.1").is_err());
|
assert!(parse_ip_range("192.168.1.10-192.168.1.1").is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
|
pub mod netbios;
|
||||||
pub mod ping;
|
pub mod ping;
|
||||||
pub mod ports;
|
pub mod ports;
|
||||||
pub mod netbios;
|
|
||||||
@@ -91,8 +91,7 @@ pub async fn ping_sweep(
|
|||||||
let nb_fut = query_netbios(enriched.ip, 500);
|
let nb_fut = query_netbios(enriched.ip, 500);
|
||||||
|
|
||||||
if port_scan {
|
if port_scan {
|
||||||
let scan_ports = ports2
|
let scan_ports = ports2.unwrap_or_else(|| DEFAULT_PORTS.to_vec());
|
||||||
.unwrap_or_else(|| DEFAULT_PORTS.to_vec());
|
|
||||||
let (nb_result, port_results) = tokio::join!(
|
let (nb_result, port_results) = tokio::join!(
|
||||||
nb_fut,
|
nb_fut,
|
||||||
scan_host_ports(enriched.ip, &scan_ports, port_timeout_ms)
|
scan_host_ports(enriched.ip, &scan_ports, port_timeout_ms)
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ fn service_name(port: u16) -> Option<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_PORTS: &[u16] = &[
|
pub const DEFAULT_PORTS: &[u16] = &[
|
||||||
21, 22, 23, 25, 53, 80, 110, 111, 135, 139, 143,
|
21, 22, 23, 25, 53, 80, 110, 111, 135, 139, 143, 443, 445, 993, 995, 1433, 1521, 3306, 3389,
|
||||||
443, 445, 993, 995, 1433, 1521, 3306, 3389,
|
|
||||||
5432, 5900, 5985, 5986, 8080, 8088, 8443,
|
5432, 5900, 5985, 5986, 8080, 8088, 8443,
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -96,7 +95,10 @@ async fn grab_banner(mut stream: TcpStream, port: u16) -> Option<String> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(data) = probe {
|
if let Some(data) = probe {
|
||||||
if timeout(banner_timeout, stream.write_all(data)).await.is_err() {
|
if timeout(banner_timeout, stream.write_all(data))
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,8 +109,15 @@ async fn grab_banner(mut stream: TcpStream, port: u16) -> Option<String> {
|
|||||||
Ok(Ok(n)) if n > 0 => {
|
Ok(Ok(n)) if n > 0 => {
|
||||||
buf.truncate(n);
|
buf.truncate(n);
|
||||||
// Strip to printable ASCII, replace control chars
|
// Strip to printable ASCII, replace control chars
|
||||||
let banner: String = buf.iter()
|
let banner: String = buf
|
||||||
.map(|&b| if b >= 0x20 && b < 0x7f { b as char } else { ' ' })
|
.iter()
|
||||||
|
.map(|&b| {
|
||||||
|
if (0x20..0x7f).contains(&b) {
|
||||||
|
b as char
|
||||||
|
} else {
|
||||||
|
' '
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let banner = banner.trim().to_string();
|
let banner = banner.trim().to_string();
|
||||||
if banner.is_empty() {
|
if banner.is_empty() {
|
||||||
@@ -124,11 +133,7 @@ async fn grab_banner(mut stream: TcpStream, port: u16) -> Option<String> {
|
|||||||
|
|
||||||
const MAX_PORT_CONCURRENT: usize = 64;
|
const MAX_PORT_CONCURRENT: usize = 64;
|
||||||
|
|
||||||
pub async fn scan_host_ports(
|
pub async fn scan_host_ports(ip: IpAddr, ports: &[u16], timeout_ms: u64) -> Vec<PortResult> {
|
||||||
ip: IpAddr,
|
|
||||||
ports: &[u16],
|
|
||||||
timeout_ms: u64,
|
|
||||||
) -> Vec<PortResult> {
|
|
||||||
let mut results = Vec::with_capacity(ports.len());
|
let mut results = Vec::with_capacity(ports.len());
|
||||||
|
|
||||||
for chunk in ports.chunks(MAX_PORT_CONCURRENT) {
|
for chunk in ports.chunks(MAX_PORT_CONCURRENT) {
|
||||||
@@ -148,4 +153,4 @@ pub async fn scan_host_ports(
|
|||||||
|
|
||||||
results.sort_by_key(|p| p.port);
|
results.sort_by_key(|p| p.port);
|
||||||
results
|
results
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,6 +211,11 @@
|
|||||||
<option value="nord">Nord</option>
|
<option value="nord">Nord</option>
|
||||||
<option value="solarized">Solarized Dark</option>
|
<option value="solarized">Solarized Dark</option>
|
||||||
<option value="mocha">Catppuccin Mocha</option>
|
<option value="mocha">Catppuccin Mocha</option>
|
||||||
|
<option value="onedark">One Dark</option>
|
||||||
|
<option value="rosepine">Rosé Pine</option>
|
||||||
|
<option value="rosepine-moon">Rosé Pine Moon</option>
|
||||||
|
<option value="tokyonight">Tokyo Night</option>
|
||||||
|
<option value="iceberg">Iceberg</option>
|
||||||
</select>
|
</select>
|
||||||
<span class="status-bar-item" id="privilege-status">unprivileged</span>
|
<span class="status-bar-item" id="privilege-status">unprivileged</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+195
-10
@@ -1456,7 +1456,7 @@ body {
|
|||||||
--border: #0d4450;
|
--border: #0d4450;
|
||||||
--border-bright: #1a5c6a;
|
--border-bright: #1a5c6a;
|
||||||
|
|
||||||
--text-primary: #93a1a1;
|
--text-primary: #cb4b16;
|
||||||
--text-secondary: #839496;
|
--text-secondary: #839496;
|
||||||
--text-muted: #586e75;
|
--text-muted: #586e75;
|
||||||
|
|
||||||
@@ -1489,11 +1489,11 @@ body {
|
|||||||
--border: #313244;
|
--border: #313244;
|
||||||
--border-bright: #45475a;
|
--border-bright: #45475a;
|
||||||
|
|
||||||
--text-primary: #cdd6f4;
|
--text-primary: #94e2d5;
|
||||||
--text-secondary: #a6adc8;
|
--text-secondary: #a6adc8;
|
||||||
--text-muted: #6c7086;
|
--text-muted: #6c7086;
|
||||||
|
|
||||||
--cyan: #89b4fa;
|
--cyan: #b4befe;
|
||||||
--cyan-hover: #7ba4eb;
|
--cyan-hover: #7ba4eb;
|
||||||
--cyan-dim: rgba(137, 180, 250, 0.08);
|
--cyan-dim: rgba(137, 180, 250, 0.08);
|
||||||
--cyan-glow: rgba(137, 180, 250, 0.2);
|
--cyan-glow: rgba(137, 180, 250, 0.2);
|
||||||
@@ -1515,24 +1515,209 @@ body {
|
|||||||
filter: hue-rotate(40deg) saturate(0.6) brightness(0.9);
|
filter: hue-rotate(40deg) saturate(0.6) brightness(0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme="latte"] .port-tag {
|
:root[data-theme="mocha"] .port-tag {
|
||||||
background: rgba(30, 102, 245, 0.1);
|
background: rgba(30, 102, 245, 0.1);
|
||||||
border-color: rgba(30, 102, 245, 0.2);
|
border-color: rgba(30, 102, 245, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme="latte"] #privilege-status::before {
|
:root[data-theme="mocha"] #privilege-status::before {
|
||||||
background: var(--status-up);
|
background: var(--status-up);
|
||||||
box-shadow: 0 0 4px rgba(64, 160, 43, 0.3);
|
box-shadow: 0 0 4px rgba(64, 160, 43, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme="latte"] ::-webkit-scrollbar-track {
|
:root[data-theme="mocha"] ::-webkit-scrollbar-track {
|
||||||
background: var(--bg-primary);
|
background: var(--bg-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme="latte"] ::-webkit-scrollbar-thumb {
|
:root[data-theme="mocha"] ::-webkit-scrollbar-thumb {
|
||||||
background: var(--border-bright);
|
background: var(--border-bright);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== Theme: One Dark ===== */
|
||||||
|
|
||||||
|
:root[data-theme="onedark"] {
|
||||||
|
--bg-primary: #282c34;
|
||||||
|
--bg-secondary: #21252b;
|
||||||
|
--bg-tertiary: #2c313a;
|
||||||
|
--bg-hover: #3e4451;
|
||||||
|
--bg-active: #4b5263;
|
||||||
|
--border: #3e4451;
|
||||||
|
--border-bright: #4b5263;
|
||||||
|
|
||||||
|
--text-primary: #abb2bf;
|
||||||
|
--text-secondary: #848b98;
|
||||||
|
--text-muted: #5c6370;
|
||||||
|
|
||||||
|
--cyan: #61afef;
|
||||||
|
--cyan-hover: #519fdf;
|
||||||
|
--cyan-dim: rgba(97, 175, 239, 0.08);
|
||||||
|
--cyan-glow: rgba(97, 175, 239, 0.2);
|
||||||
|
--cyan-subtle: rgba(97, 175, 239, 0.03);
|
||||||
|
|
||||||
|
--status-up: #98c379;
|
||||||
|
--status-up-glow: rgba(152, 195, 121, 0.15);
|
||||||
|
--status-down: #e06c75;
|
||||||
|
--status-down-glow: rgba(224, 108, 117, 0.15);
|
||||||
|
--status-filtered: #e5c07b;
|
||||||
|
--status-filtered-glow: rgba(229, 192, 123, 0.15);
|
||||||
|
--status-unknown: #5c6370;
|
||||||
|
|
||||||
|
--danger: #e06c75;
|
||||||
|
--danger-hover: #d05f68;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="onedark"] .toolbar-circuit {
|
||||||
|
filter: hue-rotate(30deg) saturate(0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Theme: Rosé Pine ===== */
|
||||||
|
|
||||||
|
:root[data-theme="rosepine"] {
|
||||||
|
--bg-primary: #191724;
|
||||||
|
--bg-secondary: #1f1d2e;
|
||||||
|
--bg-tertiary: #26233a;
|
||||||
|
--bg-hover: #403d52;
|
||||||
|
--bg-active: #524f67;
|
||||||
|
--border: #26233a;
|
||||||
|
--border-bright: #403d52;
|
||||||
|
|
||||||
|
--text-primary: #31748f;
|
||||||
|
--text-secondary: #908caa;
|
||||||
|
--text-muted: #6e6a86;
|
||||||
|
|
||||||
|
--cyan: #c4a7e7;
|
||||||
|
--cyan-hover: #b498d7;
|
||||||
|
--cyan-dim: rgba(196, 167, 231, 0.08);
|
||||||
|
--cyan-glow: rgba(196, 167, 231, 0.2);
|
||||||
|
--cyan-subtle: rgba(196, 167, 231, 0.03);
|
||||||
|
|
||||||
|
--status-up: #9ccfd8;
|
||||||
|
--status-up-glow: rgba(156, 207, 216, 0.15);
|
||||||
|
--status-down: #eb6f92;
|
||||||
|
--status-down-glow: rgba(235, 111, 146, 0.15);
|
||||||
|
--status-filtered: #f6c177;
|
||||||
|
--status-filtered-glow: rgba(246, 193, 119, 0.15);
|
||||||
|
--status-unknown: #6e6a86;
|
||||||
|
|
||||||
|
--danger: #eb6f92;
|
||||||
|
--danger-hover: #db5f82;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="rosepine"] .toolbar-circuit {
|
||||||
|
filter: hue-rotate(80deg) saturate(0.5) brightness(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Theme: Rosé Pine Moon ===== */
|
||||||
|
|
||||||
|
:root[data-theme="rosepine-moon"] {
|
||||||
|
--bg-primary: #232136;
|
||||||
|
--bg-secondary: #2a273f;
|
||||||
|
--bg-tertiary: #393552;
|
||||||
|
--bg-hover: #44415a;
|
||||||
|
--bg-active: #56526e;
|
||||||
|
--border: #393552;
|
||||||
|
--border-bright: #44415a;
|
||||||
|
|
||||||
|
--text-primary: #9ccfd8;
|
||||||
|
--text-secondary: #908caa;
|
||||||
|
--text-muted: #6e6a86;
|
||||||
|
|
||||||
|
--cyan: #c4a7e7;
|
||||||
|
--cyan-hover: #b498d7;
|
||||||
|
--cyan-dim: rgba(196, 167, 231, 0.08);
|
||||||
|
--cyan-glow: rgba(196, 167, 231, 0.2);
|
||||||
|
--cyan-subtle: rgba(196, 167, 231, 0.03);
|
||||||
|
|
||||||
|
--status-up: #9ccfd8;
|
||||||
|
--status-up-glow: rgba(156, 207, 216, 0.15);
|
||||||
|
--status-down: #eb6f92;
|
||||||
|
--status-down-glow: rgba(235, 111, 146, 0.15);
|
||||||
|
--status-filtered: #f6c177;
|
||||||
|
--status-filtered-glow: rgba(246, 193, 119, 0.15);
|
||||||
|
--status-unknown: #6e6a86;
|
||||||
|
|
||||||
|
--danger: #eb6f92;
|
||||||
|
--danger-hover: #db5f82;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="rosepine-moon"] .toolbar-circuit {
|
||||||
|
filter: hue-rotate(80deg) saturate(0.5) brightness(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Theme: Tokyo Night ===== */
|
||||||
|
|
||||||
|
:root[data-theme="tokyonight"] {
|
||||||
|
--bg-primary: #1a1b26;
|
||||||
|
--bg-secondary: #16161e;
|
||||||
|
--bg-tertiary: #1f2335;
|
||||||
|
--bg-hover: #292e42;
|
||||||
|
--bg-active: #33384d;
|
||||||
|
--border: #1f2335;
|
||||||
|
--border-bright: #292e42;
|
||||||
|
|
||||||
|
--text-primary: #bb9af7;
|
||||||
|
--text-secondary: #a9b1d6;
|
||||||
|
--text-muted: #565f89;
|
||||||
|
|
||||||
|
--cyan: #7aa2f7;
|
||||||
|
--cyan-hover: #6a92e7;
|
||||||
|
--cyan-dim: rgba(122, 162, 247, 0.08);
|
||||||
|
--cyan-glow: rgba(122, 162, 247, 0.2);
|
||||||
|
--cyan-subtle: rgba(122, 162, 247, 0.03);
|
||||||
|
|
||||||
|
--status-up: #9ece6a;
|
||||||
|
--status-up-glow: rgba(158, 206, 106, 0.15);
|
||||||
|
--status-down: #f7768e;
|
||||||
|
--status-down-glow: rgba(247, 118, 142, 0.15);
|
||||||
|
--status-filtered: #e0af68;
|
||||||
|
--status-filtered-glow: rgba(224, 175, 104, 0.15);
|
||||||
|
--status-unknown: #565f89;
|
||||||
|
|
||||||
|
--danger: #f7768e;
|
||||||
|
--danger-hover: #e7667e;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="tokyonight"] .toolbar-circuit {
|
||||||
|
filter: hue-rotate(40deg) saturate(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Theme: Iceberg ===== */
|
||||||
|
|
||||||
|
:root[data-theme="iceberg"] {
|
||||||
|
--bg-primary: #161821;
|
||||||
|
--bg-secondary: #1e2132;
|
||||||
|
--bg-tertiary: #252837;
|
||||||
|
--bg-hover: #33374c;
|
||||||
|
--bg-active: #3d4259;
|
||||||
|
--border: #252837;
|
||||||
|
--border-bright: #33374c;
|
||||||
|
|
||||||
|
--text-primary: #c6c8d1;
|
||||||
|
--text-secondary: #818596;
|
||||||
|
--text-muted: #444b71;
|
||||||
|
|
||||||
|
--cyan: #84a0c6;
|
||||||
|
--cyan-hover: #7490b6;
|
||||||
|
--cyan-dim: rgba(132, 160, 198, 0.08);
|
||||||
|
--cyan-glow: rgba(132, 160, 198, 0.2);
|
||||||
|
--cyan-subtle: rgba(132, 160, 198, 0.03);
|
||||||
|
|
||||||
|
--status-up: #b4be82;
|
||||||
|
--status-up-glow: rgba(180, 190, 130, 0.15);
|
||||||
|
--status-down: #e27878;
|
||||||
|
--status-down-glow: rgba(226, 120, 120, 0.15);
|
||||||
|
--status-filtered: #e2a478;
|
||||||
|
--status-filtered-glow: rgba(226, 164, 120, 0.15);
|
||||||
|
--status-unknown: #444b71;
|
||||||
|
|
||||||
|
--danger: #e27878;
|
||||||
|
--danger-hover: #d26868;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="iceberg"] .toolbar-circuit {
|
||||||
|
filter: hue-rotate(30deg) saturate(0.4) brightness(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
/* ===== Theme: Hacker (Easter Egg) ===== */
|
/* ===== Theme: Hacker (Easter Egg) ===== */
|
||||||
|
|
||||||
:root[data-theme="hacker"] {
|
:root[data-theme="hacker"] {
|
||||||
@@ -1605,13 +1790,13 @@ body {
|
|||||||
display: block;
|
display: block;
|
||||||
background:
|
background:
|
||||||
repeating-linear-gradient(0deg,
|
repeating-linear-gradient(0deg,
|
||||||
rgba(0, 0, 0, 0.4) 0px,
|
rgba(0, 0, 0, 0.5) 0px,
|
||||||
rgba(0, 0, 0, 0.4) 1px,
|
rgba(0, 0, 0, 0.5) 1px,
|
||||||
transparent 1px,
|
transparent 1px,
|
||||||
transparent 4px),
|
transparent 4px),
|
||||||
linear-gradient(180deg,
|
linear-gradient(180deg,
|
||||||
transparent 0%,
|
transparent 0%,
|
||||||
rgba(0, 255, 65, 0.03) 50%,
|
rgba(0, 255, 65, 0.09) 50%,
|
||||||
transparent 100%);
|
transparent 100%);
|
||||||
animation: crt-flicker 3s infinite;
|
animation: crt-flicker 3s infinite;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user