dock #44

Merged
S4NKALP merged 14 commits from feat/dock into macos 2026-07-30 17:34:30 +00:00
16 changed files with 118 additions and 31 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
* @S4NKALP
+1 -2
View File
@@ -2,9 +2,8 @@ name: CodeQL
on:
push:
branches: [main, dev]
branches: [macos]
pull_request:
branches: [main, dev]
schedule:
- cron: "0 6 * * 1"
-1
View File
@@ -2,7 +2,6 @@ name: Commit Lint
on:
pull_request:
branches: [main, dev]
permissions:
contents: read
+1 -2
View File
@@ -2,7 +2,7 @@ name: Install Script Test
on:
push:
branches: [main, dev]
branches: [macos]
paths:
- "install.sh"
- "src/window/globalmenu/**"
@@ -10,7 +10,6 @@ on:
- "pyproject.toml"
- "uv.lock"
pull_request:
branches: [main, dev]
paths:
- "install.sh"
- "src/window/globalmenu/**"
+1 -2
View File
@@ -2,9 +2,8 @@ name: Pre-commit
on:
push:
branches: [main, dev]
branches: [macos]
pull_request:
branches: [main, dev]
permissions:
contents: read
+2
View File
@@ -143,6 +143,8 @@ See [Desktop Widgets Guide](docs/desktop_widgets.md) for full docs and examples.
## Documentation
> **Note:** These docs were written with the assistance of LLM tools. Some phrasing may reflect that.
| Doc | What's inside |
| --------------------------------------------- | -------------------------------------------------- |
| [Installation Guide](docs/installation.md) | Automated & manual install, dependencies |
+1
View File
@@ -0,0 +1 @@
module.exports = { extends: ["@commitlint/config-conventional"] };
+5 -3
View File
@@ -14,6 +14,7 @@ from tomlkit import document as toml_document
from tomlkit import dump as toml_dump
from tomlkit import item as toml_item
from tomlkit import load as toml_load
from tomlkit import loads as toml_loads
from utils.gtk_utils import toml_file
@@ -126,12 +127,13 @@ class ConfigService:
self._config = toml_load(f)
else:
try:
from utils.constants import DEFAULT
from utils.constants import generate_default_toml
os.makedirs(os.path.dirname(self._config_file), exist_ok=True)
self._config = self._dict_to_toml(DEFAULT)
raw = generate_default_toml()
self._config = toml_loads(raw)
with open(self._config_file, "w") as f:
toml_dump(self._config, f)
f.write(raw)
logger.info(
f"[ConfigService] Generated default config at {self._config_file}"
)
+17 -1
View File
@@ -49,7 +49,13 @@ def load_config():
service = start_config_service()
return service.get_all()
except ImportError:
# Fallback to direct file loading
pass
try:
from utils.constants import generate_default_toml
except ImportError:
generate_default_toml = None
config = {}
if os.path.exists(CONFIG_FILE):
@@ -60,6 +66,16 @@ def load_config():
config = dict(tomlkit.load(f))
except Exception as e:
logger.error(f"Error loading config: {e}")
elif generate_default_toml:
try:
raw = generate_default_toml()
with open(CONFIG_FILE, "w") as f:
f.write(raw)
import tomlkit
config = dict(tomlkit.loads(raw))
except Exception as e:
logger.error(f"Error generating default config: {e}")
return config
+63
View File
@@ -1,3 +1,7 @@
from tomlkit import document as _document
from tomlkit import dumps as _dumps
from tomlkit import table as _table
DEFAULT = {
"general": {
"debug": False,
@@ -11,6 +15,7 @@ DEFAULT = {
"always_occluded": False,
"icon_size": 52,
"hide_special_workspace_apps": True,
"hover_scale": 1.6,
},
"panel": {
"imac_button": True,
@@ -42,3 +47,61 @@ DEFAULT = {
"limited_apps_history": ["Spotify"],
},
}
def generate_default_toml() -> str:
"""Build default config.toml with inline comments."""
doc = _document()
general = _table()
general.add("debug", False)
general.add("wallpapers_dir", "src/assets/wallpaper_example/")
general.add("keyboard_layouts", ["us"])
general.add("weather_location", "")
doc.add("general", general)
dock = _table()
dock.add("enabled", True)
dock.add("auto_hide", True)
dock.add("always_occluded", False)
dock.add("icon_size", 52)
dock.add("hide_special_workspace_apps", True)
dock.add("hover_scale", 1.6)
dock["hover_scale"].comment(
"1.0 (min, no effect) to 2.0 (max) | >2.0 not recommended — icons get blurry/pixelated on hover"
)
doc.add("dock", dock)
panel = _table()
panel.add("imac_button", True)
panel.add("systray", True)
panel.add("systray_ignore", ["blueman", "network"])
panel.add("control_center", True)
panel.add("search", True)
panel.add("global_menu", True)
panel.add("network", True)
panel.add("battery", True)
panel.add("notification_center", True)
panel.add("workspace_indicator", True)
panel.add("bluetooth", True)
panel.add("date_time", True)
panel.add("night_light_temperature", 4500)
panel.add("notch", True)
panel.add("osd", True)
panel.add("hide_special_workspace", True)
panel.add("custom_mods", True)
doc.add("panel", panel)
switcher = _table()
switcher.add("live_preview", True)
switcher.add("live_preview_delay_ms", 200)
switcher.add("window_switcher", True)
doc.add("switcher", switcher)
notification = _table()
notification.add("timeout", "5s")
notification.add("ignored_apps", ["Hyprshot"])
notification.add("limited_apps_history", ["Spotify"])
doc.add("notification", notification)
return _dumps(doc)
+5 -5
View File
@@ -41,11 +41,11 @@ from .constants import (
INDICATOR_COLOR,
INDICATOR_H,
INDICATOR_RADIUS,
MAX_SCALE,
MIN_SCALE,
PINNED_APPS_FILE,
SEPARATOR_COLOR,
SEPARATOR_WIDTH,
max_scale,
)
from .items import DockHitTest, DockItem, DockModel
from .layout import DockLayout
@@ -126,7 +126,7 @@ class DockCanvas(Gtk.DrawingArea):
return int(config().get("dock.icon_size", 52))
def _icon_cache_size(self) -> int:
return int(self._base_icon_size() * MAX_SCALE)
return int(self._base_icon_size() * max_scale())
def _get_desktop_apps(self) -> list:
if not self._desktop_apps:
@@ -140,7 +140,7 @@ class DockCanvas(Gtk.DrawingArea):
def _canvas_height(self) -> int:
size = self._base_icon_size()
return int(
size * MAX_SCALE + CANVAS_TOP_PAD + 2 * BG_PADDING_V + INDICATOR_H + 4
size * max_scale() + CANVAS_TOP_PAD + 2 * BG_PADDING_V + INDICATOR_H + 4
)
def _canvas_max_width(self) -> int:
@@ -149,7 +149,7 @@ class DockCanvas(Gtk.DrawingArea):
return 200
size = self._base_icon_size()
return int(
n * size * MAX_SCALE + max(n - 1, 0) * ICON_GAP + 2 * BG_PADDING_H + 40
n * size * max_scale() + max(n - 1, 0) * ICON_GAP + 2 * BG_PADDING_H + 40
)
def _canvas_min_width(self) -> int:
@@ -655,7 +655,7 @@ class DockCanvas(Gtk.DrawingArea):
bg_h = size + 2 * BG_PADDING_V
bg_y = h - INDICATOR_H - bg_h
baseline_y = bg_y + BG_PADDING_V + size
icon_top_y = baseline_y - size * MAX_SCALE
icon_top_y = baseline_y - size * max_scale()
self._mouse_inside = (
base_start_x <= event.x <= base_start_x + total_base
and icon_top_y <= event.y <= baseline_y
+6 -1
View File
@@ -1,3 +1,4 @@
from services.config import config
from utils.gtk_utils import toml_file
PINNED_APPS_FILE = toml_file("dock.toml")
@@ -7,7 +8,11 @@ IDLE_THRESHOLD = 0.0005
ANIM_FPS = 60
ANIM_INTERVAL_MS = 1000 // ANIM_FPS
MAX_SCALE = 2.0
def max_scale() -> float:
return float(config().get("dock.hover_scale", 2.0))
MIN_SCALE = 1.0
SIGMA_FACTOR = 1.3
+2 -2
View File
@@ -5,9 +5,9 @@ from .constants import (
BG_PADDING_V,
ICON_GAP,
INDICATOR_H,
MAX_SCALE,
MIN_SCALE,
SIGMA_FACTOR,
max_scale,
)
from .items import DockItem
@@ -27,7 +27,7 @@ class DockLayout:
return
sigma = base_icon_size * SIGMA_FACTOR
amplitude = MAX_SCALE - MIN_SCALE
amplitude = max_scale() - MIN_SCALE
n = len(items)
base_w = base_icon_size + ICON_GAP
+3
View File
@@ -135,6 +135,9 @@ class Dock(Window):
if config().has_changed("dock.hide_special_workspace_apps", old_config):
self.canvas._rebuild_model()
if config().has_changed("dock.hover_scale", old_config):
self.canvas.update_icon_size()
def _update_visibility(self) -> None:
if config().get("dock.enabled", True):
self.show()
+10 -4
View File
@@ -1,4 +1,4 @@
from fabric.utils import Gdk, Gtk, logger
from fabric.utils import Gdk, Gtk
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
@@ -90,12 +90,13 @@ class SettingsEntry(Entry):
def on_change(self, entry, *args):
value = entry.get_text()
# Try to convert to int if possible
try:
if value.isdigit():
value = int(value)
except Exception as e:
logger.error(f"An error occurred: {e}")
else:
value = float(value)
except (ValueError, TypeError):
pass
config().set(self.config_key, value)
config().save()
@@ -400,6 +401,11 @@ class SettingsWindow(Gtk.Window):
SettingsSwitch("dock.hide_special_workspace_apps"),
"Hide apps from special workspace in dock",
),
SettingsRow(
"Hover Scale",
SettingsEntry("dock.hover_scale"),
"Icon scale on hover (1.0 = off, 1.6 = default, 2.0 = max)",
),
],
)
@@ -99,11 +99,7 @@ class ClipboardPlugin(SpotlightPlugin):
def _load_history(self) -> None:
result = run_command(["cliphist", "list"], timeout=5)
raw = result.stdout if isinstance(result.stdout, str) else ""
logger.info(
f"[Clipboard] _load_history: stdout type={type(result.stdout).__name__} len={len(raw)} returncode={result.returncode}"
)
lines = raw.splitlines()
logger.info(f"[Clipboard] _load_history: {len(lines)} lines from cliphist")
new_entries = []
for line in lines[:100]:
if "\t" not in line:
@@ -122,13 +118,9 @@ class ClipboardPlugin(SpotlightPlugin):
if "binary data" in content:
entry["type"] = "image"
new_entries.append(entry)
logger.info(f"[Clipboard] _load_history: parsed {len(new_entries)} entries")
with self._history_lock:
self._history.clear()
self._history.extend(new_entries)
logger.info(
f"[Clipboard] _load_history: history now has {len(self._history)} entries"
)
def _filter_entries(self, query: str) -> list[dict]:
q = query.strip().lower()