Author SHA1 Message Date
AramJonghu 82c307de11 fix(Dockerfile): adds mpv dep
C++ / fmt (pull_request) Failing after 5s
JS/TS / fmt & lint (pull_request) Successful in 13s
C++ / build & lint (pull_request) Failing after 20s
Python / test & nuitka build (pull_request) Canceled after 24s
Python / fmt & lint (pull_request) Canceled after 26s
Rust / clippy & fmt (pull_request) Failing after 3s
2026-10-30 02:51:22 +01:00
AramJonghu 388482c620 ci(rust): fixes and optimizations
C++ / fmt (pull_request) Failing after 5s
C++ / build & lint (pull_request) Failing after 13s
JS/TS / fmt & lint (pull_request) Successful in 11s
Python / fmt & lint (pull_request) Successful in 26s
Rust / clippy & fmt (pull_request) Failing after 50s
Python / test & nuitka build (pull_request) Failing after 1m23s
- removes cargo check since the clippy step builds already
- image switched to rust:alpine
- less correctness during build for speedier compilation
2026-10-30 02:49:22 +01:00
AramJonghu ab4a662013 ci(cpp): introduce caching cmake and build + format optimization 2026-10-30 02:46:17 +01:00
AramJonghu fda39fc4c0 feat(makefile): adds a make formatter as task runner
C++ / fmt (pull_request) Failing after 6s
C++ / build & lint (pull_request) Failing after 10s
JS/TS / fmt & lint (pull_request) Successful in 14s
Python / fmt & lint (pull_request) Successful in 29s
Python / test & nuitka build (pull_request) Failing after 1m25s
Rust / build, clippy & fmt (pull_request) Failing after 4m49s
2026-10-30 02:22:57 +01:00
AramJonghu 9e97569467 fix(ci): cpp clang-tidy now properly reads non-build files only 2026-10-30 02:22:57 +01:00
AramJonghu 630b05ff2c feat(makefile): added no nuitka option for cpp-compile task 2026-10-30 02:22:57 +01:00
AramJonghu b86f865396 chore(gitignore): ordering known files and directories 2026-10-30 02:22:57 +01:00
AramJonghu 34585a639b chore(gitignore): nuitka compile files ignored and categorized 2026-10-30 02:22:57 +01:00
AramJonghu 163370354a fix(clang-tidy): make cpp-lint has proper clang-tidy check 2026-10-30 02:22:57 +01:00
AramJonghu 012d2777c2 chore(Dockerfile): add future dependencies 2026-10-30 02:22:57 +01:00
zachandAramJonghu 9c0b74ff66 Merge branch 'main' into feat/task-runner-ci 2026-10-30 02:22:57 +01:00
AramJonghu 24825250bb feat(makefile): added qmlformat + install -> compile in cpp step 2026-10-30 02:22:57 +01:00
AramJonghu 1364b5ed1a feat(makefile): C++, Python, and Rust checks included (incomplete) 2026-10-30 02:22:57 +01:00
AramJonghu ef4d849b4b feat(makefile): makefile build & install + progress on cpp and py 2026-10-30 02:22:57 +01:00
AramJonghu e12a047da2 format(clang): make cpp-fmt result 2026-10-30 02:22:57 +01:00
AramJonghu 5db2718b04 ci: rework Dockerfile image + buildx dep added 2026-10-30 02:22:57 +01:00
AramJonghu e50a28b877 refactor(workflows): combined steps where it makes sense 2026-10-30 02:22:57 +01:00
AramJonghu 0e23b18f72 fix(ci): missing dependency wayland-scanner 2026-10-30 02:22:57 +01:00
zach 90184bff58 Merge pull request 'feat: video wallpapers' (#163) from feat/video-wallpaper into main
Reviewed-on: #163
2026-10-29 19:55:05 +01:00
zach abdde2450d fix: don't use lambda expression
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Failing after 1m27s
Rust / fmt (pull_request) Successful in 31s
Rust / clippy (pull_request) Successful in 58s
2026-10-29 19:50:02 +01:00
zach fd05b3d4c4 Merge branch 'main' into feat/video-wallpaper
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 13s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Failing after 37s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Failing after 1m28s
Rust / fmt (pull_request) Successful in 39s
Rust / clippy (pull_request) Successful in 1m5s
2026-10-29 19:03:10 +01:00
zach 9d8bde1413 fix: update for hyprland 0.57 2026-10-29 11:59:50 +01:00
zach 118b8f482a fix: fix sub-pixel layout arrangement for normalized resolutions
C++ / fmt (pull_request) Failing after 7s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Failing after 31s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Failing after 1m28s
Rust / fmt (pull_request) Successful in 30s
Rust / clippy (pull_request) Successful in 54s
2026-10-27 23:00:26 +01:00
zach b58b4fdc9c fix: remove more uses of cliprect and finally fix workspace's layer texture
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Failing after 33s
Rust / build (pull_request) Successful in 48s
Python / verify (pull_request) Failing after 1m24s
Rust / fmt (pull_request) Successful in 32s
Rust / clippy (pull_request) Successful in 57s
2026-10-27 15:15:57 +01:00
zach a56afd7fac fix: don't link eglDestroyImageKHR
C++ / fmt (pull_request) Failing after 7s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Failing after 27s
Rust / build (pull_request) Successful in 47s
Python / verify (pull_request) Failing after 1m24s
Rust / fmt (pull_request) Successful in 29s
Rust / clippy (pull_request) Successful in 45s
2026-10-27 02:30:55 +01:00
zach b0cb43f75c feat: add vaapi path for video wallpapers on generic gpus
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Failing after 32s
Rust / build (pull_request) Successful in 52s
Python / verify (pull_request) Failing after 1m23s
Rust / fmt (pull_request) Successful in 51s
Rust / clippy (pull_request) Successful in 1m2s
2026-10-27 02:15:16 +01:00
zach e655ec5bc7 feat: add wallpaper transitions, store crop in database per-wallpaper, move CUDA context creation to async thread
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Failing after 28s
Python / verify (pull_request) Failing after 1m29s
Rust / build (pull_request) Successful in 53s
Rust / fmt (pull_request) Successful in 42s
Rust / clippy (pull_request) Successful in 1m4s
2026-10-26 14:47:47 +01:00
zach dc0f222ace fix: don't store dbPath as const
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Failing after 27s
Rust / build (pull_request) Successful in 47s
Python / verify (pull_request) Failing after 1m24s
Rust / fmt (pull_request) Successful in 30s
Rust / clippy (pull_request) Successful in 53s
2026-10-26 03:13:33 +01:00
zach de19fd6ac4 fix: restore code removed by incorrect replacement command
C++ / fmt (pull_request) Failing after 5s
C++ / clang-tidy (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 6s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Failing after 28s
Rust / build (pull_request) Successful in 48s
Python / verify (pull_request) Failing after 1m19s
Rust / fmt (pull_request) Successful in 36s
Rust / clippy (pull_request) Successful in 1m0s
2026-10-26 03:03:20 +01:00
zach 9edbcbd610 feat: skip two-way copy for cuda devices when decoding video wallpapers + cache wallpapers as blobs in new database
C++ / fmt (pull_request) Failing after 11s
C++ / build (pull_request) Failing after 14s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Failing after 28s
Rust / build (pull_request) Successful in 47s
Python / verify (pull_request) Failing after 1m20s
Rust / fmt (pull_request) Successful in 28s
Rust / clippy (pull_request) Successful in 59s
2026-10-26 02:51:28 +01:00
zach cd44d27b74 chore: suppress more warnings
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 29s
Rust / build (pull_request) Successful in 59s
Python / verify (pull_request) Failing after 1m31s
Rust / fmt (pull_request) Successful in 33s
Rust / clippy (pull_request) Successful in 52s
2026-10-25 02:10:50 +02:00
zach 0d0faecb1c feat: initial video wallpaper implementation 2026-10-25 02:10:50 +02:00
zach 200a6a5d54 fix: suppress private headers cmake warning 2026-10-25 02:07:38 +02:00
zach 2d2231745c fix: remove layer from settings when fading 2026-10-25 01:49:53 +02:00
zach 187e97a081 fix: notifications no longer hide when dashboard is open 2026-10-24 17:29:17 +02:00
zach 0f54d93675 fix: suppress unnecessary log output 2026-10-24 17:12:35 +02:00
zach 5401cfbc0a feat: implement file and text drop handling for chat and add shrunk sidebar override for window view 2026-10-24 16:28:13 +02:00
zach e35c87b3f2 fix: reduce shader usage and fix greeter 2026-10-24 00:35:24 +02:00
zach cbd4e6caa7 chore: move sidebar directory to modules 2026-10-23 00:35:20 +02:00
zach 36cddc96c3 fix: reduce shader usage 2026-10-23 00:05:08 +02:00
zach 6781078467 fix: short nightlight fade duration if missed start or end time or if manually activated 2026-10-19 19:21:57 +02:00
zach d3345906b9 Merge pull request 'Feat: LLM Chat' (#155) from feat/llm-chat-tab into main
Reviewed-on: #155
2026-10-18 17:05:05 +02:00
zach 08d42e1f8a chore: formatter & clangd lsp option
C++ / fmt (pull_request) Successful in 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 27s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Successful in 1m40s
Rust / fmt (pull_request) Successful in 28s
Rust / clippy (pull_request) Successful in 45s
2026-10-18 15:23:40 +02:00
zach 45aa6c9005 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 20s
C++ / clang-tidy (pull_request) Failing after 12s
JS/TS / fmt (pull_request) Successful in 6s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 48s
Rust / fmt (pull_request) Successful in 28s
Python / verify (pull_request) Successful in 1m45s
Rust / clippy (pull_request) Successful in 46s
2026-10-18 14:43:00 +02:00
zach 1763b03287 Merge pull request 'Chore/remove nix support' (#159) from chore/remove-nix-support into main
Reviewed-on: #159
2026-10-18 14:42:50 +02:00
zach 72ff6ad42a Merge branch 'main' into chore/remove-nix-support
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Successful in 1m35s
Rust / fmt (pull_request) Successful in 29s
Rust / clippy (pull_request) Successful in 55s
2026-10-18 14:42:43 +02:00
zach 2a45740020 Merge pull request 'fix(pr-template): adds an "Other" issue type' (#160) from fix/ci-pr-template into main
Reviewed-on: #160
2026-10-18 14:42:35 +02:00
zach 0a503bc27c fix: remove unimplemented method
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 27s
Rust / build (pull_request) Successful in 49s
Rust / fmt (pull_request) Successful in 28s
Python / verify (pull_request) Successful in 1m41s
Rust / clippy (pull_request) Successful in 45s
2026-10-18 13:03:25 +02:00
zach 1fe8a3a959 chore: restructure llm plugin to be more coherent & fix various bugs in llm ui
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m35s
Rust / fmt (pull_request) Successful in 36s
Rust / clippy (pull_request) Successful in 54s
2026-10-18 12:58:38 +02:00
AramJonghu ae25f33fc8 fix(pr-template): adds an "Other" issue type
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 10s
Rust / fmt (pull_request) Successful in 48s
Rust / build (pull_request) Successful in 1m9s
Rust / clippy (pull_request) Successful in 52s
Python / static (pull_request) Successful in 55s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 9s
Python / verify (pull_request) Successful in 1m26s
2026-10-14 00:22:33 +02:00
AramJonghu 02267ec9a6 docs(readme): adjust Nix support documentation
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 27s
Rust / build (pull_request) Successful in 50s
Rust / fmt (pull_request) Successful in 32s
Python / verify (pull_request) Successful in 2m16s
C++ / fmt (pull_request) Failing after 7s
C++ / build (pull_request) Failing after 12s
Rust / clippy (pull_request) Successful in 50s
C++ / clang-tidy (pull_request) Failing after 26s
2026-10-14 00:08:17 +02:00
AramJonghu 4b8d74a7e5 ci(cleanup): removed NixOS from issue template
Avoids confusion
2026-10-14 00:08:17 +02:00
AramJonghu 1fa3c84a56 chore(nix): remove all .nix files and flake.lock 2026-10-14 00:08:17 +02:00
zach fc81be922f Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 48s
Python / verify (pull_request) Successful in 1m38s
Rust / fmt (pull_request) Successful in 28s
Rust / clippy (pull_request) Successful in 46s
2026-10-12 16:06:23 +02:00
zach bee8cd061b Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 7s
C++ / build (pull_request) Failing after 11s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 48s
Python / verify (pull_request) Successful in 1m39s
Rust / fmt (pull_request) Successful in 28s
Rust / clippy (pull_request) Successful in 46s
2026-10-12 15:58:33 +02:00
zach 2cabd72fc0 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 12s
C++ / clang-tidy (pull_request) Failing after 12s
JS/TS / fmt (pull_request) Successful in 8s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 31s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m32s
Rust / fmt (pull_request) Successful in 51s
Rust / clippy (pull_request) Successful in 1m1s
2026-10-12 15:54:52 +02:00
zach a4f6aa1d4a Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 16s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 14s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 29s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Successful in 1m37s
Rust / fmt (pull_request) Successful in 31s
Rust / clippy (pull_request) Successful in 49s
2026-10-11 08:42:22 +02:00
zach bf6a7027e4 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 7s
C++ / build (pull_request) Failing after 13s
C++ / clang-tidy (pull_request) Failing after 12s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 27s
Rust / build (pull_request) Successful in 49s
Rust / fmt (pull_request) Successful in 28s
Python / verify (pull_request) Successful in 1m40s
Rust / clippy (pull_request) Successful in 47s
2026-10-08 12:09:59 +02:00
zach d70fb61e4a gemini glow fixes + positioning for window view
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Successful in 1m38s
Rust / fmt (pull_request) Successful in 27s
Rust / clippy (pull_request) Successful in 53s
2026-10-08 12:08:48 +02:00
zach 4c410d638c Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 7s
C++ / build (pull_request) Failing after 21s
C++ / clang-tidy (pull_request) Failing after 12s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 29s
Rust / build (pull_request) Successful in 47s
Python / verify (pull_request) Successful in 1m33s
Rust / fmt (pull_request) Successful in 28s
Rust / clippy (pull_request) Successful in 49s
2026-10-07 22:39:19 +02:00
zach a13fa8442b fix: qt 6.12 build
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 22s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 54s
Python / verify (pull_request) Successful in 1m41s
Rust / fmt (pull_request) Successful in 28s
Rust / clippy (pull_request) Successful in 53s
2026-10-07 22:37:46 +02:00
zach 4d75b317cd revert rounding var
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 18s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 8s
JS/TS / lint (pull_request) Successful in 12s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 52s
Python / verify (pull_request) Successful in 1m38s
Rust / fmt (pull_request) Successful in 31s
Rust / clippy (pull_request) Successful in 1m0s
2026-10-07 13:33:54 +02:00
zach 76823eff79 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 29s
C++ / build (pull_request) Failing after 33s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 51s
Python / verify (pull_request) Successful in 1m36s
Rust / fmt (pull_request) Successful in 43s
Rust / clippy (pull_request) Successful in 1m4s
2026-10-07 13:32:49 +02:00
zach c06cbea854 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 8s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 48s
Python / verify (pull_request) Successful in 1m40s
Rust / fmt (pull_request) Successful in 25s
Rust / clippy (pull_request) Successful in 43s
2026-10-06 13:33:41 +02:00
zach a00bbe50f2 add rounding var
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 8s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 48s
Python / verify (pull_request) Successful in 1m43s
Rust / fmt (pull_request) Successful in 25s
Rust / clippy (pull_request) Successful in 46s
2026-10-06 00:55:50 +02:00
zach 11e94fbb4b Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 47s
Python / verify (pull_request) Successful in 1m40s
Rust / fmt (pull_request) Successful in 29s
Rust / clippy (pull_request) Successful in 46s
2026-10-05 19:33:42 +02:00
zach b91befa9f7 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 11s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m30s
Rust / fmt (pull_request) Successful in 30s
Rust / clippy (pull_request) Successful in 48s
2026-10-05 09:58:00 +02:00
zach 99fdfbc465 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 4s
C++ / clang-tidy (pull_request) Failing after 10s
C++ / build (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 35s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Successful in 1m40s
Rust / fmt (pull_request) Successful in 28s
Rust / clippy (pull_request) Successful in 51s
2026-10-04 18:20:23 +02:00
zach e148a0f843 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 12s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 8s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 26s
Rust / build (pull_request) Successful in 48s
Rust / fmt (pull_request) Successful in 29s
Python / verify (pull_request) Successful in 1m40s
Rust / clippy (pull_request) Successful in 44s
2026-10-04 13:55:22 +02:00
zach f1918172c3 fix: merge two strokecanvas into one + better cursorShape and pointer handling
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 8s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 47s
Python / verify (pull_request) Successful in 1m34s
Rust / fmt (pull_request) Successful in 35s
Rust / clippy (pull_request) Successful in 1m0s
2026-10-04 13:53:21 +02:00
zach 506b69165d Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 16s
JS/TS / lint (pull_request) Successful in 10s
Python / static (pull_request) Successful in 35s
Rust / build (pull_request) Successful in 50s
Python / verify (pull_request) Successful in 1m39s
Rust / fmt (pull_request) Successful in 29s
Rust / clippy (pull_request) Successful in 51s
2026-10-03 20:26:19 +02:00
zach add3beed59 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 30s
Rust / build (pull_request) Successful in 48s
Python / verify (pull_request) Successful in 1m37s
Rust / fmt (pull_request) Successful in 26s
Rust / clippy (pull_request) Successful in 47s
2026-10-02 19:54:45 +02:00
zach 4b67cc1d88 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 8s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 32s
Rust / build (pull_request) Successful in 51s
Rust / fmt (pull_request) Successful in 29s
Python / verify (pull_request) Successful in 1m55s
Rust / clippy (pull_request) Successful in 45s
2026-10-01 17:11:29 +02:00
zach c64088441b Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 24s
C++ / clang-tidy (pull_request) Failing after 18s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 27s
Rust / build (pull_request) Successful in 52s
Rust / fmt (pull_request) Successful in 29s
Python / verify (pull_request) Successful in 1m44s
Rust / clippy (pull_request) Successful in 45s
2026-09-30 23:33:42 +02:00
zach e8bf8b9727 fix: restore lost changes
C++ / fmt (pull_request) Failing after 3s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 7s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 25s
Rust / build (pull_request) Successful in 50s
Rust / fmt (pull_request) Successful in 30s
Python / verify (pull_request) Successful in 1m44s
Rust / clippy (pull_request) Successful in 45s
2026-09-30 19:30:06 +02:00
zach 54f966b4ea Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 6s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Successful in 29s
Rust / build (pull_request) Successful in 47s
Rust / fmt (pull_request) Successful in 30s
Python / verify (pull_request) Successful in 1m43s
Rust / clippy (pull_request) Successful in 46s
2026-09-30 18:41:12 +02:00
zach c1f26e7dcb fix: add wheelinverter to cmake 2026-09-30 18:40:46 +02:00
zach 63b96471b0 Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 3s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 10s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m40s
Rust / fmt (pull_request) Successful in 27s
Rust / clippy (pull_request) Successful in 46s
2026-09-30 17:59:14 +02:00
zach 4f797a9741 separate dashboard and settings changes to new branch
C++ / fmt (pull_request) Successful in 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m40s
Rust / fmt (pull_request) Successful in 26s
Rust / clippy (pull_request) Successful in 46s
2026-09-30 15:11:51 +02:00
zach 83bc153ee3 fix: format and apply lint fixes in python script 2026-09-30 15:01:28 +02:00
zach ca51f660ad chore: format & fix prettierignore 2026-09-30 15:01:28 +02:00
zach 5153cc4f17 Merge branch 'main' into feat/llm-chat-tab 2026-09-30 15:01:28 +02:00
Inorishioandzach bdfb9fc241 fixed: mousewheel swallow 2026-09-30 15:01:28 +02:00
zach 13f1a3fede chore: format 2026-09-30 15:01:28 +02:00
zach d8a3d5732e new chatlistview component, file attachments, performance & primitive test 2026-09-30 15:01:28 +02:00
zach 51ed270774 Merge branch 'main' into feat/llm-chat-tab 2026-09-30 15:01:28 +02:00
zach 83f6f95f6e gitignore 2026-09-30 15:01:28 +02:00
zach cafdce2352 Merge branch 'main' into feat/llm-chat-tab 2026-09-30 15:01:28 +02:00
zach 5fc6fda804 fix: truncate context logic 2026-09-30 15:01:28 +02:00
zach a59e4286ad fix: tool call prompt 2026-09-30 15:01:28 +02:00
zach e2786a3f1e fix: truncate webfetch content to fit context 2026-09-30 15:01:28 +02:00
zach e700a4f96b use blobgroup for window chat interface + slim sidebar 2026-09-30 15:01:28 +02:00
zach a8fdc31b8d feat: implement findChild* methods 2026-09-30 15:01:28 +02:00
zach bc927b16af feat: implement findChild* methods 2026-09-30 15:01:28 +02:00
zach 57a6e48dcb fix: property link modules 2026-09-30 15:01:28 +02:00
zach 15769ba78e fix: add gnuinstalldirs include 2026-09-30 15:01:28 +02:00
zach be541d8f8d fix: cmake install dir 2026-09-30 15:01:28 +02:00
zach 0d4c08cfbb fix: cmake install dir 2026-09-30 15:01:28 +02:00
zach d3ac675492 fix: uncomment settings index cache path 2026-09-30 15:01:28 +02:00
zach e58440fea4 chore: split cmake into multiple files and fix settings searching regex + add enabled option to llm 2026-09-30 15:01:28 +02:00
zach d5d2311c24 fix: font family 2026-09-30 15:01:19 +02:00
AramJonghuandzach 5ec682d780 fix(Docker): add required deps to dockerfile 2026-09-30 15:01:19 +02:00
zach 79bedd5119 chore: format python codeicons 2026-09-30 15:01:19 +02:00
zach 14b54206b1 chore: format llm c files 2026-09-30 15:01:19 +02:00
zach 2df1cfe9aa chore: format llm c files 2026-09-30 15:01:19 +02:00
zach 88bb413f9a fab menu and model selector 2026-09-30 15:01:19 +02:00
zach 1ab49d4b38 better support for opening chat in floating window 2026-09-30 15:01:19 +02:00
zach 293bf67e09 better scrollbar in codeblocks + enter anim for delegates 2026-08-27 01:47:17 +02:00
zach c33dbc147f rotated view + wheel inverter 2026-08-26 17:10:34 +02:00
zach 78988700be async parsing + tex caching 2026-08-26 02:06:40 +02:00
zach 3946541f87 async parsing + tex caching 2026-08-26 02:06:10 +02:00
zach e18875a752 add markdown parsing + latex + tree-sitter highlighting for codeblocks in llm responses 2026-08-24 23:55:51 +02:00
zach aae3757daf better chat list view + anchor to bottom. sqlite db for chats + LIFO-ordered 2026-08-23 16:37:11 +02:00
zach 37c6a9986e start initial work on detaching chat to floating window 2026-08-20 20:24:54 +02:00
zach 9657e5092a add removal of chat sessions, auto title + icon, proper stick-to-bottom as response is streamed 2026-08-20 17:40:33 +02:00
zach 47bab21e22 initial commit for llm chat tab in sidebar 2026-08-20 00:27:48 +02:00
317 changed files with 28458 additions and 3378 deletions
+3
View File
@@ -0,0 +1,3 @@
#omz revision: d2b7f557cceaddb16ba89bbf8200d8a8df35c4d8
#omz fpath: /home/zach/.oh-my-zsh/plugins/colored-man-pages /home/zach/.oh-my-zsh/plugins/universalarchive /home/zach/.oh-my-zsh/plugins/tldr /home/zach/.oh-my-zsh/plugins/extract /home/zach/.oh-my-zsh/plugins/copypath /home/zach/.oh-my-zsh/plugins/cp /home/zach/.oh-my-zsh/plugins/conda-env /home/zach/.oh-my-zsh/plugins/command-not-found /home/zach/.oh-my-zsh/plugins/dirhistory /home/zach/.oh-my-zsh/plugins/copybuffer /home/zach/.oh-my-zsh/plugins/copyfile /home/zach/.oh-my-zsh/custom/plugins/fast-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-autosuggestions /home/zach/.oh-my-zsh/plugins/archlinux /home/zach/.oh-my-zsh/plugins/web-search /home/zach/.oh-my-zsh/plugins/sudo /home/zach/.oh-my-zsh/plugins/git /home/zach/.oh-my-zsh/functions /home/zach/.oh-my-zsh/completions /home/zach/.oh-my-zsh/custom/functions /home/zach/.oh-my-zsh/custom/completions /home/zach/tools/ripgrep/complete /home/linuxbrew/.linuxbrew/share/zsh/site-functions /home/zach/.oh-my-zsh/plugins/colored-man-pages /home/zach/.oh-my-zsh/plugins/universalarchive /home/zach/.oh-my-zsh/plugins/tldr /home/zach/.oh-my-zsh/plugins/extract /home/zach/.oh-my-zsh/plugins/copypath /home/zach/.oh-my-zsh/plugins/cp /home/zach/.oh-my-zsh/plugins/conda-env /home/zach/.oh-my-zsh/plugins/command-not-found /home/zach/.oh-my-zsh/plugins/dirhistory /home/zach/.oh-my-zsh/plugins/copybuffer /home/zach/.oh-my-zsh/plugins/copyfile /home/zach/.oh-my-zsh/custom/plugins/fast-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting /home/zach/.oh-my-zsh/custom/plugins/zsh-autosuggestions /home/zach/.oh-my-zsh/plugins/archlinux /home/zach/.oh-my-zsh/plugins/web-search /home/zach/.oh-my-zsh/plugins/sudo /home/zach/.oh-my-zsh/plugins/git /home/zach/.oh-my-zsh/functions /home/zach/.oh-my-zsh/completions /home/zach/.oh-my-zsh/custom/functions /home/zach/.oh-my-zsh/custom/completions /home/zach/.oh-my-zsh/cache/completions /home/zach/tools/ripgrep/complete /home/linuxbrew/.linuxbrew/share/zsh/site-functions /usr/local/share/zsh/site-functions /usr/share/zsh/site-functions /usr/share/zsh/functions/Calendar /usr/share/zsh/functions/Chpwd /usr/share/zsh/functions/Completion /usr/share/zsh/functions/Completion/Base /usr/share/zsh/functions/Completion/Linux /usr/share/zsh/functions/Completion/Unix /usr/share/zsh/functions/Completion/X /usr/share/zsh/functions/Completion/Zsh /usr/share/zsh/functions/Exceptions /usr/share/zsh/functions/MIME /usr/share/zsh/functions/Math /usr/share/zsh/functions/Misc /usr/share/zsh/functions/Newuser /usr/share/zsh/functions/Prompts /usr/share/zsh/functions/TCP /usr/share/zsh/functions/VCS_Info /usr/share/zsh/functions/VCS_Info/Backends /usr/share/zsh/functions/Zftp /usr/share/zsh/functions/Zle /home/zach/.zfunc
+5
View File
@@ -0,0 +1,5 @@
CompileFlags:
Remove:
# GCC-only flags from the system's default spec that clang/clangd cannot
# parse. They appear in compile_commands.json but are not set by the project.
- -mno-direct-extern-access
-1
View File
@@ -38,7 +38,6 @@ body:
attributes:
label: OS
options:
- NixOS
- Arch
- Debian / Ubuntu
- Fedora
+1
View File
@@ -30,6 +30,7 @@ body:
- Refactor / style
- CI / build / tooling
- Documentation
- Other (Describe in description box with header "Issue type")
validations:
required: true
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v4
- name: Install Docker CLI
run: apk add --no-cache docker-cli
run: apk add --no-cache docker-cli docker-cli-buildx
- name: Login to registry
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.aramjonghu.dev --username aramjonghu --password-stdin
+22 -21
View File
@@ -17,46 +17,47 @@ jobs:
run: |
find . \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \
-not -path './build/*' \
-exec clang-format -i --style=file {} +
git diff --exit-code && echo "clang-format: passed"
-exec clang-format --dry-run -Werror --style=file {} +
build:
build & lint:
runs-on: alpine
container:
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
- name: Build
run: ninja -C build
clang-tidy:
runs-on: alpine
container:
image: git.aramjonghu.dev/aramjonghu/zshell-ci:latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Cache CMake build
uses: actions/cache@v4
with:
fetch-depth: 0
path: build
key: cpp-build-${{ hashFiles('CMakeLists.txt', '**/CMakeLists.txt') }}
restore-keys: |
cpp-build-
- name: Cache ccache
uses: actions/cache@v4
with:
path: ~/.ccache
key: ccache-${{ runner.os }}-${{ github.sha }}
restore-keys: |
ccache-${{ runner.os }}-
- name: Configure
run: cmake -B build -G Ninja -DENABLE_MODULES=plugin -DCMAKE_BUILD_TYPE=Release
- name: Build
run: ninja -C build
env:
CMAKE_CXX_COMPILER_LAUNCHER: ccache
- name: clang-tidy
run: |
sed -i 's/-mno-direct-extern-access //g' build/compile_commands.json
set -o pipefail
run-clang-tidy -p build -j $(nproc) -header-filter="Plugins/.*" 2>&1 | \
grep -v 'Suppressed\|non-user\|non-system\|unknown argument.*mno-direct\|Found compiler error\|warnings generated\.\|^Use -header-filter'
run-clang-tidy -p build -j "$(nproc)" \
-source-filter="^$(pwd)/Plugins/" \
-header-filter=".*Plugins/.*" \
-removed-arg=-mno-direct-extern-access
+1 -15
View File
@@ -4,7 +4,7 @@ on:
pull_request:
jobs:
fmt:
fmt & lint:
runs-on: alpine
container: node:26-alpine
@@ -24,20 +24,6 @@ jobs:
else
echo "No JS/TS files found"
fi
lint:
runs-on: alpine
container: node:26-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git
- name: ESLint
run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
+3 -3
View File
@@ -7,7 +7,7 @@ on:
pull_request:
jobs:
static:
fmt & lint:
runs-on: debian
container: node:26-trixie-slim
@@ -33,7 +33,7 @@ jobs:
. .venv/bin/activate
ruff check .
verify:
test & nuitka build:
if: always()
runs-on: debian
container: node:26-trixie-slim
@@ -64,4 +64,4 @@ jobs:
- name: Nuitka module check
run: |
. .venv/bin/activate
nuitka --module --include-package=zshell cli/src/zshell/
nuitka --module --include-package=zshell cli/src/zshell/
+12 -99
View File
@@ -4,9 +4,9 @@ on:
pull_request:
jobs:
build:
clippy & fmt:
runs-on: alpine
container: node:26-alpine
container: rust:alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
@@ -16,34 +16,30 @@ jobs:
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-build-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
key: rust-build-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
rust-${{ runner.os }}-cargo-
- name: Install tools
run: |
apk add --no-cache \
git \
cargo \
rust
apk add --no-cache git
rustup component add clippy rustfmt
- name: Cargo check
- name: Clippy (with compile check)
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo check --manifest-path "$manifest" && \
echo "$manifest: check passed" || \
{ echo "$manifest: check failed"; exit 1; }
cargo clippy --manifest-path "$manifest" -- -D warnings && \
echo "$manifest: Clippy passed" || \
{ echo "$manifest: Clippy failed"; status=1; }
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
@@ -51,39 +47,6 @@ jobs:
echo "No Rust project found"
fi
fmt:
runs-on: alpine
container: node:26-alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-fmt-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools
run: |
apk add --no-cache \
git \
cargo \
rust \
rustfmt
- name: Format check
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
@@ -100,53 +63,3 @@ jobs:
else
echo "No Rust project found"
fi
clippy:
runs-on: alpine
container: node:26-alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-clippy-${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools
run: |
apk add --no-cache \
git \
cargo \
rust \
rust-clippy
- name: Clippy
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo clippy --manifest-path "$manifest" --all-targets --all-features -- -D warnings && \
echo "$manifest: Clippy passed" || \
{ echo "$manifest: Clippy failed"; status=1; }
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found"
fi
+25 -10
View File
@@ -1,20 +1,35 @@
**/__pycache__/
./result/
.pyre/
.cache/
.venv/
.qmlls.ini
build/
compile_commands.json
testpython
pkg/
*.tar.*
uv.lock
.qtcreator/
dist/
**/target/
**/test-plugins/
**/Charts/
network-dev/
**/zshell.build/
**/zshell.dist/
# Qml
.qtcreator/
.qmlls.ini
# CMake compile artifacts
.cache/
compile_commands.json
build/
# Python
uv.lock
testpython
.venv/
**/__pycache__/
# Nuitka compile
**/zshell.pyi
**/zshell.cpython-*
**/zshell.build/
# Agents stuff
.opencode/
run-agent.sh
AGENTS.md
+1
View File
@@ -1,3 +1,4 @@
.venv/
scripts/fzf.js
scripts/fuzzysort.js
scripts/settings-indexer.js
+2 -1
View File
@@ -3,8 +3,9 @@ FunctionsSpacing=true
IndentWidth=4
MaxColumnWidth=-1
NewlineType=native
NormalizeOrder=true
GroupAttributesTogether=false
ObjectsSpacing=true
SemicolonRule=always
SingleLineEmptyObjects=true
SortImports=false
UseTabs=true
+12 -2
View File
@@ -50,6 +50,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(QT_NO_PRIVATE_MODULE_WARNING ON)
set(ENABLE_MODULES "plugin;shell;cli;m3shapes" CACHE STRING "Modules to build/install")
set(INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/ZShell" CACHE STRING "Library install dir")
@@ -61,10 +62,12 @@ set(CMAKE_INSTALL_MESSAGE NEVER)
add_compile_options(
-Wall -Wextra -Wpedantic -Wshadow -Wconversion
-Wold-style-cast -Wnull-dereference -Wdouble-promotion
-Wformat=2 -Wfloat-equal -Woverloaded-virtual
-Wnull-dereference -Wdouble-promotion
-Wformat=2 -Wfloat-equal
-Wsign-conversion -Wredundant-decls -Wswitch
-Wunreachable-code
$<$<COMPILE_LANGUAGE:CXX>:-Wold-style-cast>
$<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>
)
@@ -188,4 +191,11 @@ if("m3shapes" IN_LIST ENABLE_MODULES)
if(TARGET m3shapesplugin)
set_target_properties(m3shapesplugin PROPERTIES INSTALL_RPATH "$ORIGIN")
endif()
# Silence warnings that come from m3shapes files we do not manage.
foreach(m3shapes_target m3shapes_core m3shapes_morph m3shapes_shapes m3shapes m3shapesplugin)
if(TARGET ${m3shapes_target})
target_compile_options(${m3shapes_target} PRIVATE -Wno-shadow -Wno-float-equal)
endif()
endforeach()
endif()
+1 -1
View File
@@ -20,7 +20,7 @@ BusyIndicator {
property int animState
property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary
property real implicitSize: Tokens.font.size.normal * 3
property real implicitSize: Tokens.font.body.medium.pointSize * 3
property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress
property real strokeWidth: Tokens.padding.extraSmall * 0.8
+42
View File
@@ -0,0 +1,42 @@
import QtQuick
import Quickshell
import ZShell.Components
Item {
id: root
property real radius
property real topLeftRadius: radius
property real topRightRadius: radius
property real bottomRightRadius: radius
property real bottomLeftRadius: radius
property color color: "transparent"
default property alias contentData: contentItem.data
Item {
id: contentItem
anchors.fill: parent
}
ShaderEffectSource {
id: src
hideSource: true
sourceItem: contentItem
}
RoundedMask {
anchors.fill: parent
source: src
topLeftRadius: root.topLeftRadius
topRightRadius: root.topRightRadius
bottomRightRadius: root.bottomRightRadius
bottomLeftRadius: root.bottomLeftRadius
color: root.color
}
Behavior on color {
CAnim {}
}
}
+4 -1
View File
@@ -28,6 +28,9 @@ Flickable {
interval: 10
running: root.doneFakeFlick
onTriggered: root.doneFakeFlick = false
onTriggered: {
root.doneFakeFlick = false;
root.returnToBounds();
}
}
}
+17 -14
View File
@@ -10,24 +10,27 @@ ListView {
maximumFlickVelocity: 3000
rebound: Transition {
onRunningChanged: {
if (!running && !root.doneFakeFlick) {
root.doneFakeFlick = true;
root.flick(1, 1);
root.flick(-1, -1);
Qt.callLater(() => root.cancelFlick());
}
}
// onRunningChanged: {
// if (!running && !root.doneFakeFlick) {
// root.doneFakeFlick = true;
// root.flick(1, 1);
// root.flick(-1, -1);
// Qt.callLater(() => root.cancelFlick());
// }
// }
Anim {
properties: "x,y"
type: Anim.DefaultEffects
}
}
Timer {
interval: 10
running: root.doneFakeFlick
onTriggered: root.doneFakeFlick = false
}
// Timer {
// interval: 10
// running: root.doneFakeFlick
//
// onTriggered: {
// root.doneFakeFlick = false;
// }
// }
}
+1
View File
@@ -4,6 +4,7 @@ MouseArea {
property int scrollAccumulatedY: 0
function onWheel(event: WheelEvent): void {
event.accepted = false;
}
onWheel: event => {
+1 -2
View File
@@ -6,7 +6,6 @@ Rectangle {
color: "transparent"
Behavior on color {
CAnim {
}
CAnim {}
}
}
+68 -81
View File
@@ -1,33 +1,41 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Templates
import qs.Services
import ZShell.Config
import qs.Services
ScrollBar {
id: root
required property Flickable flickable
readonly property bool isHorizontal: flickable && flickable.ScrollBar.horizontal === root
readonly property real axisSize: isHorizontal ? flickable.width : flickable.height
readonly property real axisContentSize: isHorizontal ? flickable.contentWidth : flickable.contentHeight
readonly property real axisContentPos: isHorizontal ? (reversed ? flickable.contentX : (flickable.contentX - flickable.originX)) : (reversed ? flickable.contentY : (flickable.contentY - flickable.originY))
readonly property real axisLength: isHorizontal ? root.width : root.height
readonly property real effectiveSize: Math.max(nonAnimHeight, root.minimumSize)
readonly property real effectiveTravel: Math.max(0, 1 - root.effectiveSize)
required property Flickable flickable
readonly property real nonAnimHeight: flickable.height / flickable.contentHeight
readonly property real nonAnimY: flickable.contentY / flickable.contentHeight
readonly property real nonAnimHeight: root.axisSize / root.axisContentSize
readonly property real nonAnimY: root.axisContentPos / root.axisContentSize
readonly property real rawTravel: Math.max(0, 1 - root.nonAnimHeight)
readonly property bool reversed: flickable instanceof ListView && flickable.verticalLayoutDirection === ListView.BottomToTop
property bool shouldBeActive
readonly property bool reversed: isHorizontal ? (flickable instanceof ListView && flickable.layoutDirection === Qt.RightToLeft) : (flickable instanceof ListView && flickable.verticalLayoutDirection === ListView.BottomToTop)
readonly property real travelScale: root.rawTravel > 0 ? root.effectiveTravel / root.rawTravel : 0
enabled: !ShellState.forActive().isDrawing
implicitHeight: Tokens.padding.extraSmall * 2
implicitWidth: Tokens.padding.extraSmall * 2
property bool shouldBeActive
property bool inverted
contentItem: Item {
}
enabled: !ShellState.forActive().isDrawing
implicitWidth: size === 1 ? 0 : isHorizontal ? 0 : Tokens.padding.extraSmall * 2
implicitHeight: size === 1 ? 0 : isHorizontal ? Tokens.padding.extraSmall * 2 : 0
contentItem: Item {}
Behavior on position {
enabled: !fullMouse.pressed
Anim {
}
Anim {}
}
onHoveredChanged: {
@@ -48,54 +56,27 @@ ScrollBar {
target: root.flickable
}
CustomClippingRect {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
implicitWidth: handle.implicitWidth
radius: Tokens.rounding.full
Loader {
anchors.fill: parent
active: root.size < 1
sourceComponent: root.isHorizontal ? horizontalTrack : verticalTrack
}
CustomRect {
id: handle
Component {
id: verticalTrack
anchors.right: parent.right
color: Colors.palette.m3secondary
implicitHeight: root.height * root.effectiveSize
implicitWidth: fullMouse.pressed || fullMouse.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!root.enabled)
return 0;
if (root.size === 1)
return 0;
if (fullMouse.pressed)
return 1;
if (fullMouse.containsMouse)
return 0.8;
if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
y: root.reversed ? root.height * (1 + root.nonAnimY) * root.travelScale : root.height * root.nonAnimY * root.travelScale
VerticalScrollBarTrack {
scrollBar: root
mouseArea: fullMouse
}
}
Behavior on implicitWidth {
Anim {
}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
Component {
id: horizontalTrack
MouseArea {
id: mouse
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
HorizontalScrollBarTrack {
scrollBar: root
mouseArea: fullMouse
}
}
@@ -112,25 +93,40 @@ ScrollBar {
property real pressOffset: 0
function contentYFromThumbTop(thumbTop) {
var visualPos = root.effectiveTravel > 0 ? thumbTop / root.travelScale : 0;
return root.reversed ? (visualPos - 1) * root.flickable.contentHeight : visualPos * root.flickable.contentHeight;
function contentPosFromThumbStart(thumbStart) {
var visualPos = root.effectiveTravel > 0 ? thumbStart / root.travelScale : 0;
return root.reversed ? (visualPos - 1) * root.axisContentSize : visualPos * root.axisContentSize;
}
function updateFromEvent(event) {
var posInTrack = event.y / root.height;
var thumbTop = posInTrack - pressOffset;
thumbTop = Math.max(0, Math.min(root.effectiveTravel, thumbTop));
var eventPos = root.isHorizontal ? event.x : event.y;
var posInTrack = eventPos / root.axisLength;
var thumbStart = posInTrack - pressOffset;
thumbStart = Math.max(0, Math.min(root.effectiveTravel, thumbStart));
root.flickable.contentY = contentYFromThumbTop(thumbTop);
var newPos = contentPosFromThumbStart(thumbStart);
if (root.isHorizontal)
root.flickable.contentX = newPos + (root.flickable.originX + (root.reversed ? root.flickable.contentWidth : 0));
else
root.flickable.contentY = newPos + (root.flickable.originY + (root.reversed ? root.flickable.contentHeight : 0));
}
function visualThumbTop() {
function visualThumbStart() {
const visualPos = root.reversed ? (1 + root.nonAnimY) : root.nonAnimY;
return visualPos * root.travelScale;
}
function onWheel(event: WheelEvent): void {
if (root.horizontal) {
event.accepted = false;
return;
}
var delta = event.angleDelta.y > 0 ? -0.1 : 0.1;
var newPos = Math.max(0, Math.min(1 - root.size, root.position + (root.inverted ? -delta : delta)));
root.position = newPos;
}
anchors.fill: parent
cursorShape: undefined
hoverEnabled: true
@@ -141,24 +137,15 @@ ScrollBar {
updateFromEvent(event);
}
onPressed: event => {
var currentTop = visualThumbTop();
var currentBottom = currentTop + root.effectiveSize;
var clickPos = event.y / root.height;
var currentStart = visualThumbStart();
var currentEnd = currentStart + root.effectiveSize;
var eventPos = root.isHorizontal ? event.x : event.y;
var clickPos = eventPos / root.axisLength;
var clickedInsideThumb = clickPos >= currentTop && clickPos <= currentBottom;
if (clickedInsideThumb) {
pressOffset = clickPos - currentTop;
} else {
pressOffset = root.effectiveSize / 2;
}
var clickedInsideThumb = clickPos >= currentStart && clickPos <= currentEnd;
pressOffset = clickedInsideThumb ? (clickPos - currentStart) : root.effectiveSize / 2;
updateFromEvent(event);
}
onWheel: event => {
var delta = event.angleDelta.y > 0 ? -0.1 : 0.1;
var newPos = Math.max(0, Math.min(1 - root.size, root.position + delta));
root.position = newPos;
}
}
}
+42
View File
@@ -0,0 +1,42 @@
import QtQuick
import Quickshell
ShaderEffect {
id: root
property Item target: parent
property real glowSize: 40
property real innerGlow: 0
property real lineWidth: 2
property real intensity: 1.0
property real speed: 1.0
property int period: 6000
property bool running: true
property color color1: "#4285F4"
property color color2: "#9B72CB"
property color color3: "#D96570"
property color color4: "#F2A54A"
readonly property size itemSize: Qt.size(width, height)
readonly property size rectSize: target ? Qt.size(target.width, target.height) : Qt.size(0, 0)
readonly property real radius: (target as Rectangle).radius ?? 0
property real phase: 0
anchors.fill: target
anchors.margins: -glowSize
z: -1
enabled: false
fragmentShader: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/shaders/geminiglow.frag.qsb`)
FrameAnimation {
running: root.running && root.visible
onTriggered: {
const dt = frameTime * 1000;
root.phase = (root.phase + dt * root.speed / root.period) % 1.0;
}
}
}
+58
View File
@@ -0,0 +1,58 @@
import QtQuick
import ZShell.Config
import qs.Services
Item {
id: track
required property var scrollBar
required property var mouseArea
ClipRect {
radius: Tokens.rounding.full
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
implicitHeight: handle.implicitHeight
CustomRect {
id: handle
anchors.bottom: parent.bottom
color: Colors.palette.m3secondary
implicitWidth: track.scrollBar.width * track.scrollBar.effectiveSize
implicitHeight: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!track.scrollBar.enabled)
return 0;
if (track.scrollBar.size === 1)
return 0;
if (track.mouseArea.pressed)
return 1;
if (track.mouseArea.containsMouse)
return 0.8;
if (track.scrollBar.policy === CustomScrollBar.AlwaysOn || track.scrollBar.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
x: track.scrollBar.reversed ? track.scrollBar.width * (1 + track.scrollBar.nonAnimY) * track.scrollBar.travelScale : track.scrollBar.width * track.scrollBar.nonAnimY * track.scrollBar.travelScale
Behavior on implicitHeight {
Anim {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
}
+3 -5
View File
@@ -34,7 +34,7 @@ MouseArea {
cursorShape: expanded ? Qt.ArrowCursor : undefined
enabled: expanded
hoverEnabled: expanded
layer.enabled: opacity < 1
layer.enabled: false
opacity: expanded ? 1 : 0
parent: {
const win = QsWindow.window;
@@ -101,8 +101,7 @@ MouseArea {
yScale: root.expanded ? 1 : 0.1
Behavior on yScale {
Anim {
}
Anim {}
}
}
@@ -137,8 +136,7 @@ MouseArea {
radius: menu.radius - column.anchors.margins
Behavior on radius {
Anim {
}
Anim {}
}
StateLayer {
+2 -2
View File
@@ -19,11 +19,11 @@ PersistentProperties {
property bool sidebar
// Dashboard state
property int dashboardTab
property int dashboardTab: 0
property date dashboardDate: new Date()
// Sidebar state
property int sidebarTab
property int sidebarTab: 0
property list<string> expandedNotifs: []
// Utilities state
+86
View File
@@ -0,0 +1,86 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
TextArea {
id: root
color: Colors.palette.m3onSurface
cursorVisible: !readOnly
font: Tokens.font.body.small
implicitHeight: contentHeight + topPadding + bottomPadding
implicitWidth: contentWidth + leftPadding + rightPadding
placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
renderType: TextArea.NativeRendering
selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter
Behavior on color {
CAnim {}
}
Behavior on selectionColor {
CAnim {}
}
cursorDelegate: Item {}
CustomRect {
id: cursor
property bool disableBlink
color: Colors.palette.m3primary
implicitHeight: root.cursorRectangle.height
implicitWidth: 1.5
radius: Tokens.rounding.largeIncreased
x: root.cursorRectangle.x
y: root.cursorRectangle.y
Behavior on x {
Anim {
duration: Tokens.anim.durations.expressiveFastEffects
easing.bezierCurve: [0.2, 1, 0.21, 1, 1, 1]
}
}
Behavior on opacity {
Anim {
type: Anim.StandardSmall
}
}
Connections {
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
}
}
target: root
}
Timer {
id: enableBlink
interval: 500
onTriggered: cursor.disableBlink = false
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
}
}
}
+96
View File
@@ -0,0 +1,96 @@
import QtQuick
import QtQuick.Templates
import ZShell.Config
import qs.Services
TextEdit {
id: root
property bool animateCursor: true
property alias cursor: cursor
color: Colors.palette.m3onSurface
cursorVisible: !readOnly
font: Tokens.font.body.small
renderType: TextField.NativeRendering
selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter
Behavior on color {
CAnim {}
}
cursorDelegate: Item {}
Behavior on selectionColor {
CAnim {}
}
CustomRect {
id: cursor
property bool disableBlink
color: Colors.palette.m3primary
implicitHeight: root.cursorRectangle.height
implicitWidth: 1.5
radius: Tokens.rounding.largeIncreased
x: root.cursorRectangle.x
y: root.cursorRectangle.y
Behavior on opacity {
Anim {
type: Anim.StandardSmall
}
}
Behavior on x {
enabled: root.animateCursor
Anim {
duration: Tokens.anim.durations.expressiveFastEffects
easing.bezierCurve: [0.2, 1, 0.21, 1, 1, 1]
}
}
Behavior on y {
enabled: root.animateCursor
Anim {
duration: Tokens.anim.durations.expressiveFastEffects
easing.bezierCurve: [0.2, 1, 0.21, 1, 1, 1]
}
}
Connections {
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
}
}
target: root
}
Timer {
id: enableBlink
interval: 500
onTriggered: cursor.disableBlink = false
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ TextField {
implicitHeight: contentHeight + topPadding + bottomPadding
implicitWidth: contentWidth + leftPadding + rightPadding
placeholderTextColor: Colors.palette.m3onSurfaceVariant // No anim cause placeholder is custom
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
renderType: TextField.QtRendering
selectedTextColor: color
selectionColor: Qt.alpha(Colors.palette.m3primary, 0.4)
verticalAlignment: TextInput.AlignVCenter
+3
View File
@@ -1,6 +1,7 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Effects
CustomFlickable {
@@ -9,6 +10,7 @@ CustomFlickable {
property real bottomFadeOpacity: fadeShouldBeActive(false) ? 0 : 1
property real fadeAmount: 0.1
property real topFadeOpacity: fadeShouldBeActive(true) ? 0 : 1
readonly property real dpr: Math.ceil((QsWindow.window as QsWindow)?.devicePixelRatio ?? 1)
function fadeShouldBeActive(isStart: bool): bool {
// When content is smaller than flickable size, hide fade when rebound starts
@@ -28,6 +30,7 @@ CustomFlickable {
type: Anim.SlowEffects
}
}
layer.textureSize: Qt.size(width * dpr, height * dpr)
layer.effect: Mask {
maskSource: mask
+7 -3
View File
@@ -7,7 +7,9 @@ CustomListView {
id: root
property real endFadeOpacity: fadeShouldBeActive(false) ? 0 : 1
property bool fadeEnabled: true
property real fadeAmount: 0.1
property real fadeThreshold: 0.0
readonly property bool horizontal: orientation === ListView.Horizontal
property real startFadeOpacity: fadeShouldBeActive(true) ? 0 : 1
@@ -16,7 +18,9 @@ CustomListView {
}
function fadeShouldBeActive(isStart: bool): bool {
// When content is smaller than flickable size, hide fade when rebound starts.
if (!fadeEnabled)
return false;
if (contentSize() + marginStart() + marginEnd() < viewportSize() && rebound.running && ((isStart ? overshootStart() > 0 : overshootStart() < 0))) {
return false;
}
@@ -28,11 +32,11 @@ CustomListView {
}
function marginEnd(): real {
return horizontal ? rightMargin : bottomMargin;
return horizontal ? rightMargin - fadeThreshold : bottomMargin - fadeThreshold;
}
function marginStart(): real {
return horizontal ? leftMargin : topMargin;
return horizontal ? leftMargin - fadeThreshold : topMargin - fadeThreshold;
}
function overshootStart(): real {
+58
View File
@@ -0,0 +1,58 @@
import QtQuick
import ZShell.Config
import qs.Services
Item {
id: track
required property var scrollBar
required property var mouseArea
ClipRect {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
radius: Tokens.rounding.full
implicitWidth: handle.implicitWidth
CustomRect {
id: handle
anchors.right: parent.right
color: Colors.palette.m3secondary
implicitHeight: track.scrollBar.height * track.scrollBar.effectiveSize
implicitWidth: track.mouseArea.pressed || track.mouseArea.containsMouse ? Tokens.padding.extraSmall * 2 : Tokens.padding.extraSmall
opacity: {
if (!track.scrollBar.enabled)
return 0;
if (track.scrollBar.size === 1)
return 0;
if (track.mouseArea.pressed)
return 1;
if (track.mouseArea.containsMouse)
return 0.8;
if (track.scrollBar.policy === CustomScrollBar.AlwaysOn || track.scrollBar.shouldBeActive)
return 0.6;
return 0;
}
radius: Tokens.rounding.full
y: track.scrollBar.reversed ? track.scrollBar.height * (1 + track.scrollBar.nonAnimY) * track.scrollBar.travelScale : track.scrollBar.height * track.scrollBar.nonAnimY * track.scrollBar.travelScale
Behavior on implicitWidth {
Anim {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
MouseArea {
acceptedButtons: Qt.NoButton
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
}
}
}
}
+1 -9
View File
@@ -43,13 +43,6 @@ Scope {
hasBar: root.geometry.barOnBottom
}
Timer {
interval: 5000
running: true
onTriggered: console.log("top height:", top.exclusiveZone, "left width:", left.exclusiveZone, "right width:", right.exclusiveZone, "bottom height:", bottom.exclusiveZone)
}
component ExclusionZone: CustomWindow {
property bool hasBar
@@ -59,7 +52,6 @@ Scope {
name: "Bar-Exclusion"
screen: root.screen
mask: Region {
}
mask: Region {}
}
}
+9 -8
View File
@@ -3,8 +3,8 @@ import QtQuick
import qs.Components
import qs.Modules.Bar.Popouts as Popouts
import qs.Modules.Notifications as Notifications
import qs.Modules.Notifications.Sidebar as Sidebar
import qs.Modules.Notifications.Sidebar.Utils as Utils
import qs.Modules.Sidebar as Sidebar
import qs.Modules.Sidebar.Utils as Utils
import qs.Modules.Dashboard as Dashboard
import qs.Modules.Osd as Osd
import qs.Components.Toast as Toasts
@@ -19,15 +19,11 @@ import ZShell.Config
Item {
id: root
required property Item bar
required property real borderThickness
readonly property alias clipboard: clipboard
readonly property alias dashboard: dashboard
readonly property alias dashboardWrapper: dashboardWrapper
readonly property alias dock: dock
readonly property alias drawing: drawing
required property var drawingItem
required property EdgeGeometry geometry
readonly property alias launcher: launcher
readonly property alias notifications: notifications
readonly property alias osd: osd
@@ -36,14 +32,19 @@ Item {
readonly property alias popoutsWrapper: popouts
readonly property alias resources: resources
readonly property alias resourcesWrapper: resourcesWrapper
required property ShellScreen screen
required property ScreenState screenState
readonly property alias settings: settings
readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar
readonly property alias toasts: toasts
readonly property alias utilities: utilities
required property Item bar
required property real borderThickness
required property var drawingItem
required property EdgeGeometry geometry
required property ShellScreen screen
required property ScreenState screenState
anchors.bottomMargin: geometry.insetBottom(borderThickness)
anchors.fill: parent
anchors.leftMargin: geometry.insetLeft(borderThickness)
+3 -6
View File
@@ -63,12 +63,10 @@ CustomWindow {
name: "Bar"
Behavior on fsTransitionProg {
Anim {
}
Anim {}
}
Behavior on surfaceColor {
CAnim {
}
CAnim {}
}
contentItem.Keys.onEscapePressed: {
@@ -288,8 +286,7 @@ CustomWindow {
y: panels.popoutsWrapper.y + panels.popouts.y + geometry.insetTop(root.borderThickness) - (geometry.barOnTop ? panels.popouts.height * extraExtent : 0)
Behavior on extraExtent {
Anim {
}
Anim {}
}
}
+12 -28
View File
@@ -27,27 +27,21 @@ ColumnLayout {
CustomText {
Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary
font.bold: true
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build()
text: Time.hourStr
}
CustomText {
Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3primary
font.bold: true
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build()
text: ":"
}
CustomText {
Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary
font.bold: true
font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
font: Tokens.font.headline.builders.large.scale(3 * root.centerScale).weight(Font.Bold).width(30).build()
text: Time.minuteStr
}
}
@@ -56,13 +50,11 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: -Tokens.padding.large * 2
color: Colors.palette.m3tertiary
font.bold: true
font.family: Config.appearance.font.family.mono
font.pointSize: Math.floor(Tokens.font.size.extraLarge * root.centerScale)
font: Tokens.font.title.builders.medium.weight(Font.DemiBold).build()
text: Time.format("dddd, d MMMM yyyy")
}
CustomClippingRect {
ClipRect {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Tokens.spacing.largeIncreased * 2
color: Colors.tPalette.m3surfaceContainer
@@ -73,7 +65,7 @@ ColumnLayout {
MaterialIcon {
anchors.centerIn: parent
color: Colors.palette.m3onSurfaceVariant
font.pointSize: Math.floor(root.centerWidth / 4)
fontStyle: Tokens.font.icon.size(root.centerWidth / 4).build()
text: "person"
}
@@ -88,9 +80,7 @@ ColumnLayout {
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3onSurfaceVariant
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
font.weight: 600
font: Tokens.font.body.large
text: root.greeter.username
visible: text.length > 0
}
@@ -155,8 +145,7 @@ ColumnLayout {
}
Behavior on opacity {
Anim {
}
Anim {}
}
}
@@ -191,7 +180,6 @@ ColumnLayout {
anchors.centerIn: parent
color: root.greeter.buffer && !root.greeter.launching ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
font.weight: 500
text: root.greeter.launching ? "hourglass_top" : "arrow_forward"
}
}
@@ -204,8 +192,7 @@ ColumnLayout {
implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
Behavior on implicitHeight {
Anim {
}
Anim {}
}
CustomText {
@@ -245,12 +232,10 @@ ColumnLayout {
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
Behavior on opacity {
Anim {
}
Anim {}
}
Behavior on scale {
Anim {
}
Anim {}
}
onMsgChanged: {
@@ -292,8 +277,7 @@ ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
color: isError ? Colors.palette.m3error : Colors.palette.m3primary
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.small
font: Tokens.font.mono.medium
horizontalAlignment: Qt.AlignHCenter
opacity: 0
scale: 0.7
+1 -1
View File
@@ -20,7 +20,7 @@ BusyIndicator {
property int animState
property color bgColor: Colors.palette.m3secondaryContainer
property color fgColor: Colors.palette.m3primary
property real implicitSize: Tokens.font.size.normal * 3
property real implicitSize: Tokens.font.body.medium.pointSize * 3
property real internalStrokeWidth: strokeWidth
readonly property alias progress: manager.progress
property real strokeWidth: Tokens.padding.extraSmall * 0.8
+41
View File
@@ -0,0 +1,41 @@
import QtQuick
import ZShell.Components
Item {
id: root
property real radius
property real topLeftRadius: radius
property real topRightRadius: radius
property real bottomRightRadius: radius
property real bottomLeftRadius: radius
property color color: "transparent"
default property alias contentData: contentItem.data
Item {
id: contentItem
anchors.fill: parent
}
ShaderEffectSource {
id: source
hideSource: true
sourceItem: contentItem
}
RoundedMask {
anchors.fill: parent
source: source
topLeftRadius: root.topLeftRadius
topRightRadius: root.topRightRadius
bottomRightRadius: root.bottomRightRadius
bottomLeftRadius: root.bottomLeftRadius
color: root.color
}
Behavior on color {
CAnim {}
}
}
+23
View File
@@ -1,13 +1,36 @@
import QtQuick
import qs.Services
Flickable {
id: root
property bool doneFakeFlick
interactive: !ShellState.forActive().isDrawing
maximumFlickVelocity: 3000
rebound: Transition {
onRunningChanged: {
if (!running && !root.doneFakeFlick) {
root.doneFakeFlick = true;
root.flick(1, 1);
root.flick(-1, -1);
Qt.callLater(() => root.cancelFlick());
}
}
Anim {
properties: "x,y"
}
}
Timer {
interval: 10
running: root.doneFakeFlick
onTriggered: {
root.doneFakeFlick = false;
root.returnToBounds();
}
}
}
+23
View File
@@ -1,13 +1,36 @@
import QtQuick
import qs.Services
ListView {
id: root
property bool doneFakeFlick
interactive: !ShellState.forActive()?.isDrawing
maximumFlickVelocity: 3000
rebound: Transition {
// onRunningChanged: {
// if (!running && !root.doneFakeFlick) {
// root.doneFakeFlick = true;
// root.flick(1, 1);
// root.flick(-1, -1);
// Qt.callLater(() => root.cancelFlick());
// }
// }
Anim {
properties: "x,y"
type: Anim.DefaultEffects
}
}
// Timer {
// interval: 10
// running: root.doneFakeFlick
//
// onTriggered: {
// root.doneFakeFlick = false;
// }
// }
}
+1
View File
@@ -4,6 +4,7 @@ MouseArea {
property int scrollAccumulatedY: 0
function onWheel(event: WheelEvent): void {
event.accepted = false;
}
onWheel: event => {
+1 -2
View File
@@ -6,7 +6,6 @@ Rectangle {
color: "transparent"
Behavior on color {
CAnim {
}
CAnim {}
}
}
+9 -7
View File
@@ -14,10 +14,9 @@ Text {
property real animateTo: 1
color: Colors.palette.m3onSurface
font.family: Config.appearance.font.family.sans
font.pointSize: Tokens.font.size.normal
font: Tokens.font.body.small
linkColor: Colors.palette.m3onPrimaryFixedVariant
renderType: Text.NativeRendering
renderType: Text.QtRendering
textFormat: Text.PlainText
Behavior on color {
@@ -28,22 +27,25 @@ Text {
enabled: root.animate
SequentialAnimation {
Anim {
TAnim {
target: root
to: root.animateFrom
type: Anim.FastEffects
}
PropertyAction {
}
Anim {
TAnim {
target: root
to: root.animateTo
type: Anim.DefaultEffects
}
}
}
component Anim: NumberAnimation {
component TAnim: Anim {
duration: root.animateDuration / 2
easing.type: Easing.BezierSpline
properties: root.animateProp.split(",").length > 1 ? root.animateProp : ""
property: root.animateProp.split(",").length === 1 ? root.animateProp : ""
target: root
+275 -46
View File
@@ -1,76 +1,305 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Shapes
import ZShell.Config
import qs.Services
TextField {
TextFieldBase {
id: root
background: null
color: Colors.palette.m3onSurface
cursorVisible: !readOnly
font.family: Config.appearance.font.family.sans
font.pointSize: Tokens.font.size.smaller
placeholderTextColor: Colors.palette.m3outline
renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering
enum TextFieldType {
Outlined,
Filled
}
Behavior on color {
CAnim {
readonly property int clampedRadius: Math.min(horizontalPadding, Math.min(width, height) / 2, radius)
readonly property string effectiveSupportingText: isError && errorText ? errorText : supportingText
readonly property int filledOffset: type === CustomTextField.Filled ? Tokens.spacing.small : 0
readonly property int leadingOffset: leadingIcon ? leadingIconLoader.width + leadingIconLoader.anchors.leftMargin : 0
readonly property real smallFontScale: smallFontSize / font.pointSize
readonly property int supportingTextOffset: effectiveSupportingText ? supportingTextLoader.height + Tokens.spacing.extraSmall : 0
readonly property int trailingOffset: trailingIcon ? trailingIconLoader.width + trailingIconLoader.anchors.rightMargin : 0
readonly property bool valid: !validate || (!text && emptyIsValid) || (validate instanceof RegExp ? validate.test(text) : !!validate(text))
property int horizontalPadding: Tokens.padding.large
property int verticalPadding: Tokens.padding.large
property int radius: Tokens.rounding.small
property string supportingText
property string trailingIcon
property int smallFontSize: Tokens.font.label.small.pointSize
property int type: CustomTextField.Outlined
property var validate // Regex or function
property bool emptyIsValid: true
property string errorText
property bool isError
property string leadingIcon
bottomPadding: verticalPadding + supportingTextOffset - filledOffset
leftPadding: horizontalPadding + leadingOffset
rightPadding: horizontalPadding + trailingOffset
topPadding: verticalPadding + filledOffset
background: Loader {
anchors.bottomMargin: root.supportingTextOffset
anchors.fill: parent
sourceComponent: root.type === CustomTextField.Filled ? filledComp : outlineComp
StateLayer {
id: stateLayer
cursorShape: Qt.IBeamCursor
enabled: !root.activeFocus
manualPressOverride: tapHandler.pressed
radius: root.type === CustomTextField.Outlined ? root.clampedRadius : 0
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
onClicked: root.focus = true
}
}
cursorDelegate: CustomRect {
id: cursor
property bool disableBlink
onEditingFinished: {
if (!valid)
isError = true;
}
onPressed: {
if (stateLayer.enabled)
stateLayer.press(stateLayer.mouseX, stateLayer.mouseY);
}
onTextEdited: {
if (isError)
isError = false;
}
color: Colors.palette.m3primary
implicitWidth: 2
radius: Tokens.rounding.medium
Item {
id: contentWrapper
Behavior on opacity {
Anim {
duration: Tokens.anim.durations.small
}
}
anchors.bottomMargin: root.supportingTextOffset
anchors.fill: parent
Connections {
function onCursorPositionChanged(): void {
if (root.activeFocus && root.cursorVisible) {
cursor.opacity = 1;
cursor.disableBlink = true;
enableBlink.restart();
CustomText {
id: placeholder
anchors.left: parent.left
anchors.leftMargin: root.leftPadding
anchors.topMargin: Tokens.padding.extraSmall
anchors.verticalCenter: parent.verticalCenter
color: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : root.text ? Colors.palette.m3outline : root.placeholderTextColor)
font.family: root.font.family
font.pointSize: root.font.pointSize
font.variableAxes: root.font.variableAxes
font.weight: root.font.weight
renderType: Text.QtRendering
text: root.placeholderText
states: [
State {
name: "smallOutlined"
when: root.type === CustomTextField.Outlined && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding
placeholder.scale: root.smallFontScale
}
AnchorChanges {
anchors.verticalCenter: contentWrapper.top
target: placeholder
}
},
State {
name: "smallFilled"
when: root.type === CustomTextField.Filled && (root.activeFocus || root.text)
PropertyChanges {
placeholder.anchors.leftMargin: -(1 - root.smallFontScale) * placeholder.width / 2 + root.horizontalPadding + root.leadingOffset
placeholder.scale: root.smallFontScale
}
AnchorChanges {
anchors.top: contentWrapper.top
anchors.verticalCenter: undefined
target: placeholder
}
}
]
transitions: Transition {
Anim {
properties: "scale,leftMargin"
type: Anim.DefaultEffects
}
AnchorAnim {
duration: Tokens.anim.durations.expressiveDefaultEffects
easing: Tokens.anim.expressiveDefaultEffects
}
}
target: root
}
Timer {
id: enableBlink
Loader {
id: leadingIconLoader
interval: 100
active: root.leadingIcon
anchors.left: parent.left
anchors.leftMargin: Tokens.padding.small
anchors.verticalCenter: parent.verticalCenter
onTriggered: cursor.disableBlink = false
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
text: root.leadingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
}
}
Timer {
interval: 500
repeat: true
running: root.activeFocus && root.cursorVisible && !cursor.disableBlink
triggeredOnStart: true
Loader {
id: trailingIconLoader
onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1
}
active: root.trailingIcon
anchors.right: parent.right
anchors.rightMargin: Tokens.padding.small
anchors.verticalCenter: parent.verticalCenter
Binding {
cursor.opacity: 0
when: !root.activeFocus || !root.cursorVisible
sourceComponent: MaterialIcon {
color: Colors.palette.m3onSurfaceVariant
text: root.trailingIcon
fontStyle: Tokens.font.icon.builders.medium.scale(0.9).build()
}
}
}
Behavior on placeholderTextColor {
CAnim {
Loader {
id: supportingTextLoader
active: root.effectiveSupportingText
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: root.horizontalPadding
sourceComponent: CustomText {
color: root.isError ? Colors.palette.m3error : Colors.palette.m3onSurfaceVariant
font: Tokens.font.label.small
text: root.effectiveSupportingText
}
}
TapHandler {
id: tapHandler
}
Component {
id: outlineComp
Shape {
id: bg
asynchronous: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
id: path
readonly property real inset: strokeWidth / 2
readonly property real outlineGap: placeholder.width * root.smallFontScale + Tokens.spacing.small * 2
property real outlineGapScale: root.activeFocus || root.text ? 1 : 0
capStyle: ShapePath.RoundCap
fillColor: "transparent"
startX: path.inset + root.horizontalPadding - root.clampedRadius + path.outlineGap * (1 - path.outlineGapScale) / 2 + path.outlineGap * path.outlineGapScale
startY: path.inset
strokeColor: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : Colors.palette.m3outline)
strokeWidth: root.activeFocus ? 2 : 1
Behavior on outlineGapScale {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on strokeColor {
CAnim {}
}
Behavior on strokeWidth {
Anim {}
}
PathLine {
x: bg.width - path.inset - root.clampedRadius
y: path.inset
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - path.inset
y: path.inset + root.clampedRadius
}
PathLine {
x: bg.width - path.inset
y: bg.height - path.inset - root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: bg.width - path.inset - root.clampedRadius
y: bg.height - path.inset
}
PathLine {
x: path.inset + root.clampedRadius
y: bg.height - path.inset
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: path.inset
y: bg.height - path.inset - root.clampedRadius
}
PathLine {
x: path.inset
y: path.inset + root.clampedRadius
}
PathArc {
radiusX: root.clampedRadius
radiusY: root.clampedRadius
x: path.inset + root.clampedRadius
y: path.inset
}
PathLine {
x: path.inset + root.horizontalPadding - root.clampedRadius / 2 + path.outlineGap * (1 - path.outlineGapScale) / 2
y: path.inset
}
}
}
}
Component {
id: filledComp
CustomRect {
color: root.activeFocus ? Colors.tPalette.m3surfaceContainerHighest : Colors.tPalette.m3surfaceContainerHigh
topLeftRadius: root.clampedRadius
topRightRadius: root.clampedRadius
CustomRect {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: root.isError ? Colors.palette.m3error : (root.activeFocus ? Colors.palette.m3primary : Colors.palette.m3outline)
implicitHeight: root.activeFocus ? 2 : 1
Behavior on implicitHeight {
Anim {}
}
}
}
}
}
+3 -2
View File
@@ -1,15 +1,16 @@
import QtQuick
import QtQuick.Controls
import ZShell.Config
import qs.Services
TextInput {
renderType: Text.NativeRendering
selectedTextColor: Colors.palette.m3onSecondaryContainer
selectionColor: Colors.tPalette.m3onSecondaryContainer
selectionColor: Colors.tPalette.colSecondaryContainer
font {
family: Config.appearance?.font.family.sans ?? "sans-serif"
hintingPreference: Font.PreferFullHinting
pixelSize: Tokens?.font.size.normal ?? 15
pixelSize: Config.appearance?.font.size.normal ?? 15
}
}
+79
View File
@@ -0,0 +1,79 @@
import QtQuick
import Quickshell
import ZShell.Config
Image {
id: root
property int fadeInAnim: Anim.DefaultEffects
property int fadeInLargeAnim: Anim.StandardLarge
property int fadeOutAnim: Anim.FastEffects
property bool fadingOut
property bool hadPrevious
property bool preventInit
function maybeStartInAnim(): void {
if (!preventInit && !opacityInAnim.running && status === Image.Ready) {
opacityInAnim.type = hadPrevious ? fadeInAnim : fadeInLargeAnim;
opacityInAnim.start();
}
}
asynchronous: true
fillMode: Image.PreserveAspectCrop
opacity: 0
retainWhileLoading: true
sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr);
}
Anim on opacity {
id: opacityInAnim
running: false
to: 1
}
Behavior on source {
SequentialAnimation {
ScriptAction {
script: opacityInAnim.stop()
}
PropertyAction {
property: "fadingOut"
target: root
value: true
}
Anim {
property: "opacity"
target: root
to: 0
type: root.fadeOutAnim
}
PropertyAction {
property: "fadingOut"
target: root
value: false
}
PropertyAction {
property: "hadPrevious"
target: root
value: root.source
}
PropertyAction {
}
ScriptAction {
script: root.maybeStartInAnim()
}
}
}
onPreventInitChanged: maybeStartInAnim()
onStatusChanged: maybeStartInAnim()
}
+115
View File
@@ -0,0 +1,115 @@
import QtQuick
import Quickshell
import M3Shapes
import ZShell.Config
import qs.Services
MaterialShape {
id: root
property bool animated: true
property real cRotation
property bool containsIcon
property real dampingRatio: 0.6
property real lRotation
property int morphAnimRotation: 60
property real morphScale: 0.14
property alias rotateAnimDuration: rotateAnim.duration
property int shapeIndex
property list<int> shapes: {
if (containsIcon)
return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pentagon, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
}
readonly property real springDuration: {
const wn = Math.sqrt(stiffness);
const r = -dampingRatio * wn;
const c = 1 / Math.sqrt(1 - dampingRatio * dampingRatio);
return Math.log(visibilityThreshold / c) / r;
}
readonly property real springMaxVelocity: {
const wn = Math.sqrt(stiffness);
const factor = Math.exp(-z * Math.acos(z) / Math.sqrt(1 - z * z));
return wn * factor;
}
property bool springSettled: true
property real stiffness: 180
property real thisLRotation
property real visibilityThreshold: 0.075
function spring(t: real): var {
const wn = Math.sqrt(stiffness);
const za = dampingRatio * wn;
const wd = wn * Math.sqrt(1 - dampingRatio * dampingRatio);
const r = za / wd;
const pos = 1 - Math.exp(-za * t) * (Math.cos(wd * t) + r * Math.sin(wd * t));
const vel = Math.exp(-za * t) * (wn * wn / wd) * Math.sin(wd * t);
return [pos, vel];
}
color: Colors.palette.m3primary
implicitSize: 38
toShape: shapes[0]
RotationAnimation on cRotation {
id: rotateAnim
duration: 4666
easing.type: Easing.Linear
from: 0
loops: Animation.Infinite
running: root.animated
to: 360
}
Behavior on color {
CAnim {
}
}
ElapsedTimer {
id: timer
}
FrameAnimation {
running: root.animated && !root.springSettled
onTriggered: {
const t = timer.elapsed();
if (t >= root.springDuration) {
root.springSettled = true;
} else {
const [pos, vel] = root.spring(t);
root.morphProgress = Math.min(1, pos); // Overshooting the morph looks weird
root.thisLRotation = pos * root.morphAnimRotation;
root.scale = 1 + vel * root.morphScale / root.springMaxVelocity;
}
}
}
Timer {
interval: 650
repeat: true
running: root.animated
triggeredOnStart: true
onTriggered: {
root.beginBatchUpdate();
root.fromShape = root.toShape;
root.shapeIndex = (root.shapeIndex + 1) % root.shapes.length;
root.toShape = root.shapes[root.shapeIndex];
root.morphProgress = 0;
root.rotation = root.rotation;
root.lRotation = (root.lRotation + root.thisLRotation) % 360;
root.thisLRotation = 0;
root.rotation = Qt.binding(() => root.cRotation + root.lRotation + root.thisLRotation);
root.springSettled = false;
timer.restart();
root.endBatchUpdate();
}
}
}
+8 -10
View File
@@ -1,16 +1,14 @@
import QtQuick
import ZShell.Config
import qs.Services
CustomText {
property real fill
property int grade: Colors.light ? 0 : -25
id: root
font.family: "Material Symbols Rounded"
font.pointSize: Tokens.font.size.larger
font.variableAxes: ({
FILL: fill.toFixed(1),
GRAD: grade,
opsz: fontInfo.pixelSize,
wght: fontInfo.weight
})
property real fill: 0
property int grade: Colors.light ? 0 : -25
property font fontStyle: Tokens.font.icon.small
renderType: Text.NativeRendering
font: Tokens.font.icon.size(fontStyle.pointSize).weight(fontStyle.weight).vaxes(fontStyle.variableAxes).fill(fill.toFixed(1)).grade(grade).build()
}
+6 -8
View File
@@ -1,8 +1,6 @@
import QtQuick
import QtQuick.Shapes
import ZShell
import ZShell.Components
import qs.Helpers
import ZShell.Config
import qs.Services
@@ -13,7 +11,6 @@ MouseArea {
property alias bottomRightRadius: base.bottomRightRadius
property real circleRadius
property alias color: base.color
property bool disabled
readonly property real endRadius: {
const d1 = distSq(0, 0);
const d2 = distSq(width, 0);
@@ -22,6 +19,7 @@ MouseArea {
return (Math.sqrt(Math.max(d1, d2, d3, d4)) + (shapeMorph ? 24 : 0)) * 1.3;
}
property real endRadiusAtPress
property bool manualHoverOverride
property bool manualPressOverride
property real pressX: width / 2
property real pressY: height / 2
@@ -29,7 +27,7 @@ MouseArea {
readonly property alias rect: base
property bool shapeMorph
property bool showHoverBackground: true
property real stateOpacity: containsMouse ? 0.08 : 0
property real stateOpacity: containsMouse || manualHoverOverride ? 0.08 : 0
property alias topLeftRadius: base.topLeftRadius
property alias topRightRadius: base.topRightRadius
@@ -52,8 +50,8 @@ MouseArea {
}
anchors.fill: parent
cursorShape: disabled ? undefined : Qt.PointingHandCursor
enabled: !disabled
cursorShape: !enabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled
hoverEnabled: true
Behavior on stateOpacity {
@@ -100,13 +98,13 @@ MouseArea {
id: base
anchors.fill: parent
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
color: Colors.palette.m3onSurface
opacity: root.stateOpacity
// Pick up radius from parent if it has one (parent can be anything with radius props)
// qmllint disable missing-property
radius: root.parent?.radius ?? 0
bottomLeftRadius: root.parent?.bottomLeftRadius ?? radius ?? 0
bottomRightRadius: root.parent?.bottomRightRadius ?? radius ?? 0
topLeftRadius: root.parent?.topLeftRadius ?? radius ?? 0
topRightRadius: root.parent?.topRightRadius ?? radius ?? 0
// qmllint enable missing-property
+16 -39
View File
@@ -13,34 +13,16 @@ RowLayout {
spacing: Tokens.spacing.largeIncreased * 2
ColumnLayout {
CustomRect {
Layout.fillHeight: true
Layout.fillWidth: true
spacing: Tokens.spacing.medium
topLeftRadius: Tokens.rounding.largeIncreased
bottomLeftRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
CustomRect {
Layout.fillWidth: true
color: Colors.tPalette.m3surfaceContainer
implicitHeight: weather.implicitHeight
radius: Tokens.rounding.small
topLeftRadius: Tokens.rounding.largeIncreased
WeatherInfo {
id: weather
rootHeight: root.height
}
}
CustomClippingRect {
Layout.fillHeight: true
Layout.fillWidth: true
bottomLeftRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
UserDock {
greeter: root.greeter
}
UserDock {
greeter: root.greeter
}
}
@@ -49,21 +31,16 @@ RowLayout {
screenHeight: root.screenHeight
}
ColumnLayout {
CustomRect {
Layout.fillHeight: true
Layout.fillWidth: true
spacing: Tokens.spacing.medium
bottomRightRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
CustomRect {
Layout.fillHeight: true
Layout.fillWidth: true
bottomRightRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
SessionDock {
greeter: root.greeter
}
SessionDock {
greeter: root.greeter
}
}
}
+1 -2
View File
@@ -134,8 +134,7 @@ Scope {
id: lockIcon
anchors.centerIn: parent
font.bold: true
font.pointSize: Tokens.font.size.extraLarge * 4
fontStyle: Tokens.font.icon.builders.extraLarge.size(Tokens.font.icon.extraLarge.pointSize * 3).build()
text: "lock"
}
+8 -15
View File
@@ -39,8 +39,7 @@ Item {
anchors.centerIn: parent
animate: true
color: root.greeter.launching ? Colors.palette.m3secondary : Colors.palette.m3outline
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
font: Tokens.font.mono.medium
opacity: root.buffer ? 0 : 1
text: {
if (root.greeter.launching)
@@ -51,8 +50,7 @@ Item {
}
Behavior on opacity {
Anim {
}
Anim {}
}
}
@@ -64,15 +62,13 @@ Item {
anchors.verticalCenter: parent.verticalCenter
color: Colors.palette.m3onSurface
elide: Text.ElideLeft
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal
font: Tokens.font.mono.medium
horizontalAlignment: Qt.AlignHCenter
opacity: root.greeter.echoResponse && root.buffer ? 1 : 0
text: root.buffer
Behavior on opacity {
Anim {
}
Anim {}
}
}
@@ -89,7 +85,7 @@ Item {
anchors.centerIn: parent
anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0
implicitHeight: Tokens.font.size.normal
implicitHeight: Tokens.font.body.medium
implicitWidth: fullWidth
interactive: false
opacity: root.greeter.echoResponse ? 0 : 1
@@ -107,8 +103,7 @@ Item {
scale: 0
Behavior on opacity {
Anim {
}
Anim {}
}
Behavior on scale {
Anim {
@@ -156,15 +151,13 @@ Item {
Behavior on implicitWidth {
id: imWidthBehavior
Anim {
}
Anim {}
}
model: ScriptModel {
values: root.buffer.split("")
}
Behavior on opacity {
Anim {
}
Anim {}
}
}
}
+11 -17
View File
@@ -13,22 +13,19 @@ ColumnLayout {
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small
spacing: Tokens.spacing.large
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3outline
elide: Text.ElideRight
font.family: Config.appearance.font.family.mono
font.weight: 500
font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
text: root.greeter.sessions.length > 0 ? qsTr("%1 session%2").arg(root.greeter.sessions.length).arg(root.greeter.sessions.length === 1 ? "" : "s") : qsTr("Sessions")
}
CustomClippingRect {
Item {
Layout.fillHeight: true
Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader {
active: opacity > 0
@@ -46,16 +43,14 @@ ColumnLayout {
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
font.pointSize: Tokens.font.size.extraLarge * 2
fontStyle: Tokens.font.icon.large
text: "desktop_windows"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.large
font.weight: 500
font: Tokens.font.headline.builders.small.width(90).build()
text: qsTr("No Sessions Found")
}
}
@@ -68,6 +63,7 @@ ColumnLayout {
clip: true
currentIndex: root.greeter.sessionIndex
highlightFollowsCurrentItem: false
boundsBehavior: ListView.StopAtBounds
model: root.greeter.sessions
spacing: Tokens.spacing.small
@@ -80,7 +76,7 @@ ColumnLayout {
anchors.left: parent?.left
anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.medium - Tokens.padding.small
radius: Tokens.rounding.large
StateLayer {
onClicked: {
@@ -97,7 +93,7 @@ ColumnLayout {
MaterialIcon {
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant
font.pointSize: Tokens.font.size.extraLarge
fontStyle: Tokens.font.icon.extraLarge
text: modelData.kind === "x11" ? "tv" : "desktop_windows"
}
@@ -109,8 +105,7 @@ ColumnLayout {
Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal
font.weight: 600
font: Tokens.font.body.builders.medium.weight(Font.Bold).build()
text: modelData.name
}
@@ -118,8 +113,7 @@ ColumnLayout {
Layout.fillWidth: true
color: session.index === sessions.currentIndex ? Colors.palette.m3onPrimaryFixedVariant : Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.small
font: Tokens.font.mono.small
text: modelData.kind
}
}
@@ -129,7 +123,7 @@ ColumnLayout {
color: Colors.palette.m3primary
implicitHeight: sessions.currentItem?.implicitHeight ?? 0
implicitWidth: sessions.width
radius: Tokens.rounding.medium - Tokens.padding.small
radius: Tokens.rounding.large
y: sessions.currentItem?.y ?? 0
Behavior on y {
+11 -16
View File
@@ -14,22 +14,19 @@ ColumnLayout {
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small
spacing: Tokens.spacing.large
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3outline
elide: Text.ElideRight
font.family: Config.appearance.font.family.mono
font.weight: 500
font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
text: root.greeter.users.length > 0 ? qsTr("%1 user%2").arg(root.greeter.users.length).arg(root.greeter.users.length === 1 ? "" : "s") : qsTr("Users")
}
CustomClippingRect {
Item {
Layout.fillHeight: true
Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader {
active: opacity > 0
@@ -48,16 +45,14 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
fill: 1
font.pointSize: Tokens.font.size.extraLarge * 2
fontStyle: Tokens.font.icon.large
text: "account_circle"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.large
font.weight: 500
font: Tokens.font.headline.builders.small.width(90).build()
text: qsTr("No Users Found")
}
}
@@ -70,6 +65,7 @@ ColumnLayout {
clip: true
currentIndex: root.greeter.selectedUser ? root.greeter.users.indexOf(root.greeter.selectedUser) : -1
highlightFollowsCurrentItem: false
boundsBehavior: ListView.StopAtBounds
model: root.greeter.users
spacing: Tokens.spacing.small
@@ -82,7 +78,7 @@ ColumnLayout {
anchors.left: parent?.left
anchors.right: parent?.right
implicitHeight: row.implicitHeight + Tokens.padding.small * 2
radius: Tokens.rounding.medium - Tokens.padding.small
radius: Tokens.rounding.large
StateLayer {
onClicked: {
@@ -97,7 +93,7 @@ ColumnLayout {
anchors.margins: Tokens.padding.small
spacing: Tokens.spacing.medium
CustomClippingRect {
ClipRect {
Layout.preferredHeight: 50
Layout.preferredWidth: 50
radius: Tokens.rounding.full
@@ -106,7 +102,7 @@ ColumnLayout {
anchors.centerIn: parent
color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurfaceVariant
fill: 1
font.pointSize: Tokens.font.size.extraLarge
fontStyle: Tokens.font.icon.extraLarge
text: modelData.kind === "x11" ? "tv" : "account_circle"
}
@@ -124,8 +120,7 @@ ColumnLayout {
Layout.fillWidth: true
color: user.index === users.currentIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal
font.weight: 600
font: Tokens.font.body.builders.medium.weight(Font.Bold).build()
text: modelData.username
}
}
@@ -135,7 +130,7 @@ ColumnLayout {
color: Colors.palette.m3primary
implicitHeight: users.currentItem?.implicitHeight ?? 0
implicitWidth: users.width
radius: Tokens.rounding.medium - Tokens.padding.small
radius: Tokens.rounding.large
y: users.currentItem?.y ?? 0
Behavior on y {
-171
View File
@@ -1,171 +0,0 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import ZShell.Config
import qs.Components
import qs.Helpers
import qs.Services
ColumnLayout {
id: root
required property int rootHeight
anchors.left: parent.left
anchors.margins: Tokens.padding.large * 2
anchors.right: parent.right
spacing: Tokens.spacing.small
Loader {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: -Tokens.padding.large
Layout.topMargin: Tokens.padding.large * 2
active: root.rootHeight > 610
visible: active
sourceComponent: CustomText {
color: Colors.palette.m3primary
font.pointSize: Tokens.font.size.extraLarge
font.weight: 500
text: qsTr("Weather")
}
}
RowLayout {
Layout.fillWidth: true
spacing: Tokens.spacing.largeIncreased
MaterialIcon {
animate: true
color: Colors.palette.m3secondary
font.pointSize: Tokens.font.size.extraLarge * 2.5
text: Weather.icon
}
ColumnLayout {
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3secondary
elide: Text.ElideRight
font.pointSize: Tokens.font.size.large
font.weight: 500
text: Weather.description
}
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.pointSize: Tokens.font.size.normal
text: qsTr("Humidity: %1%").arg(Weather.humidity)
}
}
Loader {
Layout.rightMargin: Tokens.padding.small
active: root.width > 400
visible: active
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3primary
elide: Text.ElideLeft
font.pointSize: Tokens.font.size.extraLarge
font.weight: 500
horizontalAlignment: Text.AlignRight
text: Weather.temp
}
CustomText {
Layout.fillWidth: true
animate: true
color: Colors.palette.m3outline
elide: Text.ElideLeft
font.pointSize: Tokens.font.size.smaller
horizontalAlignment: Text.AlignRight
text: qsTr("Feels like: %1").arg(Weather.feelsLike)
}
}
}
}
Loader {
id: forecastLoader
Layout.bottomMargin: Tokens.padding.large * 2
Layout.fillWidth: true
Layout.topMargin: Tokens.spacing.small
active: root.rootHeight > 820
visible: active
sourceComponent: RowLayout {
spacing: Tokens.spacing.largeIncreased
Repeater {
model: {
const forecast = Weather.hourlyForecast;
const count = root.width < 320 ? 3 : root.width < 400 ? 4 : 5;
if (!forecast)
return Array.from({
length: count
}, () => null);
const start = Math.max(0, Weather.currentHourIndex);
return forecast.slice(start, start + count);
}
ColumnLayout {
id: forecastHour
required property var modelData
Layout.fillWidth: true
spacing: Tokens.spacing.small
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3outline
font.pointSize: Tokens.font.size.larger
horizontalAlignment: Text.AlignHCenter
text: {
const hour = forecastHour.modelData?.hour ?? 0;
return hour > 12 ? `${(hour - 12).toString().padStart(2, "0")} PM` : `${hour.toString().padStart(2, "0")} AM`;
}
}
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
font.pointSize: Tokens.font.size.extraLarge * 1.5
font.weight: 500
text: forecastHour.modelData?.icon ?? "cloud_alert"
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3secondary
font.pointSize: Tokens.font.size.larger
text: Config.services.useFahrenheit ? `${forecastHour.modelData?.tempF ?? 0}°F` : `${forecastHour.modelData?.tempC ?? 0}°C`
}
}
}
}
}
Timer {
interval: 900000 // 15 minutes
repeat: true
running: true
triggeredOnStart: true
onTriggered: Weather.reload()
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ Singleton {
function toggleNightLight(): void {
service.manualToggle = true;
service.toggle();
service.toggle(true);
}
NightlightManager {
+8 -13
View File
@@ -123,33 +123,29 @@ Item {
focus: true
opacity: 0
Behavior on opacity {
Anim {
duration: 300
}
}
Behavior on rsx {
enabled: !selectArea.pressed && root.mode === "select"
Anim {
}
Anim {}
}
Behavior on rsy {
enabled: !selectArea.pressed && root.mode === "select"
Anim {
}
Anim {}
}
Behavior on sh {
enabled: !selectArea.pressed && root.mode === "select"
Anim {
}
Anim {}
}
Behavior on sw {
enabled: !selectArea.pressed && root.mode === "select"
Anim {}
}
Behavior on opacity {
Anim {
duration: 300
}
}
@@ -393,8 +389,7 @@ Item {
y: selectionRect.y - root.realBorderWidth
Behavior on border.color {
Anim {
}
Anim {}
}
}
+105 -43
View File
@@ -5,7 +5,7 @@ import Quickshell
import Quickshell.Io
import QtQuick
import ZShell.Models
import ZShell.Internal
import ZShell.Wallpaper
import ZShell.Config
import qs.Components
import qs.Services
@@ -16,24 +16,48 @@ Searcher {
id: root
property string actualCurrent: WallpaperPath.currentWallpaperPath
property alias crops: adapter.monitorCrops
readonly property string current: showPreview ? previewPath : actualCurrent
property alias monitorCrops: monitorCrops
readonly property string currentThumbnail: thumbnailFor(current)
readonly property bool currentIsVideo: isVideoPath(current)
property bool pendingPreviewClear
property bool previewColorLock
property string previewPath
property bool recentlyChanged
property bool showPreview: false
property int cropVersion: 0
function getCategoryFor(w: FileSystemEntry): string {
function getCategoryFor(w: WallpaperEntry): string {
let category = w.parentDir.slice(Paths.wallsdir.length + 1);
if (category.includes("/"))
category = category.slice(0, category.indexOf("/"));
return category;
}
readonly property var videoExts: ["mp4", "mkv", "webm", "avi", "mov", "wmv", "flv", "m4v", "mpg", "mpeg", "3gp", "ogv", "ts", "vob"]
function isVideoPath(path: string): bool {
if (!path)
return false;
const dot = path.lastIndexOf(".");
if (dot < 0)
return false;
return videoExts.includes(path.slice(dot + 1).toLowerCase());
}
function thumbnailFor(path: string): string {
if (!path)
return "";
for (const entry of root.list) {
if (entry.path === path)
return entry.thumbnailPath || path;
}
return path;
}
function getCrop(screen: string): var {
return root.crops[screen];
return wallpapers.crop(root.current, screen);
}
function preview(path: string): void {
@@ -50,19 +74,18 @@ Searcher {
else if (zoom > 5.0)
zoom = 5.0;
root.crops[screen] = {
wallpapers.setCrop(root.current, screen, {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
zoom: zoom
};
});
}
function setWallpaper(path: string): void {
actualCurrent = path;
WallpaperPath.currentWallpaperPath = path;
Quickshell.screens.forEach(n => setCrop(n.name, Qt.rect(0, 0, 1, 1), 1.0));
Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]);
if (Config.colors.schemeGen)
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.colors.mode}`]);
@@ -96,38 +119,13 @@ Searcher {
target: "wallpaper"
}
FileView {
id: monitorCrops
path: `${Paths.state}/wallpaper-crops.json`
watchChanges: true
onAdapterUpdated: cropWriteDelay.restart()
onFileChanged: reload()
JsonAdapter {
id: adapter
property var monitorCrops: ({})
}
}
Timer {
id: cropWriteDelay
interval: 100
repeat: false
running: false
onTriggered: monitorCrops.writeAdapter()
}
FileSystemModel {
WallpaperDb {
id: wallpapers
filter: FileSystemModel.Images
dbPath: `${Paths.state}/wallpapers.sqlite`
cacheSize: wallpaper.largestBufferSize
requireHardwareCodec: true
path: Config.general.wallpaperPath
recursive: true
}
Process {
@@ -146,36 +144,100 @@ Searcher {
WallpaperService {
id: wallpaper
dbPath: wallpapers.dbPath
path: root.current
}
Connections {
target: wallpapers
function onCacheStored(path: string): void {
if (path === root.current)
wallpaper.invalidateCache();
}
function onCropsChanged(path: string): void {
if (path === root.current)
root.cropVersion++;
}
}
Variants {
id: screenVariants
model: Quickshell.screens
WallpaperObject {
readonly property var crops: root.getCrop(modelData.name)
id: obj
required property var modelData
cropHeight: crops?.height ?? 1.0
cropWidth: crops?.width ?? 1.0
cropX: crops?.x ?? 0.0
cropY: crops?.y ?? 0.0
// Crop animation only for edits, never for wallpaper swaps.
property bool animateCrop: false
screen: modelData.name
service: wallpaper
zoom: crops?.zoom ?? 1.0
// Apply the wallpaper's own crop. Driven off the service's pathChanged
// (which fires after applyPath has snapshotted the outgoing frame with
// the outgoing crop) and off crop edits, never as a reactive binding on
// the wallpaper path — so a swap applies the incoming crop atomically
// after the base is captured.
function seedCrop(): void {
// Load the current wallpaper's crop without pushing: the service
// already read it from the database. This only seeds the values so
// a later edit animates from the right starting point.
obj.syncCrop(root.getCrop(modelData.name));
}
// Apply an edited crop: assign via the setters so the Behaviors animate
// the numbers and push each intermediate value to the service.
function editCrop(): void {
const c = root.getCrop(modelData.name);
obj.cropX = c?.x ?? 0.0;
obj.cropY = c?.y ?? 0.0;
obj.cropWidth = c?.width ?? 1.0;
obj.cropHeight = c?.height ?? 1.0;
obj.zoom = c?.zoom ?? 1.0;
}
Component.onCompleted: seedCrop()
onScreenChanged: seedCrop()
Behavior on cropHeight {
enabled: obj.animateCrop
Anim {}
}
Behavior on cropWidth {
enabled: obj.animateCrop
Anim {}
}
Behavior on cropX {
enabled: obj.animateCrop
Anim {}
}
Behavior on cropY {
enabled: obj.animateCrop
Anim {}
}
}
}
Connections {
target: wallpaper
function onPathChanged(): void {
for (const obj of screenVariants.instances) {
obj.animateCrop = false;
obj.seedCrop();
}
}
}
Connections {
target: root
function onCropVersionChanged(): void {
for (const obj of screenVariants.instances) {
obj.animateCrop = true;
obj.editCrop();
}
}
}
}
+1 -2
View File
@@ -210,8 +210,7 @@ GridLayout {
roleValue: "network"
delegate: EntryWrapper {
NetworkWidget {
}
NetworkWidget {}
}
}
-49
View File
@@ -1,49 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Effects
import ZShell.Config
import qs.Components
import qs.Services
Item {
id: root
required property Item bar
required property ScreenState screenState
anchors.fill: parent
CustomRect {
anchors.fill: parent
anchors.margins: -1
color: Colors.palette.m3surface
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskInverted: true
maskSource: mask
maskSpreadAtMin: 1
maskThresholdMin: 0.5
}
}
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
anchors.margins: Config.bar.border + 1
anchors.topMargin: root.bar.implicitHeight + 1
radius: Config.bar.border > 0 ? Config.bar.rounding : 0
topLeftRadius: Config.bar.rounding
topRightRadius: Config.bar.rounding
}
}
}
+7 -9
View File
@@ -45,7 +45,9 @@ WidgetBase {
}
required property GridLayout loader
required property Wrapper popouts
readonly property real size: horizontal ? timeText.contentWidth + Tokens.padding.medium * 2 : verticalColumn.implicitHeight + Tokens.padding.medium * 2
readonly property int roundedWidth: Math.round(timeText.implicitWidth)
readonly property int evenWidth: roundedWidth % 2 === 0 ? roundedWidth : roundedWidth + 1
readonly property real size: horizontal ? evenWidth + Tokens.padding.medium * 2 : verticalColumn.implicitHeight + Tokens.padding.medium * 2
required property ScreenState screenState
implicitHeight: horizontal ? baseSize : size
@@ -63,8 +65,7 @@ WidgetBase {
visible: root.horizontal
Behavior on color {
CAnim {
}
CAnim {}
}
}
@@ -88,8 +89,7 @@ WidgetBase {
text: Qt.formatDateTime(Time.date, modelData)
Behavior on color {
CAnim {
}
CAnim {}
}
}
}
@@ -126,8 +126,7 @@ WidgetBase {
}
Behavior on color {
CAnim {
}
CAnim {}
}
}
}
@@ -140,8 +139,7 @@ WidgetBase {
visible: Config.services.useTwelveHourClock && root.formatParts.timeTokens.length > 0
Behavior on color {
CAnim {
}
CAnim {}
}
}
}
@@ -24,7 +24,7 @@ Row {
color: Battery.getColors(root.percentage, root.devState).bg
height: root.batHeight
radius: root.radius
width: root.batWidth
width: root.batWidth - 1
CustomText {
color: Battery.getColors(root.percentage, root.devState).fg
+1 -1
View File
@@ -13,7 +13,7 @@ WidgetBase {
readonly property int textWidth: Math.min(metrics.width, 200)
implicitHeight: horizontal ? baseSize : layout.implicitHeight + Tokens.padding.small * 2
implicitWidth: horizontal ? layout.implicitWidth + Tokens.padding.small * 2 : baseSize
implicitWidth: horizontal ? (layout.implicitWidth % 2 === 0 ? layout.implicitWidth : layout.implicitWidth + 1) + Tokens.padding.small * 2 : baseSize
radius: Tokens.rounding.full
Behavior on implicitHeight {
+8 -12
View File
@@ -12,7 +12,6 @@ GridLayout {
property real animatedPercentage: 0
readonly property real arcStartAngle: 0.75 * Math.PI
readonly property real arcSweep: 1.5 * Math.PI
readonly property real dpr: (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1
required property bool horizontal
property string icon
required property color iconColor
@@ -23,17 +22,12 @@ GridLayout {
property bool warning: percentage * 100 >= warningThreshold
property int warningThreshold: 80
function snap(v) {
return Math.round(v * dpr) / dpr;
}
columnSpacing: Tokens.spacing.medium
columns: horizontal ? -1 : 1
percentage: 0
Behavior on animatedPercentage {
Anim {
}
Anim {}
}
Component.onCompleted: animatedPercentage = percentage
@@ -59,7 +53,7 @@ GridLayout {
Layout.preferredHeight: root.horizontal ? icon.implicitHeight - Tokens.padding.extraSmall : 4
Layout.preferredWidth: root.horizontal ? 4 : icon.implicitWidth
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
radius: Math.min(width, height) / 2
radius: Tokens.rounding.full
Item {
id: clipper
@@ -69,18 +63,20 @@ GridLayout {
anchors.left: root.horizontal ? undefined : parent.left
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
clip: true
height: root.horizontal ? root.snap(root.percentage * track.height) : track.height * 2
width: root.horizontal ? track.width * 2 : root.snap(root.percentage * track.width)
implicitHeight: root.horizontal ? root.percentage * track.height : track.height * 2
implicitWidth: root.horizontal ? track.width * 2 : root.percentage * track.width
CustomRect {
anchors.bottom: root.horizontal ? parent.bottom : undefined
anchors.horizontalCenter: root.horizontal ? parent.horizontalCenter : undefined
anchors.left: root.horizontal ? undefined : parent.left
anchors.verticalCenter: root.horizontal ? undefined : parent.verticalCenter
color: root.mainColor
height: track.height
radius: track.radius
width: track.width
implicitHeight: track.height
implicitWidth: track.width
}
}
}
+2
View File
@@ -33,6 +33,7 @@ WidgetBase {
anchors.horizontalCenterOffset: root.horizontal ? 0 : 1
anchors.verticalCenterOffset: root.horizontal ? 0 : -3
columnSpacing: Tokens.spacing.small
rowSpacing: 0
columns: root.horizontal ? -1 : 1
ServiceRef {
@@ -51,6 +52,7 @@ WidgetBase {
Layout.alignment: Qt.AlignVCenter
Layout.fillHeight: root.horizontal
Layout.fillWidth: !root.horizontal
horizontal: root.horizontal
icon: "memory"
iconColor: root.screenState.resources ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
+8 -9
View File
@@ -31,7 +31,7 @@ WidgetBase {
return -1;
}
readonly property real size: horizontal ? grid.implicitWidth + grid.anchors.leftMargin + grid.anchors.rightMargin : grid.implicitHeight + grid.anchors.bottomMargin + grid.anchors.topMargin
readonly property int spacing: Tokens.spacing.medium / 2
readonly property int spacing: Tokens.spacing.extraSmall
// Entries that can shrink to nothing, spacing included
function collapsed(entry: var): bool {
@@ -51,14 +51,12 @@ WidgetBase {
Behavior on implicitHeight {
enabled: !root.horizontal
Anim {
}
Anim {}
}
Behavior on implicitWidth {
enabled: root.horizontal
Anim {
}
Anim {}
}
GridLayout {
@@ -70,6 +68,8 @@ WidgetBase {
anchors.rightMargin: root.horizontal ? Tokens.padding.extraSmall : 0
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
columns: root.horizontal ? -1 : 1
columnSpacing: root.spacing
rowSpacing: root.spacing
Repeater {
model: ScriptModel {
@@ -111,8 +111,7 @@ WidgetBase {
delegate: EntryWrapper {
name: "upower"
UPowerWidget {
}
UPowerWidget {}
}
}
}
@@ -135,8 +134,8 @@ WidgetBase {
Layout.rightMargin: root.horizontal ? Math.round(rightGap) : 0
Layout.topMargin: root.horizontal ? 0 : Math.round(leftGap)
children: item
implicitHeight: item?.implicitHeight ?? 0
implicitWidth: item?.implicitWidth ?? 0
implicitHeight: item?.height ?? item?.implicitHeight ?? 0
implicitWidth: item?.width ?? item?.implicitWidth ?? 0
Behavior on leftGap {
Anim {
@@ -6,7 +6,7 @@ import qs.Components
import qs.Services
MaterialIcon {
id: speaker
id: root
required property bool horizontal
@@ -15,6 +15,8 @@ MaterialIcon {
fill: 1
fontStyle: Tokens.font.icon.small
text: Audio.muted ? "volume_off" : "volume_up"
width: Math.round(implicitWidth)
height: Math.round(implicitHeight)
Behavior on Layout.maximumWidth {
Anim {}
@@ -6,7 +6,7 @@ import qs.Components
import qs.Services
MaterialIcon {
id: mic
id: root
required property bool horizontal
@@ -15,6 +15,8 @@ MaterialIcon {
fill: 1
fontStyle: Tokens.font.icon.small
text: Audio.sourceMuted ? "mic_off" : "mic"
width: Math.round(implicitWidth)
height: Math.round(implicitHeight)
Behavior on Layout.maximumWidth {
Anim {}
+4 -1
View File
@@ -19,7 +19,7 @@ Item {
required property Wrapper popouts
function resolveIcon(app: string, icon: string): string {
if (app === "chrome_status_icon_1") {
if (app === "discord_status_icon_1" && Config.bar.tray.replaceIcons) {
return Quickshell.iconPath("discord-tray");
} else if (app === "AyuGramDesktop") {
if (icon === Quickshell.iconPath("com.ayugram.desktop-attention-symbolic"))
@@ -50,6 +50,8 @@ Item {
radius: Tokens.rounding.full
StateLayer {
id: layer
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
color: icon.layer.enabled && root.current ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
@@ -77,6 +79,7 @@ Item {
anchors.centerIn: parent
antialiasing: true
backer.retainWhileLoading: true
color: root.current && !Config.bar.tray.showOnHover ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
implicitSize: Config.bar.tray.trayIconSize * root.dpr
layer.enabled: Config.bar.tray.recolorIcons
+1
View File
@@ -38,6 +38,7 @@ WidgetBase {
anchors.topMargin: root.horizontal ? 0 : Tokens.padding.extraSmall
columns: root.horizontal ? -1 : 1
rowSpacing: -2
columnSpacing: Tokens.spacing.extraSmall
Repeater {
id: repeater
+25 -27
View File
@@ -12,13 +12,14 @@ import qs.Services
Item {
id: root
property real activeWorkspaceMargin: Math.ceil(Tokens.padding.medium / 4)
property real activeWorkspaceMargin: Tokens.padding.extraSmall / 2
readonly property int spacing: Tokens.spacing.extraSmall / 2
readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1
required property bool horizontal
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
required property ShellScreen screen
readonly property real shortSize: Math.max(Config.bar.height, Tokens.sizes.bar.innerSize)
readonly property real size: (workspaceButtonWidth * workspacesShown) + activeWorkspaceMargin * 2
readonly property real size: (workspaceButtonWidth * workspacesShown) + (spacing * Math.max(0, workspacesShown - 1)) + activeWorkspaceMargin * 2
property int workspaceButtonWidth: (horizontal ? bgRect.implicitHeight : bgRect.implicitWidth) - root.activeWorkspaceMargin * 2
property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown
readonly property list<var> workspaces: Hyprland.workspaces.values.filter(w => w.monitor === root.monitor)
@@ -59,15 +60,17 @@ Item {
anchors.fill: parent
anchors.margins: root.activeWorkspaceMargin
columnSpacing: 0
columnSpacing: root.spacing
columns: root.horizontal ? root.workspacesShown : 1
rowSpacing: 0
rowSpacing: root.spacing
rows: root.horizontal ? 1 : root.workspacesShown
layer.enabled: true
layer.mipmap: true
Repeater {
model: root.workspaces
Button {
CustomRect {
id: button
required property int index
@@ -75,27 +78,19 @@ Item {
implicitHeight: indicator.indicatorThickness
implicitWidth: indicator.indicatorThickness
width: root.workspaceButtonWidth
radius: Tokens.rounding.full
background: Item {
id: workspaceButtonBackground
implicitHeight: root.workspaceButtonWidth
implicitWidth: root.workspaceButtonWidth
CustomText {
anchors.centerIn: parent
color: Colors.palette.m3onSecondaryContainer
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
text: button.modelData?.name ?? ""
verticalAlignment: Text.AlignVCenter
}
CustomText {
anchors.centerIn: parent
color: Colors.palette.m3onSecondaryContainer
text: button.modelData?.name ?? ""
}
onPressed: {
const ws = button.modelData.name;
Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`);
StateLayer {
onPressed: {
const ws = button.modelData.name;
Hyprland.dispatch(Hyprland.usingLua ? `hl.dsp.focus({ workspace= "${ws}"})` : `workspace ${ws}`);
}
}
}
}
@@ -104,8 +99,10 @@ Item {
CustomRect {
id: indicator
property real indicatorLength: (Math.abs(idxPair.idx1 - idxPair.idx2) + 1) * root.workspaceButtonWidth
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * root.workspaceButtonWidth + root.activeWorkspaceMargin
readonly property real step: root.workspaceButtonWidth + root.spacing
property real indicatorLength: (Math.abs(idxPair.idx1 - idxPair.idx2) + 1) * root.workspaceButtonWidth + Math.abs(idxPair.idx1 - idxPair.idx2) * root.spacing
property real indicatorPosition: Math.min(idxPair.idx1, idxPair.idx2) * step + root.activeWorkspaceMargin
property real indicatorThickness: root.workspaceButtonWidth
anchors.horizontalCenter: root.horizontal ? undefined : parent.horizontalCenter
@@ -129,9 +126,10 @@ Item {
implicitHeight: grid.height
implicitWidth: grid.width
source: grid
autoPaddingEnabled: false
sourceColor: Colors.palette.m3onSurface
x: root.horizontal ? -indicator.x + 3 : 0
y: root.horizontal ? 0 : -indicator.y + 3
x: root.horizontal ? -indicator.x + root.activeWorkspaceMargin : 0
y: root.horizontal ? 0 : -indicator.y + root.activeWorkspaceMargin
}
}
}
+32 -28
View File
@@ -33,8 +33,7 @@ Item {
y: parent.y
Behavior on implicitHeight {
Anim {
}
Anim {}
}
}
@@ -101,8 +100,7 @@ Item {
value: Audio.volume
Behavior on value {
Anim {
}
Anim {}
}
onInteraction: value => Audio.setVolume(value)
@@ -123,7 +121,7 @@ Item {
}
}
CustomClippingRect {
CustomRect {
Layout.fillWidth: true
Layout.preferredHeight: 65 + Tokens.spacing.small * 2
Layout.topMargin: root.topMargin
@@ -136,17 +134,21 @@ Item {
node: Audio.source
}
CustomRect {
id: sourcePeakFill
ClipRect {
anchors.fill: parent
radius: parent.radius
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
color: Qt.alpha(Colors.palette.m3primary, 0.15)
implicitWidth: parent.width * sourcePeak.peak
CustomRect {
id: sourcePeakFill
Behavior on implicitWidth {
Anim {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
color: Qt.alpha(Colors.palette.m3primary, 0.15)
implicitWidth: parent.width * sourcePeak.peak
Behavior on implicitWidth {
Anim {}
}
}
}
@@ -195,8 +197,7 @@ Item {
value: Audio.sourceVolume
Behavior on value {
Anim {
}
Anim {}
}
onInteraction: value => Audio.setSourceVolume(value)
@@ -230,7 +231,7 @@ Item {
model: Audio.streams.filter(s => s.isSink)
CustomClippingRect {
CustomRect {
id: appBox
required property int index
@@ -248,17 +249,21 @@ Item {
node: appBox.modelData
}
CustomRect {
id: peakFill
ClipRect {
anchors.fill: parent
radius: parent.radius
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
color: Qt.alpha(Colors.palette.m3primary, 0.15)
implicitWidth: parent.width * peak.peak
CustomRect {
id: peakFill
Behavior on implicitWidth {
Anim {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
color: Qt.alpha(Colors.palette.m3primary, 0.15)
implicitWidth: parent.width * peak.peak
Behavior on implicitWidth {
Anim {}
}
}
}
@@ -316,8 +321,7 @@ Item {
value: appBox.modelData.audio.volume
Behavior on value {
Anim {
}
Anim {}
}
onInteraction: value => Audio.setStreamVolume(appBox.modelData, value)
+2 -1
View File
@@ -9,7 +9,7 @@ import qs.Modules
import qs.Helpers
import qs.Services
CustomClippingRect {
CustomRect {
id: root
readonly property bool hasUpdates: Object.keys(Updates.updates)?.length > 0
@@ -73,6 +73,7 @@ CustomClippingRect {
implicitHeight: Math.min(contentHeight, (root.itemHeight + spacing) * 5 - spacing)
implicitWidth: contentWidth
spacing: Tokens.spacing.medium
clip: true
delegate: CustomRect {
id: update
-2
View File
@@ -1,6 +1,4 @@
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import QtQuick
import qs.Components
import ZShell.Config
+5 -3
View File
@@ -25,7 +25,7 @@ Item {
searchField.forceActiveFocus();
}
CustomClippingRect {
ClipRect {
id: search
anchors.left: parent.left
@@ -54,7 +54,7 @@ Item {
}
}
CustomClippingRect {
CustomRect {
id: preview
anchors.bottom: parent.bottom
@@ -64,6 +64,7 @@ Item {
color: Colors.tPalette.m3surfaceContainer
implicitWidth: ClipHistory.previewIsImage ? Math.max(Math.min(imagePreview.sourceSize.width + Tokens.padding.large * 2, Config.clipboard.sizes.previewWidth), Config.clipboard.sizes.minPreviewWidth) : Math.max(Math.min(textPreviewColumn.width + textPreviewColumn.anchors.margins * 2, Config.clipboard.sizes.previewWidth), Config.clipboard.sizes.minPreviewWidth)
radius: 25
clip: true
Behavior on implicitWidth {
Anim {}
@@ -228,11 +229,12 @@ Item {
anchors.fill: parent
spacing: Tokens.spacing.small
CustomClippingRect {
Item {
id: textRect
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
Item {
id: textWrapper
+1 -1
View File
@@ -14,7 +14,7 @@ Row {
padding: 20
spacing: 12
CustomClippingRect {
ClipRect {
color: Colors.layer(Colors.palette.m3surfaceContainerHigh, 2)
implicitHeight: info.implicitHeight
implicitWidth: info.implicitHeight
+1 -1
View File
@@ -16,7 +16,7 @@ Item {
implicitWidth: 1000
implicitHeight: 320
CustomClippingRect {
ClipRect {
anchors.fill: parent
radius: root.radius
@@ -46,7 +46,7 @@ CustomListView {
anchors.bottom: parent.bottom
spacing: Tokens.spacing.extraSmall / 2
CustomClippingRect {
ClipRect {
id: iconClip
Layout.fillHeight: true
@@ -25,7 +25,7 @@ Item {
onClicked: root.close()
}
CustomClippingRect {
ClipRect {
id: popupBackground
readonly property real padding: 4
@@ -40,7 +40,7 @@ Item {
visible: false
z: 999
CustomClippingRect {
ClipRect {
id: popupBackground
readonly property real padding: Tokens.padding.extraSmall
+18 -1
View File
@@ -25,6 +25,8 @@ CustomListView {
return [0];
case "variant":
return SchemeVariants.query(text);
case "files":
return Files.query(text);
default:
return Apps.search(text);
}
@@ -33,7 +35,7 @@ CustomListView {
function stateForText(text: string): string {
const prefix = Config.launcher.actionPrefix;
if (text.startsWith(prefix)) {
for (const action of ["calc", "scheme", "variant"])
for (const action of ["calc", "scheme", "variant", "files"])
if (text.startsWith(`${prefix}${action} `))
return action;
@@ -163,6 +165,13 @@ CustomListView {
PropertyChanges {
root.delegate: variantItem
}
},
State {
name: "files"
PropertyChanges {
root.delegate: filesItem
}
}
]
transitions: Transition {
@@ -264,6 +273,14 @@ CustomListView {
}
}
Component {
id: filesItem
FilesItem {
list: root
}
}
Connections {
function onTextChanged() {
root.syncDisplayText();
+2
View File
@@ -85,6 +85,8 @@ Item {
} else if (text.startsWith(Config.launcher.actionPrefix)) {
if (text.startsWith(`${Config.launcher.actionPrefix}calc `))
currentItem.onClicked();
else if (text.startsWith(`${Config.launcher.actionPrefix}files `))
currentItem.onClicked();
else
currentItem.modelData.onClicked(list.currentList);
} else {
+1 -1
View File
@@ -16,7 +16,7 @@ Item {
implicitHeight: Config.launcher.sizes.itemHeight
StateLayer {
radius: Tokens.rounding.small
radius: Tokens.rounding.medium
onClicked: {
root.modelData?.onClicked(root.list);
+1 -1
View File
@@ -19,7 +19,7 @@ Item {
implicitHeight: Config.launcher.sizes.itemHeight
StateLayer {
radius: Tokens.rounding.small
radius: Tokens.rounding.medium
onClicked: {
Apps.launch(root.modelData);
+2 -3
View File
@@ -24,7 +24,7 @@ Item {
implicitHeight: Config.launcher.sizes.itemHeight
StateLayer {
radius: Tokens.rounding.small
radius: Tokens.rounding.medium
onClicked: {
root.onClicked();
@@ -97,8 +97,7 @@ Item {
text: qsTr("Open in calculator")
Behavior on opacity {
Anim {
}
Anim {}
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ Item {
implicitHeight: Config.launcher.sizes.itemHeight
StateLayer {
radius: Tokens.rounding.small
radius: Tokens.rounding.medium
onClicked: {
root.modelData?.onClicked(root.list);
+61 -9
View File
@@ -1,3 +1,5 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import ZShell.Config
@@ -9,7 +11,7 @@ Item {
id: root
required property bool isCurrent
required property var modelData // FileSystemEntry-like {path, relativePath}
required property var modelData
required property real tileHeight
required property real tileWidth
required property ScreenState screenState
@@ -19,7 +21,7 @@ Item {
implicitHeight: tileHeight + Tokens.padding.large * 2
implicitWidth: tileWidth
CustomClippingRect {
ClipRect {
id: image
anchors.horizontalCenter: parent.horizontalCenter
@@ -29,25 +31,75 @@ Item {
radius: Tokens.rounding.small
y: Tokens.padding.large
MaterialIcon {
Loader {
active: opacity > 0
anchors.centerIn: parent
color: Colors.tPalette.m3outline
fontStyle: Tokens.font.icon.builders.extraLarge.scale(2).weight(Font.DemiBold).build()
text: "image"
opacity: img.status === Image.Ready ? 0 : 1
Behavior on opacity {
Anim {}
}
sourceComponent: CustomRect {
color: Colors.palette.m3primaryContainer
implicitHeight: loadingIndicator.implicitSize + Tokens.padding.large * 2
implicitWidth: loadingIndicator.implicitSize + Tokens.padding.large * 2
radius: Tokens.rounding.full
LoadingIndicator {
id: loadingIndicator
anchors.centerIn: parent
containsIcon: true
implicitSize: Math.min(image.width, image.height) * 0.3
}
}
}
CachingImage {
Image {
id: img
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
cache: true
asynchronous: true
fillMode: Image.PreserveAspectCrop
height: root.tileHeight
path: root.modelData ? root.modelData.path : ""
source: root.modelData ? root.modelData.thumbnailPath : ""
smooth: root.isCurrent
sourceSize.height: root.tileHeight
sourceSize.width: root.tileHeight * (16 / 9)
width: root.tileHeight * (16 / 9)
opacity: status === Image.Ready ? 1 : 0
Behavior on opacity {
Anim {
type: Anim.SlowEffects
}
}
Loader {
opacity: root.modelData.isVideo && img.status === Image.Ready ? 1 : 0
active: opacity > 0
anchors.centerIn: parent
Behavior on opacity {
Anim {
type: Anim.SlowEffects
}
}
sourceComponent: CustomRect {
implicitWidth: videoIcon.implicitWidth + Tokens.padding.extraLarge * 2
implicitHeight: implicitWidth
radius: Tokens.rounding.full
color: Colors.tPalette.m3surface
MaterialIcon {
id: videoIcon
anchors.centerIn: parent
text: "video_library"
fontStyle: Tokens.font.icon.extraLarge
}
}
}
}
StateLayer {
+194
View File
@@ -0,0 +1,194 @@
// qs/Services/Files.qml
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import ZShell.Config
Singleton {
id: root
property list<var> results: []
property string _pendingQuery: ""
property int _generation: 0
readonly property var _imageExts: ({
"png": true,
"jpg": true,
"jpeg": true,
"gif": true,
"bmp": true,
"webp": true,
"svg": true,
"tiff": true,
"tif": true,
"ico": true,
"heic": true,
"avif": true
})
// freedesktop mimetype-icon names; extend as needed
readonly property var _extIcons: ({
// images
"png": "image-x-generic",
"jpg": "image-x-generic",
"jpeg": "image-x-generic",
"gif": "image-x-generic",
"bmp": "image-x-generic",
"webp": "image-x-generic",
"svg": "image-x-generic",
"tiff": "image-x-generic",
"tif": "image-x-generic",
"ico": "image-x-generic",
"heic": "image-x-generic",
"avif": "image-x-generic",
// documents
"pdf": "application-pdf",
"doc": "x-office-document",
"docx": "x-office-document",
"odt": "x-office-document",
"txt": "text-x-generic",
"md": "text-x-generic",
"xls": "x-office-spreadsheet",
"xlsx": "x-office-spreadsheet",
"ods": "x-office-spreadsheet",
"ppt": "x-office-presentation",
"pptx": "x-office-presentation",
"odp": "x-office-presentation",
// archives
"zip": "package-x-generic",
"tar": "package-x-generic",
"gz": "package-x-generic",
"xz": "package-x-generic",
"7z": "package-x-generic",
"rar": "package-x-generic",
// audio/video
"mp3": "audio-x-generic",
"flac": "audio-x-generic",
"wav": "audio-x-generic",
"ogg": "audio-x-generic",
"mp4": "video-x-generic",
"mkv": "video-x-generic",
"webm": "video-x-generic",
"avi": "video-x-generic",
"mov": "video-x-generic",
// code
"js": "text-x-script",
"ts": "text-x-script",
"py": "text-x-script",
"sh": "text-x-script",
"qml": "text-x-script",
"cpp": "text-x-c++src",
"c": "text-x-csrc",
"h": "text-x-chdr",
"json": "application-json",
"html": "text-html",
"css": "text-css"
})
function showInFolder(path: string): void {
const explorer = Config.general.apps.explorer;
let args = [];
if (explorer.includes("dolphin"))
args = [...explorer, "--select", path];
else
args = [...explorer, path];
if (Config.launcher.uwsm)
Quickshell.execDetached(["app2unit", "--", ...args]);
else
Quickshell.execDetached([...args]);
}
function launch(command: list<string>): void {
if (Config.launcher.uwsm)
Quickshell.execDetached(["app2unit", "--"].concat(command));
else
Quickshell.execDetached(command);
}
function transformSearch(search: string): string {
return search.slice(Config.launcher.actionPrefix.length + "files ".length);
}
function query(search: string): list<var> {
search = transformSearch(search);
if (search !== _pendingQuery) {
_pendingQuery = search;
debounceTimer.restart();
}
return results;
}
function _extOf(name: string): string {
const i = name.lastIndexOf(".");
// no dot, or dotfile with no real extension (".bashrc" -> treat as no ext)
if (i <= 0)
return "";
return name.slice(i + 1).toLowerCase();
}
function _iconFor(name: string, isDir: bool): string {
if (isDir)
return "folder";
const ext = _extOf(name);
return _extIcons[ext] ?? "text-x-generic";
}
function _buildEntry(path: string): var {
const name = path.split("/").pop();
const ext = _extOf(name);
// trailing slash from plocate's directory output (if -d/dir results ever appear)
const isDir = path.endsWith("/");
return {
path: path,
name: name,
icon: _iconFor(name, isDir),
isImage: !isDir && (_imageExts[ext] ?? false)
};
}
Timer {
id: debounceTimer
interval: 120
onTriggered: root._runQuery(root._pendingQuery)
}
function _runQuery(search: string): void {
_generation++;
if (!search) {
proc.running = false;
results = [];
return;
}
if (proc.running)
proc.running = false;
proc.buffer = [];
proc.myGeneration = _generation;
proc.command = ["plocate", "-i", "-b", "--limit", "50", search];
proc.running = true;
}
Process {
id: proc
property var buffer: []
property int myGeneration: 0
stdout: SplitParser {
onRead: line => proc.buffer.push(root._buildEntry(line))
}
onRunningChanged: {
if (!running && myGeneration === root._generation)
root.results = buffer;
}
}
}
+1 -1
View File
@@ -55,7 +55,7 @@ ColumnLayout {
text: Time.format("dddd, d MMMM yyyy")
}
CustomClippingRect {
ClipRect {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Tokens.spacing.largeIncreased * 2
color: Colors.tPalette.m3surfaceContainer
+4 -17
View File
@@ -5,14 +5,13 @@ import qs.Helpers
import ZShell.Config
import qs.Services
CustomClippingRect {
Item {
id: root
required property var lock
implicitHeight: layout.implicitHeight
implicitWidth: layout.implicitWidth
radius: Tokens.rounding.largeIncreased
RowLayout {
id: layout
@@ -50,22 +49,10 @@ CustomClippingRect {
lock: root.lock
}
ColumnLayout {
NotifDock {
Layout.fillHeight: true
Layout.fillWidth: true
spacing: Tokens.spacing.medium
CustomRect {
Layout.fillHeight: true
Layout.fillWidth: true
bottomRightRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
NotifDock {
lock: root.lock
}
}
lock: root.lock
}
}
}
+1 -1
View File
@@ -221,9 +221,9 @@ WlSessionLockSurface {
anchors.fill: parent
color: Colors.palette.m3surface
layer.enabled: true
opacity: Colors.transparency.enabled ? Colors.transparency.base : 1
radius: lockContent.radius
layer.enabled: true
layer.effect: MultiEffect {
blurMax: 15
+32 -27
View File
@@ -2,44 +2,49 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import ZShell.Components
import qs.Components
import ZShell.Config
import qs.Components
import qs.Helpers
import qs.Services
CustomClippingRect {
Item {
id: root
required property var lock
color: Colors.tPalette.m3surfaceContainer
implicitHeight: layout.implicitHeight + layout.anchors.margins * 2
radius: Tokens.rounding.small
FadeImage {
id: image
ClipRect {
anchors.fill: parent
asynchronous: true
fillMode: Image.PreserveAspectCrop
layer.enabled: true
opacity: status === Image.Ready ? 1 : 0
source: Players.getArtUrl(Players.active)
sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr);
}
Behavior on opacity {
Anim {
type: Anim.StandardExtraLarge
}
}
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
bottomLeftRadius: Tokens.rounding.largeIncreased
FadeImage {
id: image
CustomRect {
anchors.fill: parent
color: Colors.palette.m3surface
opacity: 0.7
asynchronous: true
fillMode: Image.PreserveAspectCrop
opacity: status === Image.Ready ? 1 : 0
source: Players.getArtUrl(Players.active)
sourceSize: {
const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1;
return Qt.size(width * dpr, height * dpr);
}
Behavior on opacity {
Anim {
type: Anim.StandardExtraLarge
}
}
CustomRect {
anchors.fill: parent
color: Colors.palette.m3surface
opacity: 0.7
}
}
}
@@ -77,7 +82,7 @@ CustomClippingRect {
spacing: Tokens.spacing.extraSmall
IconButton {
enabled: Players.active?.canGoPrevious
enabled: Players.active?.canGoPrevious ?? false
icon: "skip_previous"
isRound: true
shapeMorph: true
@@ -88,7 +93,7 @@ CustomClippingRect {
IconButton {
checked: Players.active?.isPlaying ?? false
enabled: Players.active?.canTogglePlaying
enabled: Players.active?.canTogglePlaying ?? false
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
implicitWidth: implicitHeight + Tokens.padding.largeIncreased * 2
isRound: true
@@ -98,7 +103,7 @@ CustomClippingRect {
}
IconButton {
enabled: Players.active?.canGoNext
enabled: Players.active?.canGoNext ?? false
icon: "skip_next"
isRound: true
shapeMorph: true
+94 -105
View File
@@ -11,127 +11,116 @@ import ZShell.Config
import qs.Daemons
import qs.Services
ColumnLayout {
CustomRect {
id: root
required property var lock
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small
bottomRightRadius: Tokens.rounding.largeIncreased
color: Colors.tPalette.m3surfaceContainer
radius: Tokens.rounding.small
topRightRadius: Tokens.rounding.largeIncreased
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3outline
elide: Text.ElideRight
font.family: Config.appearance.font.family.mono
font.weight: 500
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
}
ColumnLayout {
anchors.fill: parent
anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.small
ClippingRectangle {
id: clipRect
Layout.fillHeight: true
Layout.fillWidth: true
color: "transparent"
radius: Tokens.rounding.small
Loader {
active: opacity > 0
anchors.centerIn: parent
opacity: NotifServer.list.length > 0 ? 0 : 1
Behavior on opacity {
Anim {
duration: Tokens.anim.durations.extraLarge
}
}
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased
Image {
asynchronous: true
fillMode: Image.PreserveAspectFit
layer.enabled: true
source: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/dino.png`)
sourceSize.width: clipRect.width * 0.8
layer.effect: Coloriser {
brightness: 1
colorizationColor: Colors.palette.m3outlineVariant
}
}
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
font: Tokens.font.headline.builders.small.width(90).build()
text: qsTr("No Notifications")
}
}
CustomText {
Layout.fillWidth: true
color: Colors.palette.m3outline
elide: Text.ElideRight
font: Tokens.font.mono.builders.small.weight(Font.Medium).build()
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
}
CustomListView {
anchors.fill: parent
clip: true
spacing: Tokens.spacing.small
Item {
Layout.fillHeight: true
Layout.fillWidth: true
add: Transition {
Anim {
from: 0
property: "opacity"
to: 1
Loader {
active: opacity > 0
anchors.centerIn: parent
opacity: NotifServer.list.length > 0 ? 0 : 1
Behavior on opacity {
Anim {
duration: Tokens.anim.durations.extraLarge
}
}
sourceComponent: ColumnLayout {
spacing: Tokens.spacing.largeIncreased
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
from: 0
property: "scale"
to: 1
CustomText {
Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant
font: Tokens.font.headline.builders.small.width(90).build()
text: qsTr("No Notifications")
}
}
}
delegate: NotifGroup {}
displaced: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
property: "y"
}
}
model: ScriptModel {
values: {
const list = NotifServer.notClosed.map(n => [n.appName, null]);
return [...new Map(list).keys()];
}
}
move: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
CustomListView {
anchors.fill: parent
clip: true
spacing: Tokens.spacing.small
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
property: "y"
}
}
remove: Transition {
Anim {
property: "opacity"
to: 0
}
add: Transition {
Anim {
from: 0
property: "opacity"
to: 1
}
Anim {
property: "scale"
to: 0.6
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
from: 0
property: "scale"
to: 1
}
}
delegate: NotifGroup {}
displaced: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
property: "y"
}
}
model: ScriptModel {
values: {
const list = NotifServer.notClosed.map(n => [n.appName, null]);
return [...new Map(list).keys()];
}
}
move: Transition {
Anim {
properties: "opacity,scale"
to: 1
}
Anim {
duration: Tokens.anim.durations.expressiveDefaultSpatial
easing: Tokens.anim.expressiveDefaultSpatial
property: "y"
}
}
remove: Transition {
Anim {
property: "opacity"
to: 0
}
Anim {
property: "scale"
to: 0.6
}
}
}
}
+1 -1
View File
@@ -85,7 +85,7 @@ CustomRect {
}
}
ClippingRectangle {
ClipRect {
anchors.fill: parent
color: root.urgency === "critical" ? Colors.palette.m3error : root.urgency === "low" ? Colors.layer(Colors.palette.m3surfaceContainerHighest, 3) : Colors.palette.m3secondaryContainer
radius: Tokens.rounding.full
+2 -1
View File
@@ -1,12 +1,13 @@
import Quickshell
import Quickshell.Widgets
import QtQuick
import qs.Components
import qs.Paths
Item {
id: root
ClippingRectangle {
ClipRect {
anchors.fill: parent
radius: Tokens.rounding.full
+4 -3
View File
@@ -16,7 +16,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
animate: true
color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.title.large
font: Tokens.font.body.large
text: Weather.description
}
@@ -29,7 +29,7 @@ ColumnLayout {
animate: true
color: Colors.palette.m3primary
font: Tokens.font.body.large
font: Tokens.font.title.large
text: Weather.temp
}
@@ -37,6 +37,7 @@ ColumnLayout {
animate: true
color: Colors.palette.m3secondary
text: Weather.icon
fontStyle: Tokens.font.icon.large
}
}
@@ -53,7 +54,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
animate: true
color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.body.normal
font: Tokens.font.body.medium
text: {
const today = Weather.forecast[0];
return qsTr("High %1 • Low %2").arg(Weather.formatTemp(today?.maxTempC)).arg(Weather.formatTemp(today?.minTempC));
+12 -5
View File
@@ -36,7 +36,7 @@ CustomRect {
}
}
VerticalFadeListView {
CustomListView {
id: layout
anchors.bottom: parent.bottom
@@ -45,13 +45,20 @@ CustomRect {
anchors.margins: Tokens.padding.large
anchors.right: parent.right
implicitHeight: contentItem.childrenRect.height
model: Weather.hourlyForecast
currentIndex: Weather.currentHourIndex
// highlightFollowsCurrentItem: true
// preferredHighlightEnd: currentItem.implicitWidth
// preferredHighlightBegin: 0
// highlightRangeMode: ListView.ApplyRange
clip: true
model: Weather.hourlyForecast.slice(Weather.currentHourIndex - 1)
orientation: VerticalFadeListView.Horizontal
spacing: Tokens.spacing.medium
delegate: ColumnLayout {
id: hour
readonly property bool current: ListView.view.currentItem === this
readonly property var cond: modelData
required property int index
required property var modelData
@@ -60,7 +67,7 @@ CustomRect {
MaterialShape {
Layout.alignment: Qt.AlignHCenter
color: Qt.alpha(Colors.palette.m3primary, hour.index === Weather.currentHourIndex ? 1 : 0)
color: Qt.alpha(Colors.palette.m3primary, hour.current ? 1 : 0)
implicitSize: temp.implicitHeight + Tokens.padding.small * 2
shape: MaterialShape.Cookie4Sided
@@ -72,7 +79,7 @@ CustomRect {
id: temp
anchors.centerIn: parent
color: hour.index === Weather.currentHourIndex ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
color: hour.current ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
font: Tokens.font.body.medium
text: Weather.formatTemp(hour.cond.tempC).slice(0, -1) // Remove C/F
}
@@ -96,7 +103,7 @@ CustomRect {
Layout.topMargin: Tokens.spacing.extraSmall
color: Colors.palette.m3onSurfaceVariant
font: Tokens.font.body.small
text: hour.index === 0 ? qsTr("Now") : Qt.formatDateTime(new Date(hour.cond.timestamp.replace("T", " ")), Config.services.useTwelveHourClock ? "ha" : "hh:00")
text: hour.current ? qsTr("Now") : Qt.formatDateTime(new Date(hour.cond.timestamp.replace("T", " ")), Config.services.useTwelveHourClock ? "ha" : "hh:00")
}
}
}
+2 -2
View File
@@ -19,6 +19,7 @@ CustomRect {
return base + brief.anchors.topMargin;
}
radius: Tokens.rounding.small
topLeftRadius: Tokens.rounding.largeIncreased
Timer {
interval: 900000 // 15 minutes
@@ -48,7 +49,6 @@ CustomRect {
anchors.right: parent.right
asynchronous: true
sourceComponent: Forecast {
}
sourceComponent: Forecast {}
}
}
+9 -10
View File
@@ -3,8 +3,9 @@ pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Widgets
import QtQuick
import qs.Components
import ZShell.Config
import ZShell.Components
import qs.Components
import qs.Daemons
import qs.Helpers
@@ -30,31 +31,29 @@ Item {
if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x)
return 0;
if (screenState.dashboard && (panels.dashboard.x + panels.dashboard.width >= panels.notifications.x))
return 0;
if (screenState.osd) {
const h = panels.osd.y - Tokens.spacing.small * 2 - padding * 2;
if (height > h)
height = h;
}
if (screenState.session) {
const h = panels.session.y - Tokens.spacing.small * 2 - padding * 2;
if (height > h)
height = h;
}
return Math.min(((QsWindow.window as QsWindow)?.screen?.height ?? 0) - 1 * 2, height + padding * 2);
}
implicitWidth: Config.notifs.sizes.width + padding * 2
Behavior on implicitHeight {
Anim {}
}
ClippingWrapperRectangle {
Item {
anchors.fill: parent
anchors.margins: root.padding
color: "transparent"
radius: Tokens.rounding.large - root.padding
clip: true
// radius: Tokens.rounding.large - root.padding
CustomListView {
id: list
+1 -1
View File
@@ -108,7 +108,7 @@ CustomRect {
visible: root.hasImage || root.hasAppIcon
width: Config.notifs.sizes.image
sourceComponent: CustomClippingRect {
sourceComponent: ClipRect {
color: root.modelData.urgency === NotificationUrgency.Critical ? Colors.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? Colors.layer(Colors.palette.m3surfaceContainerHighest, 2) : Colors.palette.m3secondaryContainer
implicitHeight: Config.notifs.sizes.image
implicitWidth: Config.notifs.sizes.image
-36
View File
@@ -1,36 +0,0 @@
import qs.Components
import ZShell.Config
import QtQuick
import QtQuick.Layouts
import qs.Services
Item {
id: root
required property ScreenState screenState
ColumnLayout {
id: layout
anchors.fill: parent
spacing: Tokens.spacing.small
CustomRect {
Layout.fillHeight: true
Layout.fillWidth: true
color: Colors.tPalette.m3surfaceContainerLow
radius: Tokens.rounding.small
NotifDock {
screenState: root.screenState
}
}
CustomRect {
Layout.fillWidth: true
Layout.topMargin: Tokens.padding.small - layout.spacing
color: Colors.tPalette.m3outlineVariant
implicitHeight: 1
}
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ import ZShell.Config
import qs.Components
import qs.Services
CustomClippingRect {
ClipRect {
id: root
property real animPerc: UPower.displayDevice.percentage

Some files were not shown because too many files have changed in this diff Show More