Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 44 additions & 9 deletions IsThereNet/IsThereNetApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import os.log
import ServiceManagement
import Sparkle
import SwiftUI
import QuartzCore

private func mainAsyncAfter(_ duration: TimeInterval, _ action: @escaping () -> Void) -> DispatchWorkItem {
let workItem = DispatchWorkItem { action() }
Expand Down Expand Up @@ -503,6 +504,18 @@ private struct SoundsConfig: Codable, Equatable {
}
}

private struct BarGradientConfig: Codable, Equatable {
var enabled: Bool? = false
// Percentage of the bar height used for the fade-to-transparent (1-100)
var fadePercent: Double? = 60.0
}

private struct BarConfig: Codable, Equatable {
// Thickness of the horizontal bar in points. Clamped at runtime.
var height: Double? = 3.0
var gradient: BarGradientConfig? = BarGradientConfig()
}

private struct Config: Codable, Equatable {
var pingIP = "1.1.1.1"
var pingIntervalSeconds = 5.0
Expand All @@ -515,6 +528,7 @@ private struct Config: Codable, Equatable {
var colors: ColorsConfig? = ColorsConfig()
var screen: String? = "all"
var launchAtLogin: Bool? = true
var bar: BarConfig? = BarConfig()
}

private var CONFIG_FS_WATCHER: FSEventStreamRef?
Expand Down Expand Up @@ -730,19 +744,40 @@ class Window {
fader?.cancel()
closer?.cancel()

let box = NSBox()
box.boxType = .custom
box.fillColor = color
box.frame = NSRect(x: 0, y: 10, width: screen.frame.width + 10, height: 3)
// Resolve bar visuals from config (with clamping and fallbacks)
let configuredHeight = CGFloat(CONFIG.bar?.height ?? 3.0)
let barHeight = max(1.0, min(configuredHeight, 10.0)) // keep within the visible window height
let gradientEnabled = (CONFIG.bar?.gradient?.enabled ?? false) && barHeight >= 2.0
let fadePercent = max(1.0, min(CGFloat(CONFIG.bar?.gradient?.fadePercent ?? 60.0), 100.0))

// Create the bar view (solid or gradient)
let barView = NSView(frame: NSRect(x: 0, y: 10, width: screen.frame.width + 10, height: barHeight))
barView.wantsLayer = true
if gradientEnabled {
let grad = CAGradientLayer()
grad.colors = [color.cgColor, color.withAlphaComponent(0.0).cgColor]
grad.startPoint = CGPoint(x: 0.5, y: 1.0)
grad.endPoint = CGPoint(x: 0.5, y: 0.0)
// reach full transparency by fadePercent of the height
grad.locations = [0.0, NSNumber(value: Double(fadePercent / 100.0))]
grad.frame = barView.bounds
barView.layer = grad
} else {
let layer = CALayer()
layer.backgroundColor = color.cgColor
layer.frame = barView.bounds
barView.layer = layer
}

box.shadow = NSShadow()
box.shadow!.shadowColor = color
box.shadow!.shadowBlurRadius = 3
box.shadow!.shadowOffset = .init(width: 0, height: -2)
// Subtle shadow to give a soft edge under the bar
barView.shadow = NSShadow()
barView.shadow!.shadowColor = color
barView.shadow!.shadowBlurRadius = 3
barView.shadow!.shadowOffset = .init(width: 0, height: -2)

let containerView = NSView()
containerView.frame = NSRect(x: 0, y: 0, width: screen.frame.width + 10, height: 20)
containerView.addSubview(box)
containerView.addSubview(barView)

window.setContentSize(NSSize(width: screen.frame.width + 10, height: 20))
window.contentView = containerView
Expand Down
32 changes: 32 additions & 0 deletions IsThereNet/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"pingIntervalSeconds": 5,
"pingTimeoutSeconds": 1,
"sounds": {
"connected": "Submerge",
"slow": "Bubble",
"disconnected": "Jump",
"volume": 1
},
"colors": {
"connected": "systemGreen",
"slow": "systemYellow",
"disconnected": "systemRed"
},
"launchAtLogin": true,
"pingIP": "1.1.1.1",
"pingSlowThresholdMilliseconds": 300,
"shellCommandOnStatusChange": "",
"fadeSeconds": {
"disconnected": 0,
"connected": 5,
"slow": 10
},
"screen": "all",
"bar": {
"height": 3,
"gradient": {
"enabled": false,
"fadePercent": 60
}
}
}
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ That IP was chosen for multiple reasons:

## Uh.. how do I quit this app?

### Bar appearance (new)

You can adjust the thickness of the top bar and enable an optional fade gradient:

```json
"bar": {
"height": 3, // Bar thickness in points (min 1, max 10 applied)
"gradient": {
"enabled": false, // If true, fades to transparent towards the bottom
"fadePercent": 60 // Portion of the bar height used for the fade (1–100)
}
}
```

Notes:
- Defaults preserve current look: height 3, gradient disabled.
- Values are validated: height clamped 1–10; fadePercent clamped 1–100. For very small heights (< 2), gradient is automatically disabled.

The app has no Dock icon and no menubar icon so to quit it you'd need to do *one of the following*:

- Launch **Activity Monitor**, find **IsThereNet** and press the ❌ button at the top
Expand Down