1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

Compare commits

..

100 Commits

Author SHA1 Message Date
bbedward
4a68ce35a3 hyprland: remove focus grab from dankpopout 2025-11-16 11:44:59 -05:00
bbedward
eb4655fcbc rebase woes 2025-11-16 11:37:19 -05:00
bbedward
6eb349c9d4 barmask: fix multi-screen handling 2025-11-16 11:33:47 -05:00
bbedward
0a8a7895b3 hyprland: use FocusGrab for ondemand windows 2025-11-16 11:33:47 -05:00
bbedward
73c82a4dd9 dankbar/mask: extra polish for Hyprland maybe 2025-11-16 11:33:47 -05:00
bbedward
ccf28fc4e7 dankbar: add a mask while popouts are open
- Retains ability to click items on the bar, while another is open
2025-11-16 11:32:47 -05:00
bbedward
64ec5be919 wallpaper: empty input region 2025-11-15 23:41:24 -05:00
bbedward
3916512d66 systemtray: fix erroneous undefined condition 2025-11-15 21:46:34 -05:00
bbedward
e2f426a1bd Revert "systemtray: fix UI thread freeze when opening menu on Hyprland"
This reverts commit 4cb652abd9.
2025-11-15 21:42:50 -05:00
bbedward
aa1df8dfcf core: more syncmap conversions 2025-11-15 20:00:47 -05:00
bbedward
67557555f2 core: refactor to use a generic-compatible syncmap 2025-11-15 19:45:19 -05:00
bbedward
4cb652abd9 systemtray: fix UI thread freeze when opening menu on Hyprland
- Similar pattern as fix from Noctalia
2025-11-15 17:57:23 -05:00
bbedward
d11868b99f systray: don't try to force focus of menus 2025-11-15 14:57:47 -05:00
bbedward
1798417e6a systemtray: don't take keyboard focus
- bricks hyprland
2025-11-15 14:48:13 -05:00
github-actions[bot]
43dc3e5bb1 nix: update vendorHash for go.mod changes 2025-11-15 19:43:35 +00:00
bbedward
91891a14ed core/wayland: thread-safety meta fixes + cleanups + hypr workaround
- fork go-wayland/client and modify to make it thread-safe internally
- use sync.Map and atomic values in many places to cut down on mutex
  boilerplate
- do not create extworkspace client unless explicitly requested
2025-11-15 14:41:00 -05:00
bbedward
20f7d60147 settings: various consistency issues fixed
part of #725
2025-11-15 12:05:44 -05:00
bbedward
7e17e7d37a osd: fix opacity
part of #725
2025-11-15 11:43:05 -05:00
bbedward
cbb244f785 osd: add option to disable each OSD 2025-11-15 11:36:33 -05:00
Sunner
1c264d858b Follow symlinks when searching for sessions (#728) 2025-11-15 10:29:34 -05:00
bbedward
217037c2ae evdev: fix test 2025-11-14 23:26:14 -05:00
bbedward
b4dbd0b69c evdev: enhance keyboard detection for capslock 2025-11-14 23:22:06 -05:00
github-actions[bot]
89a2b5c00b chore: bump version to v0.5.2 2025-11-15 00:31:06 +00:00
bbedward
929b6dae1a widgets: fix some 0-width issues 2025-11-14 19:26:51 -05:00
Pi Home Server
52fe493da9 Feature/privacy widget - Settings to force icons on (#715)
* Update

* Update

* Update

* Update

* Update

* Set default to false

* Update SettingsData.qml

Set default visibility to false

* privacy widget: fix truncated settings menu

---------

Co-authored-by: bbedward <bbedward@gmail.com>
2025-11-14 19:16:17 -05:00
purian23
3e6be3e762 Greet path updates 2025-11-14 17:54:35 -05:00
purian23
7a8cc449b9 Add local ACL greeter permissions to dms core installer 2025-11-14 16:32:06 -05:00
purian23
8f5a9d6e9f Update dms greeter to scan system & local directories 2025-11-14 15:36:14 -05:00
bbedward
1c5e31fea9 greeter: allow mangowc as compositor 2025-11-14 14:51:28 -05:00
claymorwan
fd08ae18ab feat: plugin layer namespace (#717) 2025-11-14 14:50:29 -05:00
bbedward
a7eb3de06e dankbar: configurable auto-hide delay 2025-11-14 14:00:37 -05:00
bbedward
8902dd7c44 launcher: grid re-style and customizable column counts 2025-11-14 13:54:44 -05:00
bbedward
6387d8400c osd: account for bar position when on bottom 2025-11-14 13:47:26 -05:00
bbedward
597cacb9cc matugen: update gtk4/gtk3-dark colors
- also some change to dankinstall to use niri/xwls from system repos,
  too lazy to split the commits
2025-11-14 13:20:59 -05:00
bbedward
3e285ad9ff dankdash: remove useless tint rectangle
part of #716
2025-11-14 13:09:46 -05:00
bbedward
cc1fa89790 clock: use precision minutes instead of seconds, unless needed
part of #716
2025-11-14 12:42:23 -05:00
bbedward
b0ed007751 core/dankinstall: more deb fixes 2025-11-14 12:22:13 -05:00
bbedward
e1e2650d2b core/dankinstall: fix hyprland util manual compile on debian 2025-11-14 12:13:49 -05:00
bbedward
b23f17b633 core/dankinstall: fix hyprpicker build 2025-11-14 12:07:03 -05:00
github-actions[bot]
818e40b2df nix: update vendorHash for go.mod changes 2025-11-14 17:06:06 +00:00
bbedward
5685e39631 core: improve evdev capslock detection, wayland context fixes 2025-11-14 12:04:47 -05:00
kritag
72534b7674 adding tokyonight, everforest, nord and rose-pine themes (#714)
Co-authored-by: Kristian Tagesen <kristian.tagesen@tietoevry.com>
2025-11-14 11:40:26 -05:00
bbedward
328490d23d powermenu: smarter positioning in control center 2025-11-14 10:45:16 -05:00
bbedward
97a0696930 clock: fix overview clock when seconds is on 2025-11-14 10:29:41 -05:00
bbedward
cb4e0660e0 dock: add reveal IPCs 2025-11-14 10:08:16 -05:00
bbedward
67c642de4c keybinds: add toggleWithPath 2025-11-14 09:03:27 -05:00
bbedward
0d7c2e1024 core/cli: fix keybind provider path override 2025-11-14 08:56:16 -05:00
bbedward
16a779a41b powermenu: restore grid as an option
fixes #712
2025-11-14 08:51:15 -05:00
purian23
c4ca3c8644 Add root dms-cli build script 2025-11-14 00:22:49 -05:00
bbedward
aabcbe34f3 Merge branch 'master' of github.com:AvengeMedia/DankMaterialShell 2025-11-14 00:06:50 -05:00
bbedward
f06626e441 dock: use modded app IDs for grouping logic
fixes #710
2025-11-14 00:06:27 -05:00
purian23
c4e1a71776 Relocate notification tests to scripts dir 2025-11-13 23:53:18 -05:00
bbedward
77e6c16bd2 core/extworkspace: fix some thread-safety issues 2025-11-13 23:52:32 -05:00
purian23
9d1fac3570 Relocate Nix dir under distro/nix 2025-11-13 23:47:00 -05:00
bbedward
b7aeaa7fc5 systemtray: better hide/unhide behavioro 2025-11-13 22:49:30 -05:00
bbedward
f6d8c9ff61 Merge branch 'master' of github.com:AvengeMedia/DankMaterialShell 2025-11-13 22:41:47 -05:00
bbedward
0490794d6c dankbar: add caps lock indicator widget 2025-11-13 22:41:33 -05:00
github-actions[bot]
335c83dd3c nix: update vendorHash for go.mod changes 2025-11-14 03:26:50 +00:00
bbedward
91da720c26 i18n:update translations 2025-11-13 22:25:22 -05:00
bbedward
b6ac744a68 Merge branch 'master' of github.com:AvengeMedia/DankMaterialShell 2025-11-13 22:24:51 -05:00
bbedward
526c4092fd evdev: add evdev monitor for caps lock state 2025-11-13 22:24:27 -05:00
github-actions[bot]
ed06dda384 nix: update vendorHash for go.mod changes 2025-11-14 02:54:15 +00:00
bbedward
6465b11e9b core: ensure all NM tests use mock backend + re-orgs + dep updates 2025-11-13 21:44:03 -05:00
purian23
b2879878a1 feat: Priority pinned items in Control Center 2025-11-13 21:23:54 -05:00
bbedward
3e17b086fb ci: add docs to release archive 2025-11-13 20:19:54 -05:00
purian23
0545e6bcda Remove release tags 2025-11-13 20:01:38 -05:00
purian23
27a907433f Test Copr workflow update 2025-11-13 19:40:16 -05:00
purian23
69616800e3 Release update 2025-11-13 18:54:01 -05:00
github-actions[bot]
abf1f53432 chore: bump version to v0.5.1 2025-11-13 23:45:49 +00:00
bbedward
881c5f75cb ci: ensure version on tag 2025-11-13 18:44:03 -05:00
bbedward
4e45796ade ci: no flake version update 2025-11-13 18:38:47 -05:00
bbedward
1ce4ea5230 ci: update 2025-11-13 18:30:34 -05:00
purian23
f2a2437baa fix Copr dms-greeter 2025-11-13 18:00:30 -05:00
bbedward
508dc9db1e weather: imperial switch not just fahrenheit
fixes #699
2025-11-13 17:41:03 -05:00
bbedward
a914e3557f Merge branch 'master' of github.com:AvengeMedia/DankMaterialShell 2025-11-13 17:31:29 -05:00
bbedward
f489dc062f dankinstall: fix variant passing 2025-11-13 17:31:02 -05:00
purian23
a7e09f4850 Update Copr dms-greeter paths 2025-11-13 17:29:22 -05:00
bbedward
8ea97530d4 matugen: add terminals always dark option 2025-11-13 17:19:37 -05:00
bbedward
13ab54e83a matugen: vscode theme repairs 2025-11-13 17:06:04 -05:00
bbedward
4bc40325cb hyprland: re-add special workspace filtering 2025-11-13 16:56:12 -05:00
bbedward
58d9355ea3 matugen: fix multi vscode themes 2025-11-13 16:51:16 -05:00
bbedward
d46b7528e7 systemtray: new tray detail menu 2025-11-13 16:30:07 -05:00
bbedward
1858597fc9 fix sudo usages 2025-11-13 15:41:41 -05:00
bbedward
83cce5afe4 dankinstall: re-simplify installation 2025-11-13 14:34:42 -05:00
bbedward
201bd8dc1f cli: fix greeter enable, and color sync 2025-11-13 13:21:18 -05:00
bbedward
b62ba69060 dankbar: fix hiding widgets that should not be enabled 2025-11-13 12:55:52 -05:00
bbedward
5d2f5557e5 dwl/mangowc: add layout switcher and viewer widget 2025-11-13 12:44:56 -05:00
bbedward
cf75c1aad0 show a power profile OSD 2025-11-13 10:23:14 -05:00
Saurabh
76a60df88b Feat: wezterm theming support (#705)
* implemented logic for wezterm theming

added matugen configs and dank16 functions, updated matugen worked
scripta

* fixed theme dir

fixed path and moved output location to default wezterm dir
2025-11-13 08:54:47 -05:00
bbedward
9322c79b4e nix: fix greeter path 2025-11-13 08:53:02 -05:00
Lucas
12365edcf0 flake: update to new monorepo structure (#701)
* nix: move alejandra.toml to root

* nix: build using local dms cli

* workflow: update update-vendor-hash to new structure
2025-11-13 00:26:03 -05:00
bbedward
5efc1f9dad powermenu: switch back to a list based style 2025-11-12 23:26:56 -05:00
bbedward
ab976cbb24 popout: add separate variable for layer override
fixes #700
2025-11-12 23:20:04 -05:00
bbedward
db584b7897 rename backend to core 2025-11-12 23:12:31 -05:00
bbedward
0fdc0748cf nix: fix flake 2025-11-12 22:44:17 -05:00
bbedward
2e79c21dc2 fedora: fix spec 2025-11-12 22:24:38 -05:00
bbedward
5490a230bd systemtray: fix menu positioning 2025-11-12 22:21:02 -05:00
bbedward
a6b059b30d don't gitignore Makefile 2025-11-12 22:19:08 -05:00
bbedward
712e6011aa fix contributing ref 2025-11-12 22:14:27 -05:00
bbedward
68f6f87410 disable vendor hash update 2025-11-12 22:06:46 -05:00
431 changed files with 18427 additions and 3456 deletions

View File

@@ -7,6 +7,10 @@ on:
description: 'Versioning (e.g., 0.1.14, leave empty for latest release)'
required: false
default: ''
release:
description: 'Release number (e.g., 1, 2, 3 for hotfixes)'
required: false
default: '1'
jobs:
build-and-upload:
@@ -19,6 +23,7 @@ jobs:
- name: Determine version
id: version
run: |
# Get version from manual input or latest release
if [ -n "${{ github.event.inputs.version }}" ]; then
VERSION="${{ github.event.inputs.version }}"
echo "Using manual version: $VERSION"
@@ -27,8 +32,14 @@ jobs:
echo "Using latest release version: $VERSION"
fi
RELEASE="${{ github.event.inputs.release }}"
if [ -z "$RELEASE" ]; then
RELEASE="1"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "✅ Building DMS stable version: $VERSION"
echo "release=$RELEASE" >> $GITHUB_OUTPUT
echo "✅ Building DMS hotfix version: $VERSION-$RELEASE"
- name: Setup build environment
run: |
@@ -57,6 +68,7 @@ jobs:
- name: Generate stable spec file
run: |
VERSION="${{ steps.version.outputs.version }}"
RELEASE="${{ steps.version.outputs.release }}"
CHANGELOG_DATE="$(date '+%a %b %d %Y')"
cat > ~/rpmbuild/SPECS/dms.spec <<'SPECEOF'
@@ -68,7 +80,7 @@ jobs:
Name: dms
Version: %{version}
Release: 1%{?dist}
Release: RELEASE_PLACEHOLDER%{?dist}
Summary: %{pkg_summary}
License: MIT
@@ -212,16 +224,17 @@ jobs:
%{_bindir}/dgop
%changelog
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-1
* CHANGELOG_DATE_PLACEHOLDER AvengeMedia <contact@avengemedia.com> - VERSION_PLACEHOLDER-RELEASE_PLACEHOLDER
- Stable release VERSION_PLACEHOLDER
- Built from GitHub release
- Includes latest dms-cli and dgop binaries
SPECEOF
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" ~/rpmbuild/SPECS/dms.spec
sed -i "s/RELEASE_PLACEHOLDER/${RELEASE}/g" ~/rpmbuild/SPECS/dms.spec
sed -i "s/CHANGELOG_DATE_PLACEHOLDER/${CHANGELOG_DATE}/g" ~/rpmbuild/SPECS/dms.spec
echo "✅ Spec file generated for v${VERSION}"
echo "✅ Spec file generated for v${VERSION}-${RELEASE}"
echo ""
echo "=== Spec file preview ==="
head -40 ~/rpmbuild/SPECS/dms.spec
@@ -295,7 +308,7 @@ jobs:
run: |
echo "### 🎉 DMS Stable Build Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Version:** ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Version:** ${{ steps.version.outputs.version }}-${{ steps.version.outputs.release }}" >> $GITHUB_STEP_SUMMARY
echo "- **SRPM:** ${{ steps.build.outputs.srpm_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **Project:** https://copr.fedorainfracloud.org/coprs/avengemedia/dms/" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

View File

@@ -5,7 +5,7 @@ on:
branches:
- '**'
paths:
- 'backend/**'
- 'core/**'
- '.github/workflows/go-ci.yml'
jobs:
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
defaults:
run:
working-directory: backend
working-directory: core
steps:
- name: Checkout
@@ -22,7 +22,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: ./backend/go.mod
go-version-file: ./core/go.mod
- name: Format check
run: |

View File

@@ -14,7 +14,7 @@ concurrency:
cancel-in-progress: true
jobs:
build-backend:
build-core:
runs-on: ubuntu-latest
strategy:
matrix:
@@ -22,7 +22,7 @@ jobs:
defaults:
run:
working-directory: backend
working-directory: core
steps:
- name: Checkout
@@ -33,7 +33,15 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: ./backend/go.mod
go-version-file: ./core/go.mod
- name: Format check
run: |
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
echo "The following files are not formatted:"
gofmt -s -l .
exit 1
fi
- name: Run tests
run: go test -v ./...
@@ -93,35 +101,36 @@ jobs:
if: matrix.arch == 'arm64'
uses: actions/upload-artifact@v4
with:
name: backend-assets-${{ matrix.arch }}
name: core-assets-${{ matrix.arch }}
path: |
backend/dankinstall-${{ matrix.arch }}.gz
backend/dankinstall-${{ matrix.arch }}.gz.sha256
backend/dms-${{ matrix.arch }}.gz
backend/dms-${{ matrix.arch }}.gz.sha256
backend/dms-distropkg-${{ matrix.arch }}.gz
backend/dms-distropkg-${{ matrix.arch }}.gz.sha256
core/dankinstall-${{ matrix.arch }}.gz
core/dankinstall-${{ matrix.arch }}.gz.sha256
core/dms-${{ matrix.arch }}.gz
core/dms-${{ matrix.arch }}.gz.sha256
core/dms-distropkg-${{ matrix.arch }}.gz
core/dms-distropkg-${{ matrix.arch }}.gz.sha256
if-no-files-found: error
- name: Upload artifacts with completions
if: matrix.arch == 'amd64'
uses: actions/upload-artifact@v4
with:
name: backend-assets-${{ matrix.arch }}
name: core-assets-${{ matrix.arch }}
path: |
backend/dankinstall-${{ matrix.arch }}.gz
backend/dankinstall-${{ matrix.arch }}.gz.sha256
backend/dms-${{ matrix.arch }}.gz
backend/dms-${{ matrix.arch }}.gz.sha256
backend/dms-distropkg-${{ matrix.arch }}.gz
backend/dms-distropkg-${{ matrix.arch }}.gz.sha256
backend/completion.bash
backend/completion.fish
backend/completion.zsh
core/dankinstall-${{ matrix.arch }}.gz
core/dankinstall-${{ matrix.arch }}.gz.sha256
core/dms-${{ matrix.arch }}.gz
core/dms-${{ matrix.arch }}.gz.sha256
core/dms-distropkg-${{ matrix.arch }}.gz
core/dms-distropkg-${{ matrix.arch }}.gz.sha256
core/completion.bash
core/completion.fish
core/completion.zsh
if-no-files-found: error
update-versions:
runs-on: ubuntu-latest
needs: build-core
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -129,7 +138,7 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Update VERSION and flake.nix
- name: Update VERSION
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
@@ -142,10 +151,7 @@ jobs:
# Update VERSION file in quickshell/
echo "${version}" > quickshell/VERSION
# Update version in backend/flake.nix
sed -i "s/version = \"[^\"]*\"/version = \"$version_no_v\"/" backend/flake.nix
git add quickshell/VERSION backend/flake.nix
git add quickshell/VERSION
if ! git diff --cached --quiet; then
git commit -m "chore: bump version to $version"
@@ -155,9 +161,13 @@ jobs:
echo "No version changes needed"
fi
# Force-push the tag to point to the commit with updated VERSION
git tag -f "${version}"
git push -f origin "${version}"
release:
runs-on: ubuntu-24.04
needs: build-backend
needs: [build-core, update-versions]
env:
TAG: ${{ github.ref_name }}
steps:
@@ -166,12 +176,17 @@ jobs:
with:
fetch-depth: 0
- name: Download backend artifacts
- name: Fetch updated tag after version bump
run: |
git fetch origin --force tag ${{ github.ref_name }}
git checkout ${{ github.ref_name }}
- name: Download core artifacts
uses: actions/download-artifact@v4
with:
pattern: backend-assets-*
pattern: core-assets-*
merge-multiple: true
path: ./_backend_assets
path: ./_core_assets
- name: Generate Changelog
id: changelog
@@ -233,8 +248,8 @@ jobs:
mkdir -p _release_assets
# Copy backend binaries and rename dms-*.gz to dms-cli-*.gz
for file in _backend_assets/dms-*.gz*; do
# Copy core binaries and rename dms-*.gz to dms-cli-*.gz
for file in _core_assets/dms-*.gz*; do
if [ -f "$file" ]; then
basename=$(basename "$file")
if [[ "$basename" == dms-distropkg-* ]]; then
@@ -247,12 +262,15 @@ jobs:
done
# Copy dankinstall binaries
cp _backend_assets/dankinstall-*.gz* _release_assets/
cp _core_assets/dankinstall-*.gz* _release_assets/
# Copy completions
cp _backend_assets/completion.* _release_assets/ 2>/dev/null || true
cp _core_assets/completion.* _release_assets/ 2>/dev/null || true
# Create QML source package (exclude build artifacts and git files)
# Copy root LICENSE and CONTRIBUTING.md to quickshell/ for packaging
cp LICENSE CONTRIBUTING.md quickshell/
# Tar the CONTENTS of quickshell/, not the directory itself
(cd quickshell && tar --exclude='.git' \
--exclude='.github' \
@@ -272,23 +290,28 @@ jobs:
tar -xzf _release_assets/dms-qml.tar.gz -C _temp_full/dms
# Add CLI binaries
if [ -f "_backend_assets/dms-${arch}.gz" ]; then
gunzip -c "_backend_assets/dms-${arch}.gz" > _temp_full/bin/dms
if [ -f "_core_assets/dms-${arch}.gz" ]; then
gunzip -c "_core_assets/dms-${arch}.gz" > _temp_full/bin/dms
chmod +x _temp_full/bin/dms
fi
if [ -f "_backend_assets/dms-distropkg-${arch}.gz" ]; then
gunzip -c "_backend_assets/dms-distropkg-${arch}.gz" > _temp_full/bin/dms-distropkg
if [ -f "_core_assets/dms-distropkg-${arch}.gz" ]; then
gunzip -c "_core_assets/dms-distropkg-${arch}.gz" > _temp_full/bin/dms-distropkg
chmod +x _temp_full/bin/dms-distropkg
fi
# Add shell completions
for completion in _backend_assets/completion.*; do
for completion in _core_assets/completion.*; do
if [ -f "$completion" ]; then
cp "$completion" _temp_full/completions/
fi
done
# Copy docs directory
if [ -d "docs" ]; then
cp -r docs _temp_full/
fi
# Create installation guide
cat > _temp_full/INSTALL.md << 'EOFINSTALL'
# DankMaterialShell Installation
@@ -337,8 +360,7 @@ jobs:
## Troubleshooting
- Run with verbose output: `quickshell -v -p ~/.config/quickshell/dms`
- Check logs in `~/.local/state/DankMaterialShell/`
- Run with verbose output: `DMS_LOG_LEVEL=debug dms run`
- Ensure all dependencies are installed
EOFINSTALL

View File

@@ -3,8 +3,8 @@ name: Update Vendor Hash
on:
push:
paths:
- "backend/go.mod"
- "backend/go.sum"
- "core/go.mod"
- "core/go.sum"
branches:
- master
@@ -20,14 +20,14 @@ jobs:
- name: Install Nix
uses: cachix/install-nix-action@v31
- name: Update vendorHash in backend/flake.nix
- name: Update vendorHash in flake.nix
run: |
set -euo pipefail
# Try to build and capture the expected hash from error message
echo "Attempting nix build to get new vendorHash..."
cd backend
if output=$(nix build .#dms-cli 2>&1); then
if output=$(nix build .#dmsCli 2>&1); then
echo "Build succeeded, no hash update needed"
exit 0
fi
@@ -58,7 +58,7 @@ jobs:
# Verify the build works with the new hash
echo "Verifying build with new vendorHash..."
nix build .#dms-cli
nix build .#dmsCli
echo "vendorHash updated successfully!"
@@ -66,12 +66,12 @@ jobs:
run: |
set -euo pipefail
if ! git diff --quiet backend/flake.nix; then
if ! git diff --quiet flake.nix; then
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add backend/flake.nix
git commit -m "flake: update vendorHash for go.mod changes"
git add flake.nix
git commit -m "nix: update vendorHash for go.mod changes"
for attempt in 1 2 3; do
if git push; then
@@ -86,5 +86,5 @@ jobs:
echo "Failed to push after retries" >&2
exit 1
else
echo "No changes to backend/flake.nix"
echo "No changes to flake.nix"
fi

1
.gitignore vendored
View File

@@ -27,7 +27,6 @@ qrc_*.cpp
ui_*.h
*.qmlc
*.jsc
Makefile*
*build-*
*.qm
*.prl

View File

@@ -23,7 +23,7 @@ DankMaterialShell is a complete desktop shell for [niri](https://github.com/YaLT
## Repository Structure
This is a monorepo containing both the shell interface and backend services:
This is a monorepo containing both the shell interface and the core backend services:
```
DankMaterialShell/
@@ -32,12 +32,14 @@ DankMaterialShell/
│ ├── Services/ # System integration (audio, network, bluetooth)
│ ├── Widgets/ # Reusable UI controls
│ └── Common/ # Shared resources and themes
├── backend/ # Go backend and CLI
├── core/ # Go backend and CLI
│ ├── cmd/ # dms CLI and dankinstall binaries
│ ├── internal/ # System integration, IPC, distro support
│ └── pkg/ # Shared packages
├── distro/ # Distribution packaging (Fedora RPM specs)
├── nix/ # NixOS/home-manager modules
├── distro/ # Distribution packaging
│ ├── fedora/ # Fedora RPM specs
│ ├── debian/ # Debian packaging
│ └── nix/ # NixOS/home-manager modules
└── flake.nix # Nix flake for declarative installation
```
@@ -135,15 +137,14 @@ dms plugins search # Browse plugin registry
See component-specific documentation:
- **[quickshell/](quickshell/)** - QML shell development, widgets, and modules
- **[backend/](backend/)** - Go backend, CLI tools, and system integration
- **[distro/](distro/)** - Distribution packaging
- **[nix/](nix/)** - NixOS and home-manager modules
- **[core/](core/)** - Go backend, CLI tools, and system integration
- **[distro/](distro/)** - Distribution packaging (Fedora, Debian, NixOS)
### Building from Source
**Backend:**
**Core + Dankinstall:**
```bash
cd backend
cd core
make # Build dms CLI
make dankinstall # Build installer
```

View File

@@ -1,14 +0,0 @@
package main
import "os/exec"
func commandExists(cmd string) bool {
_, err := exec.LookPath(cmd)
return err == nil
}
func isArchPackageInstalled(packageName string) bool {
cmd := exec.Command("pacman", "-Q", packageName)
err := cmd.Run()
return err == nil
}

48
core/.mockery.yml Normal file
View File

@@ -0,0 +1,48 @@
with-expecter: true
dir: "internal/mocks/{{.InterfaceDirRelative}}"
mockname: "Mock{{.InterfaceName}}"
outpkg: "{{.PackageName}}"
packages:
github.com/Wifx/gonetworkmanager/v2:
interfaces:
NetworkManager:
Device:
DeviceWireless:
AccessPoint:
Connection:
Settings:
ActiveConnection:
IP4Config:
net:
interfaces:
Conn:
github.com/AvengeMedia/danklinux/internal/plugins:
interfaces:
GitClient:
github.com/godbus/dbus/v5:
interfaces:
BusObject:
github.com/AvengeMedia/danklinux/internal/server/brightness:
config:
dir: "internal/mocks/brightness"
outpkg: mocks_brightness
interfaces:
DBusConn:
github.com/AvengeMedia/danklinux/internal/server/network:
config:
dir: "internal/mocks/network"
outpkg: mocks_network
interfaces:
Backend:
github.com/AvengeMedia/danklinux/internal/server/cups:
config:
dir: "internal/mocks/cups"
outpkg: mocks_cups
interfaces:
CUPSClientInterface:
github.com/AvengeMedia/DankMaterialShell/core/internal/server/evdev:
config:
dir: "internal/mocks/evdev"
outpkg: mocks_evdev
interfaces:
EvdevDevice:

157
core/Makefile Normal file
View File

@@ -0,0 +1,157 @@
BINARY_NAME=dms
BINARY_NAME_INSTALL=dankinstall
SOURCE_DIR=cmd/dms
SOURCE_DIR_INSTALL=cmd/dankinstall
BUILD_DIR=bin
PREFIX ?= /usr/local
INSTALL_DIR=$(PREFIX)/bin
GO=go
GOFLAGS=-ldflags="-s -w"
# Version and build info
VERSION=$(shell git describe --tags --always 2>/dev/null || echo "dev")
BUILD_TIME=$(shell date -u '+%Y-%m-%d_%H:%M:%S')
COMMIT=$(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILD_LDFLAGS=-ldflags='-s -w -X main.Version=$(VERSION) -X main.buildTime=$(BUILD_TIME) -X main.commit=$(COMMIT)'
# Architecture to build for dist target (amd64, arm64, or all)
ARCH ?= all
.PHONY: all build dankinstall dist clean install install-all install-dankinstall uninstall uninstall-all uninstall-dankinstall install-config uninstall-config test fmt vet deps help
# Default target
all: build
# Build the main binary (dms)
build:
@echo "Building $(BINARY_NAME)..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 $(GO) build $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) ./$(SOURCE_DIR)
@echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME)"
dankinstall:
@echo "Building $(BINARY_NAME_INSTALL)..."
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 $(GO) build $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME_INSTALL) ./$(SOURCE_DIR_INSTALL)
@echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME_INSTALL)"
# Build distro binaries for amd64 and arm64 (Linux only, no update/greeter support)
dist:
ifeq ($(ARCH),all)
@echo "Building $(BINARY_NAME) for distribution (amd64 and arm64)..."
@mkdir -p $(BUILD_DIR)
@echo "Building for linux/amd64..."
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 ./$(SOURCE_DIR)
@echo "Building for linux/arm64..."
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64 ./$(SOURCE_DIR)
@echo "Distribution builds complete:"
@echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64"
@echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64"
else
@echo "Building $(BINARY_NAME) for distribution ($(ARCH))..."
@mkdir -p $(BUILD_DIR)
@echo "Building for linux/$(ARCH)..."
CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) $(GO) build -tags distro_binary $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-$(ARCH) ./$(SOURCE_DIR)
@echo "Distribution build complete:"
@echo " $(BUILD_DIR)/$(BINARY_NAME)-linux-$(ARCH)"
endif
build-all: build dankinstall
install: build
@echo "Installing $(BINARY_NAME) to $(INSTALL_DIR)..."
@install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME) $(INSTALL_DIR)/$(BINARY_NAME)
@echo "Installation complete"
install-all: build-all
@echo "Installing $(BINARY_NAME) to $(INSTALL_DIR)..."
@install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME) $(INSTALL_DIR)/$(BINARY_NAME)
@echo "Installing $(BINARY_NAME_INSTALL) to $(INSTALL_DIR)..."
@install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME_INSTALL) $(INSTALL_DIR)/$(BINARY_NAME_INSTALL)
@echo "Installation complete"
install-dankinstall: dankinstall
@echo "Installing $(BINARY_NAME_INSTALL) to $(INSTALL_DIR)..."
@install -D -m 755 $(BUILD_DIR)/$(BINARY_NAME_INSTALL) $(INSTALL_DIR)/$(BINARY_NAME_INSTALL)
@echo "Installation complete"
uninstall:
@echo "Uninstalling $(BINARY_NAME) from $(INSTALL_DIR)..."
@rm -f $(INSTALL_DIR)/$(BINARY_NAME)
@echo "Uninstall complete"
uninstall-all:
@echo "Uninstalling $(BINARY_NAME) from $(INSTALL_DIR)..."
@rm -f $(INSTALL_DIR)/$(BINARY_NAME)
@echo "Uninstalling $(BINARY_NAME_INSTALL) from $(INSTALL_DIR)..."
@rm -f $(INSTALL_DIR)/$(BINARY_NAME_INSTALL)
@echo "Uninstall complete"
uninstall-dankinstall:
@echo "Uninstalling $(BINARY_NAME_INSTALL) from $(INSTALL_DIR)..."
@rm -f $(INSTALL_DIR)/$(BINARY_NAME_INSTALL)
@echo "Uninstall complete"
clean:
@echo "Cleaning build artifacts..."
@rm -rf $(BUILD_DIR)
@echo "Clean complete"
test:
@echo "Running tests..."
$(GO) test -v ./...
fmt:
@echo "Formatting Go code..."
$(GO) fmt ./...
vet:
@echo "Running go vet..."
$(GO) vet ./...
deps:
@echo "Updating dependencies..."
$(GO) mod tidy
$(GO) mod download
dev:
@echo "Building $(BINARY_NAME) for development..."
@mkdir -p $(BUILD_DIR)
$(GO) build -o $(BUILD_DIR)/$(BINARY_NAME) ./$(SOURCE_DIR)
@echo "Development build complete: $(BUILD_DIR)/$(BINARY_NAME)"
check-go:
@echo "Checking Go version..."
@go version | grep -E "go1\.(2[2-9]|[3-9][0-9])" > /dev/null || (echo "ERROR: Go 1.22 or higher required" && exit 1)
@echo "Go version OK"
version: check-go
@echo "Version: $(VERSION)"
@echo "Build Time: $(BUILD_TIME)"
@echo "Commit: $(COMMIT)"
help:
@echo "Available targets:"
@echo " all - Build the main binary (dms) (default)"
@echo " build - Build the main binary (dms)"
@echo " dankinstall - Build dankinstall binary"
@echo " dist - Build dms for linux amd64/arm64 (no update/greeter)"
@echo " Use ARCH=amd64 or ARCH=arm64 to build only one"
@echo " build-all - Build both binaries"
@echo " install - Install dms to $(INSTALL_DIR)"
@echo " install-all - Install both dms and dankinstall to $(INSTALL_DIR)"
@echo " install-dankinstall - Install only dankinstall to $(INSTALL_DIR)"
@echo " uninstall - Remove dms from $(INSTALL_DIR)"
@echo " uninstall-all - Remove both binaries from $(INSTALL_DIR)"
@echo " uninstall-dankinstall - Remove only dankinstall from $(INSTALL_DIR)"
@echo " clean - Clean build artifacts"
@echo " test - Run tests"
@echo " fmt - Format Go code"
@echo " vet - Run go vet"
@echo " deps - Update dependencies"
@echo " dev - Build with debug info"
@echo " check-go - Check Go version compatibility"
@echo " version - Show version information"
@echo " help - Show this help message"

View File

@@ -31,6 +31,7 @@ Distribution-aware installer with TUI for deploying DMS and compositor configura
- DDC/CI protocol - External monitor brightness control (like `ddcutil`)
- Backlight control - Internal display brightness via `login1` or sysfs
- LED control - Keyboard/device LED management
- evdev input monitoring - Keyboard state tracking (caps lock, etc.)
**Plugin System**
- Plugin registry integration

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -4,15 +4,15 @@ import (
"fmt"
"os"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/logger"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/tui"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/tui"
tea "github.com/charmbracelet/bubbletea"
)
var Version = "dev"
func main() {
fileLogger, err := logger.NewFileLogger()
fileLogger, err := log.NewFileLogger()
if err != nil {
fmt.Printf("Warning: Failed to create log file: %v\n", err)
fmt.Println("Continuing without file logging...")

View File

@@ -5,8 +5,8 @@ import (
"strings"
"time"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/server/brightness"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/brightness"
"github.com/spf13/cobra"
)

View File

@@ -4,9 +4,9 @@ import (
"fmt"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/plugins"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/server"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server"
"github.com/spf13/cobra"
)

View File

@@ -5,8 +5,8 @@ import (
"os"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/dank16"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/dank16"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/spf13/cobra"
)
@@ -25,6 +25,7 @@ func init() {
dank16Cmd.Flags().Bool("foot", false, "Output in Foot terminal format")
dank16Cmd.Flags().Bool("alacritty", false, "Output in Alacritty terminal format")
dank16Cmd.Flags().Bool("ghostty", false, "Output in Ghostty terminal format")
dank16Cmd.Flags().Bool("wezterm", false, "Output in Wezterm terminal format")
dank16Cmd.Flags().String("vscode-enrich", "", "Enrich existing VSCode theme file with terminal colors")
dank16Cmd.Flags().String("background", "", "Custom background color")
dank16Cmd.Flags().String("contrast", "dps", "Contrast algorithm: dps (Delta Phi Star, default) or wcag")
@@ -42,6 +43,7 @@ func runDank16(cmd *cobra.Command, args []string) {
isFoot, _ := cmd.Flags().GetBool("foot")
isAlacritty, _ := cmd.Flags().GetBool("alacritty")
isGhostty, _ := cmd.Flags().GetBool("ghostty")
isWezterm, _ := cmd.Flags().GetBool("wezterm")
vscodeEnrich, _ := cmd.Flags().GetString("vscode-enrich")
background, _ := cmd.Flags().GetString("background")
contrastAlgo, _ := cmd.Flags().GetString("contrast")
@@ -84,6 +86,8 @@ func runDank16(cmd *cobra.Command, args []string) {
fmt.Print(dank16.GenerateAlacrittyTheme(colors))
} else if isGhostty {
fmt.Print(dank16.GenerateGhosttyTheme(colors))
} else if isWezterm {
fmt.Print(dank16.GenerateWeztermTheme(colors))
} else {
fmt.Print(dank16.GenerateGhosttyTheme(colors))
}

View File

@@ -12,10 +12,10 @@ import (
"strings"
"time"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/distros"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/errdefs"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/version"
"github.com/AvengeMedia/DankMaterialShell/core/internal/distros"
"github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/version"
"github.com/spf13/cobra"
)

View File

@@ -8,8 +8,8 @@ import (
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/greeter"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/greeter"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/spf13/cobra"
)
@@ -277,9 +277,9 @@ func enableGreeter() error {
}
}
wrapperCmd := "dms-greeter"
if !commandExists("dms-greeter") {
wrapperCmd = "/usr/local/bin/dms-greeter"
wrapperCmd, err := findCommandPath("dms-greeter")
if err != nil {
return fmt.Errorf("dms-greeter not found in PATH. Please ensure it is installed and accessible")
}
compositorLower := strings.ToLower(selectedCompositor)
@@ -444,7 +444,7 @@ func checkGreeterStatus() error {
desc: "Session state",
},
{
source: filepath.Join(homeDir, ".cache", "quickshell", "dankshell", "dms-colors.json"),
source: filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json"),
target: filepath.Join(cacheDir, "colors.json"),
desc: "Color theme",
},

View File

@@ -5,9 +5,9 @@ import (
"fmt"
"os"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/keybinds"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/keybinds/providers"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds"
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds/providers"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/spf13/cobra"
)
@@ -34,9 +34,7 @@ var keybindsShowCmd = &cobra.Command{
}
func init() {
keybindsShowCmd.Flags().String("hyprland-path", "$HOME/.config/hypr", "Path to Hyprland config directory")
keybindsShowCmd.Flags().String("mangowc-path", "$HOME/.config/mango", "Path to MangoWC config directory")
keybindsShowCmd.Flags().String("sway-path", "$HOME/.config/sway", "Path to Sway config directory")
keybindsShowCmd.Flags().String("path", "", "Override config path for the provider")
keybindsCmd.AddCommand(keybindsListCmd)
keybindsCmd.AddCommand(keybindsShowCmd)
@@ -89,25 +87,34 @@ func runKeybindsList(cmd *cobra.Command, args []string) {
func runKeybindsShow(cmd *cobra.Command, args []string) {
providerName := args[0]
registry := keybinds.GetDefaultRegistry()
if providerName == "hyprland" {
hyprlandPath, _ := cmd.Flags().GetString("hyprland-path")
hyprlandProvider := providers.NewHyprlandProvider(hyprlandPath)
registry.Register(hyprlandProvider)
}
customPath, _ := cmd.Flags().GetString("path")
if customPath != "" {
var provider keybinds.Provider
switch providerName {
case "hyprland":
provider = providers.NewHyprlandProvider(customPath)
case "mangowc":
provider = providers.NewMangoWCProvider(customPath)
case "sway":
provider = providers.NewSwayProvider(customPath)
default:
log.Fatalf("Provider %s does not support custom path", providerName)
}
if providerName == "mangowc" {
mangowcPath, _ := cmd.Flags().GetString("mangowc-path")
mangowcProvider := providers.NewMangoWCProvider(mangowcPath)
registry.Register(mangowcProvider)
}
sheet, err := provider.GetCheatSheet()
if err != nil {
log.Fatalf("Error getting cheatsheet: %v", err)
}
if providerName == "sway" {
swayPath, _ := cmd.Flags().GetString("sway-path")
swayProvider := providers.NewSwayProvider(swayPath)
registry.Register(swayProvider)
output, err := json.MarshalIndent(sheet, "", " ")
if err != nil {
log.Fatalf("Error generating JSON: %v", err)
}
fmt.Fprintln(os.Stdout, string(output))
return
}
provider, err := registry.Get(providerName)

View File

@@ -7,10 +7,10 @@ import (
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/config"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/distros"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/dms"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
"github.com/AvengeMedia/DankMaterialShell/core/internal/distros"
"github.com/AvengeMedia/DankMaterialShell/core/internal/dms"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
)

View File

@@ -7,9 +7,9 @@ import (
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/config"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/spf13/cobra"
)

View File

@@ -5,7 +5,7 @@ package main
import (
"os"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
)
var Version = "dev"

View File

@@ -5,7 +5,7 @@ package main
import (
"os"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
)
var Version = "dev"

View File

@@ -12,8 +12,8 @@ import (
"syscall"
"time"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/server"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server"
)
var isSessionManaged bool

View File

@@ -3,7 +3,7 @@ package main
import (
"fmt"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/tui"
"github.com/AvengeMedia/DankMaterialShell/core/internal/tui"
"github.com/charmbracelet/lipgloss"
)

26
core/cmd/dms/utils.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"fmt"
"os/exec"
)
func commandExists(cmd string) bool {
_, err := exec.LookPath(cmd)
return err == nil
}
// findCommandPath returns the absolute path to a command in PATH
func findCommandPath(cmd string) (string, error) {
path, err := exec.LookPath(cmd)
if err != nil {
return "", fmt.Errorf("command '%s' not found in PATH", cmd)
}
return path, nil
}
func isArchPackageInstalled(packageName string) bool {
cmd := exec.Command("pacman", "-Q", packageName)
err := cmd.Run()
return err == nil
}

View File

@@ -1,65 +1,68 @@
module github.com/AvengeMedia/DankMaterialShell/backend
module github.com/AvengeMedia/DankMaterialShell/core
go 1.24.6
require (
github.com/Wifx/gonetworkmanager/v2 v2.2.0
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.6
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/log v0.4.2
github.com/fsnotify/fsnotify v1.9.0
github.com/godbus/dbus/v5 v5.1.0
github.com/spf13/cobra v1.9.1
github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83
github.com/spf13/cobra v1.10.1
github.com/stretchr/testify v1.11.1
github.com/yaslama/go-wayland/wayland v0.0.0-20250907155644-2874f32d9c34
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6
)
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/clipperhouse/displaywidth v0.5.0 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/cyphar/filepath-securejoin v0.6.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg/v2 v2.0.2 // indirect
github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/pjbgf/sha1cd v0.5.0 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/net v0.44.0 // indirect
github.com/stretchr/objx v0.5.3 // indirect
golang.org/x/crypto v0.44.0 // indirect
golang.org/x/net v0.47.0 // indirect
)
require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/colorprofile v0.3.3 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/charmbracelet/x/ansi v0.9.3 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/charmbracelet/x/ansi v0.11.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/go-git/go-git/v6 v6.0.0-20250929195514-145daf2492dd
github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/afero v1.15.0
github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0
golang.org/x/text v0.29.0 // indirect
golang.org/x/sys v0.38.0
golang.org/x/text v0.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -14,27 +14,33 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU=
github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
github.com/charmbracelet/colorprofile v0.3.3 h1:DjJzJtLP6/NZ8p7Cgjno0CKGr7wwRJGxWUwh2IyhfAI=
github.com/charmbracelet/colorprofile v0.3.3/go.mod h1:nB1FugsAbzq284eJcjfah2nhdSLppN2NqvfotkfRYP4=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/charmbracelet/x/ansi v0.11.0 h1:uuIVK7GIplwX6UBIz8S2TF8nkr7xRlygSsBRjSJqIvA=
github.com/charmbracelet/x/ansi v0.11.0/go.mod h1:uQt8bOrq/xgXjlGcFMc8U2WYbnxyjrKhnvTQluvfCaE=
github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4=
github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/clipperhouse/displaywidth v0.5.0 h1:AIG5vQaSL2EKqzt0M9JMnvNxOCRTKUc4vUnLWGgP89I=
github.com/clipperhouse/displaywidth v0.5.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -44,23 +50,29 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=
github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs=
github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30 h1:4KqVJTL5eanN8Sgg3BV6f2/QzfZEFbCd+rTak1fGRRA=
github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30/go.mod h1:snwvGrbywVFy2d6KJdQ132zapq4aLyzLMgpo79XdEfM=
github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0 h1:EC9n6hr6yKDoVJ6g7Ko523LbbceJfR0ohbOp809Fyf4=
github.com/go-git/go-billy/v6 v6.0.0-20251111123000-fb5ff8f3f0b0/go.mod h1:E3VhlS+AKkrq6ZNn1axE2/nDRJ87l1FJk9r5HT2vPX0=
github.com/go-git/go-git-fixtures/v5 v5.1.1 h1:OH8i1ojV9bWfr0ZfasfpgtUXQHQyVS8HXik/V1C099w=
github.com/go-git/go-git-fixtures/v5 v5.1.1/go.mod h1:Altk43lx3b1ks+dVoAG2300o5WWUnktvfY3VI6bcaXU=
github.com/go-git/go-git/v6 v6.0.0-20250929195514-145daf2492dd h1:30HEd5KKVM7GgMJ1GSNuYxuZXEg8Pdlngp6T51faxoc=
github.com/go-git/go-git/v6 v6.0.0-20250929195514-145daf2492dd/go.mod h1:lz8PQr/p79XpFq5ODVBwRJu5LnOF8Et7j95ehqmCMJU=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9 h1:SOFrnF9LCssC6q6Rb0084Bzg2aBYbe8QXv9xKGXmt/w=
github.com/go-git/go-git/v6 v6.0.0-20251112161705-8cc3e21f07a9/go.mod h1:0wtvm/JfPC9RFVEAP3ks0ec5h64/qmZkTTUE3pjz7Hc=
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83 h1:B+A58zGFuDrvEZpPN+yS6swJA0nzqgZvDzgl/OPyefU=
github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83/go.mod h1:iHAf8OIncO2gcQ8XOjS7CMJ2aPbX2Bs0wl5pZyanEqk=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
@@ -79,8 +91,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
@@ -91,7 +103,6 @@ github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
@@ -101,36 +112,33 @@ github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yaslama/go-wayland/wayland v0.0.0-20250907155644-2874f32d9c34 h1:iTAt1me6SBYsuzrl/CmrxtATPlOG/pVviosM3DhUdKE=
github.com/yaslama/go-wayland/wayland v0.0.0-20250907155644-2874f32d9c34/go.mod h1:jzmUN5lUAv2O8e63OvcauV4S30rIZ1BvF/PNYE37vDo=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0=
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

View File

@@ -9,7 +9,7 @@ import (
"strings"
"time"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
type ConfigDeployer struct {

View File

@@ -6,7 +6,7 @@ import (
"strings"
"testing"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

View File

@@ -124,3 +124,17 @@ func GenerateGhosttyTheme(colors []string) string {
}
return result.String()
}
func GenerateWeztermTheme(colors []string) string {
var result strings.Builder
labels := []string{"ansi", "brights"}
for j, label := range labels {
start := j * 8
colorSlice := make([]string, 8)
for i, color := range colors[start : start+8] {
colorSlice[i] = fmt.Sprintf("'%s'", color)
}
fmt.Fprintf(&result, "%s = [%s]\n", label, strings.Join(colorSlice, ", "))
}
return result.String()
}

View File

@@ -9,7 +9,7 @@ import (
"runtime"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
func init() {
@@ -321,7 +321,7 @@ func (a *ArchDistribution) InstallPackages(ctx context.Context, dependencies []d
return fmt.Errorf("failed to install prerequisites: %w", err)
}
systemPkgs, aurPkgs, manualPkgs := a.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
systemPkgs, aurPkgs, manualPkgs, variantMap := a.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
// Phase 3: System Packages
if len(systemPkgs) > 0 {
@@ -361,7 +361,7 @@ func (a *ArchDistribution) InstallPackages(ctx context.Context, dependencies []d
IsComplete: false,
LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")),
}
if err := a.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil {
if err := a.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install manual packages: %w", err)
}
}
@@ -387,7 +387,7 @@ func (a *ArchDistribution) InstallPackages(ctx context.Context, dependencies []d
return nil
}
func (a *ArchDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string, []string) {
func (a *ArchDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string, []string, map[string]deps.PackageVariant) {
systemPkgs := []string{}
aurPkgs := []string{}
manualPkgs := []string{}
@@ -410,7 +410,6 @@ func (a *ArchDistribution) categorizePackages(dependencies []deps.Dependency, wm
pkgInfo, exists := packageMap[dep.Name]
if !exists {
// If no mapping exists, treat as manual build
manualPkgs = append(manualPkgs, dep.Name)
continue
}
@@ -425,7 +424,7 @@ func (a *ArchDistribution) categorizePackages(dependencies []deps.Dependency, wm
}
}
return systemPkgs, aurPkgs, manualPkgs
return systemPkgs, aurPkgs, manualPkgs, variantMap
}
func (a *ArchDistribution) installSystemPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error {

View File

@@ -13,8 +13,8 @@ import (
"strings"
"time"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/version"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/version"
)
const forceQuickshellGit = false

View File

@@ -6,7 +6,7 @@ import (
"path/filepath"
"testing"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
func TestBaseDistribution_detectDMS_NotInstalled(t *testing.T) {

View File

@@ -6,7 +6,7 @@ import (
"os/exec"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
func init() {
@@ -209,7 +209,7 @@ func (d *DebianDistribution) InstallPrerequisites(ctx context.Context, sudoPassw
}
devToolsCmd := ExecSudoCommand(ctx, sudoPassword,
"apt-get install -y curl wget git cmake ninja-build pkg-config libxcb-cursor-dev libglib2.0-dev libpolkit-agent-1-dev")
"apt-get install -y curl wget git cmake ninja-build pkg-config libxcb-cursor-dev libglib2.0-dev libpolkit-agent-1-dev libjpeg-dev libpugixml-dev")
if err := d.runWithProgress(devToolsCmd, progressChan, PhasePrerequisites, 0.10, 0.12); err != nil {
return fmt.Errorf("failed to install development tools: %w", err)
}
@@ -238,7 +238,7 @@ func (d *DebianDistribution) InstallPackages(ctx context.Context, dependencies [
return fmt.Errorf("failed to install prerequisites: %w", err)
}
systemPkgs, manualPkgs := d.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
systemPkgs, manualPkgs, variantMap := d.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
if len(systemPkgs) > 0 {
progressChan <- InstallProgressMsg{
@@ -273,7 +273,7 @@ func (d *DebianDistribution) InstallPackages(ctx context.Context, dependencies [
IsComplete: false,
LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")),
}
if err := d.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil {
if err := d.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install manual packages: %w", err)
}
}
@@ -297,10 +297,15 @@ func (d *DebianDistribution) InstallPackages(ctx context.Context, dependencies [
return nil
}
func (d *DebianDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string) {
func (d *DebianDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string, map[string]deps.PackageVariant) {
systemPkgs := []string{}
manualPkgs := []string{}
variantMap := make(map[string]deps.PackageVariant)
for _, dep := range dependencies {
variantMap[dep.Name] = dep.Variant
}
packageMap := d.GetPackageMapping(wm)
for _, dep := range dependencies {
@@ -326,7 +331,7 @@ func (d *DebianDistribution) categorizePackages(dependencies []deps.Dependency,
}
}
return systemPkgs, manualPkgs
return systemPkgs, manualPkgs, variantMap
}
func (d *DebianDistribution) installAPTPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
@@ -513,7 +518,7 @@ func (d *DebianDistribution) installGhosttyDebian(ctx context.Context, sudoPassw
return nil
}
func (d *DebianDistribution) InstallManualPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
func (d *DebianDistribution) InstallManualPackages(ctx context.Context, packages []string, variantMap map[string]deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
if len(packages) == 0 {
return nil
}
@@ -527,7 +532,7 @@ func (d *DebianDistribution) InstallManualPackages(ctx context.Context, packages
return fmt.Errorf("failed to install ghostty: %w", err)
}
default:
if err := d.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, sudoPassword, progressChan); err != nil {
if err := d.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, variantMap, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install %s: %w", pkg, err)
}
}

View File

@@ -1,7 +1,7 @@
package distros
import (
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
// NewDependencyDetector creates a DependencyDetector for the specified distribution

View File

@@ -6,7 +6,7 @@ import (
"os/exec"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
func init() {
@@ -165,7 +165,7 @@ func (f *FedoraDistribution) GetPackageMappingWithVariants(wm deps.WindowManager
packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem}
case deps.WindowManagerNiri:
packages["niri"] = f.getNiriMapping(variants["niri"])
packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeCOPR, RepoURL: "yalter/niri"}
packages["xwayland-satellite"] = PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeSystem}
}
return packages
@@ -203,7 +203,7 @@ func (f *FedoraDistribution) getNiriMapping(variant deps.PackageVariant) Package
if variant == deps.VariantGit {
return PackageMapping{Name: "niri", Repository: RepoTypeCOPR, RepoURL: "yalter/niri-git"}
}
return PackageMapping{Name: "niri", Repository: RepoTypeCOPR, RepoURL: "yalter/niri"}
return PackageMapping{Name: "niri", Repository: RepoTypeSystem}
}
func (f *FedoraDistribution) detectXwaylandSatellite() deps.Dependency {
@@ -314,7 +314,7 @@ func (f *FedoraDistribution) InstallPackages(ctx context.Context, dependencies [
return fmt.Errorf("failed to install prerequisites: %w", err)
}
dnfPkgs, coprPkgs, manualPkgs := f.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
dnfPkgs, coprPkgs, manualPkgs, variantMap := f.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
// Phase 2: Enable COPR repositories
if len(coprPkgs) > 0 {
@@ -369,7 +369,7 @@ func (f *FedoraDistribution) InstallPackages(ctx context.Context, dependencies [
IsComplete: false,
LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")),
}
if err := f.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil {
if err := f.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install manual packages: %w", err)
}
}
@@ -395,7 +395,7 @@ func (f *FedoraDistribution) InstallPackages(ctx context.Context, dependencies [
return nil
}
func (f *FedoraDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string) {
func (f *FedoraDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string, map[string]deps.PackageVariant) {
dnfPkgs := []string{}
coprPkgs := []PackageMapping{}
manualPkgs := []string{}
@@ -432,7 +432,7 @@ func (f *FedoraDistribution) categorizePackages(dependencies []deps.Dependency,
}
}
return dnfPkgs, coprPkgs, manualPkgs
return dnfPkgs, coprPkgs, manualPkgs, variantMap
}
func (f *FedoraDistribution) extractPackageNames(packages []PackageMapping) []string {
@@ -483,7 +483,7 @@ func (f *FedoraDistribution) enableCOPRRepos(ctx context.Context, coprPkgs []Pac
}
priorityCmd := ExecSudoCommand(ctx, sudoPassword,
fmt.Sprintf("bash -c 'echo \"priority=1\" | tee -a %s' 2>&1", repoFile))
fmt.Sprintf("bash -c 'echo \"priority=1\" | tee -a %s'", repoFile))
priorityOutput, err := priorityCmd.CombinedOutput()
if err != nil {
f.logError("failed to set niri COPR repo priority", err)

View File

@@ -7,7 +7,7 @@ import (
"runtime"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
var GentooGlobalUseFlags = []string{
@@ -263,9 +263,9 @@ func (g *GentooDistribution) setGlobalUseFlags(ctx context.Context, sudoPassword
var cmd *exec.Cmd
if hasUse {
cmd = ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("sed -i 's/^USE=\"\\(.*\\)\"/USE=\"\\1 %s\"/' /etc/portage/make.conf; exit_code=$?; exit $exit_code", useFlags))
cmd = ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("sed -i 's/^USE=\"\\(.*\\)\"/USE=\"\\1 %s\"/' /etc/portage/make.conf", useFlags))
} else {
cmd = ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("bash -c \"echo 'USE=\\\"%s\\\"' >> /etc/portage/make.conf\"; exit_code=$?; exit $exit_code", useFlags))
cmd = ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("bash -c \"echo 'USE=\\\"%s\\\"' >> /etc/portage/make.conf\"", useFlags))
}
output, err := cmd.CombinedOutput()
@@ -343,8 +343,7 @@ func (g *GentooDistribution) InstallPrerequisites(ctx context.Context, sudoPassw
LogOutput: "Syncing Portage tree with emerge --sync",
}
syncCmd := ExecSudoCommand(ctx, sudoPassword,
"emerge --sync --quiet; exit_code=$?; exit $exit_code")
syncCmd := ExecSudoCommand(ctx, sudoPassword, "emerge --sync --quiet")
syncOutput, syncErr := syncCmd.CombinedOutput()
if syncErr != nil {
g.log(fmt.Sprintf("emerge --sync output: %s", string(syncOutput)))
@@ -365,7 +364,7 @@ func (g *GentooDistribution) InstallPrerequisites(ctx context.Context, sudoPassw
args := []string{"emerge", "--ask=n", "--quiet"}
args = append(args, missingPkgs...)
cmd := ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("%s; exit_code=$?; exit $exit_code", strings.Join(args, " ")))
cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " "))
output, err := cmd.CombinedOutput()
if err != nil {
g.logError("failed to install prerequisites", err)
@@ -392,7 +391,7 @@ func (g *GentooDistribution) InstallPackages(ctx context.Context, dependencies [
return fmt.Errorf("failed to install prerequisites: %w", err)
}
systemPkgs, guruPkgs, manualPkgs := g.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
systemPkgs, guruPkgs, manualPkgs, variantMap := g.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
g.log(fmt.Sprintf("CATEGORIZED PACKAGES: system=%d, guru=%d, manual=%d", len(systemPkgs), len(guruPkgs), len(manualPkgs)))
@@ -448,7 +447,7 @@ func (g *GentooDistribution) InstallPackages(ctx context.Context, dependencies [
IsComplete: false,
LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")),
}
if err := g.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil {
if err := g.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install manual packages: %w", err)
}
}
@@ -472,7 +471,7 @@ func (g *GentooDistribution) InstallPackages(ctx context.Context, dependencies [
return nil
}
func (g *GentooDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]PackageMapping, []PackageMapping, []string) {
func (g *GentooDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]PackageMapping, []PackageMapping, []string, map[string]deps.PackageVariant) {
systemPkgs := []PackageMapping{}
guruPkgs := []PackageMapping{}
manualPkgs := []string{}
@@ -509,7 +508,7 @@ func (g *GentooDistribution) categorizePackages(dependencies []deps.Dependency,
}
}
return systemPkgs, guruPkgs, manualPkgs
return systemPkgs, guruPkgs, manualPkgs, variantMap
}
func (g *GentooDistribution) extractPackageNames(packages []PackageMapping) []string {
@@ -553,7 +552,7 @@ func (g *GentooDistribution) installPortagePackages(ctx context.Context, package
CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")),
}
cmd := ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("%s || exit $?", strings.Join(args, " ")))
cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " "))
return g.runWithProgressTimeout(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60, 0)
}
@@ -745,6 +744,6 @@ func (g *GentooDistribution) installGURUPackages(ctx context.Context, packages [
CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")),
}
cmd := ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("%s || exit $?", strings.Join(args, " ")))
cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " "))
return g.runWithProgressTimeout(cmd, progressChan, PhaseAURPackages, 0.70, 0.85, 0)
}

View File

@@ -3,7 +3,7 @@ package distros
import (
"context"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
// DistroFamily represents a family of related distributions

View File

@@ -7,6 +7,8 @@ import (
"os/exec"
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
// ManualPackageInstaller provides methods for installing packages from source
@@ -42,8 +44,7 @@ func (m *ManualPackageInstaller) getLatestQuickshellTag(ctx context.Context) str
return m.parseLatestTagFromGitOutput(string(tagOutput))
}
// InstallManualPackages handles packages that need manual building
func (m *ManualPackageInstaller) InstallManualPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
func (m *ManualPackageInstaller) InstallManualPackages(ctx context.Context, packages []string, variantMap map[string]deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
if len(packages) == 0 {
return nil
}
@@ -51,9 +52,10 @@ func (m *ManualPackageInstaller) InstallManualPackages(ctx context.Context, pack
m.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", ")))
for _, pkg := range packages {
variant := variantMap[pkg]
switch pkg {
case "dms (DankMaterialShell)", "dms":
if err := m.installDankMaterialShell(ctx, sudoPassword, progressChan); err != nil {
if err := m.installDankMaterialShell(ctx, variant, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install DankMaterialShell: %w", err)
}
case "dgop":
@@ -69,7 +71,7 @@ func (m *ManualPackageInstaller) InstallManualPackages(ctx context.Context, pack
return fmt.Errorf("failed to install niri: %w", err)
}
case "quickshell":
if err := m.installQuickshell(ctx, sudoPassword, progressChan); err != nil {
if err := m.installQuickshell(ctx, variant, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install quickshell: %w", err)
}
case "hyprland":
@@ -294,7 +296,7 @@ func (m *ManualPackageInstaller) installNiri(ctx context.Context, sudoPassword s
return nil
}
func (m *ManualPackageInstaller) installQuickshell(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
func (m *ManualPackageInstaller) installQuickshell(ctx context.Context, variant deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
m.log("Installing quickshell from source...")
homeDir := os.Getenv("HOME")
@@ -322,10 +324,9 @@ func (m *ManualPackageInstaller) installQuickshell(ctx context.Context, sudoPass
}
var cloneCmd *exec.Cmd
if forceQuickshellGit {
if forceQuickshellGit || variant == deps.VariantGit {
cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir)
} else {
// Get latest tag from repository
latestTag := m.getLatestQuickshellTag(ctx)
if latestTag != "" {
m.log(fmt.Sprintf("Using latest quickshell tag: %s", latestTag))
@@ -391,8 +392,8 @@ func (m *ManualPackageInstaller) installQuickshell(ctx context.Context, sudoPass
CommandInfo: "sudo cmake --install build",
}
installCmd := ExecSudoCommand(ctx, sudoPassword,
fmt.Sprintf("cd %s && cmake --install build", tmpDir))
installCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install build")
installCmd.Dir = tmpDir
if err := installCmd.Run(); err != nil {
return fmt.Errorf("failed to install quickshell: %w", err)
}
@@ -454,8 +455,8 @@ func (m *ManualPackageInstaller) installHyprland(ctx context.Context, sudoPasswo
CommandInfo: "sudo make install",
}
installCmd := ExecSudoCommand(ctx, sudoPassword,
fmt.Sprintf("cd %s && make install", tmpDir))
installCmd := ExecSudoCommand(ctx, sudoPassword, "make install")
installCmd.Dir = tmpDir
if err := installCmd.Run(); err != nil {
return fmt.Errorf("failed to install Hyprland: %w", err)
}
@@ -477,6 +478,95 @@ func (m *ManualPackageInstaller) installHyprpicker(ctx context.Context, sudoPass
return fmt.Errorf("failed to create cache directory: %w", err)
}
// Install hyprutils first
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.05,
Step: "Building hyprutils dependency...",
IsComplete: false,
CommandInfo: "git clone https://github.com/hyprwm/hyprutils.git",
}
hyprutilsDir := filepath.Join(cacheDir, "hyprutils-build")
if err := os.MkdirAll(hyprutilsDir, 0755); err != nil {
return fmt.Errorf("failed to create hyprutils directory: %w", err)
}
defer os.RemoveAll(hyprutilsDir)
cloneUtilsCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/hyprwm/hyprutils.git", hyprutilsDir)
if err := cloneUtilsCmd.Run(); err != nil {
return fmt.Errorf("failed to clone hyprutils: %w", err)
}
configureUtilsCmd := exec.CommandContext(ctx, "cmake",
"--no-warn-unused-cli",
"-DCMAKE_BUILD_TYPE:STRING=Release",
"-DCMAKE_INSTALL_PREFIX:PATH=/usr",
"-DBUILD_TESTING=off",
"-S", ".",
"-B", "./build")
configureUtilsCmd.Dir = hyprutilsDir
configureUtilsCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir)
if err := m.runWithProgressStep(configureUtilsCmd, progressChan, PhaseSystemPackages, 0.05, 0.1, "Configuring hyprutils..."); err != nil {
return fmt.Errorf("failed to configure hyprutils: %w", err)
}
buildUtilsCmd := exec.CommandContext(ctx, "cmake", "--build", "./build", "--config", "Release", "--target", "all")
buildUtilsCmd.Dir = hyprutilsDir
buildUtilsCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir)
if err := m.runWithProgressStep(buildUtilsCmd, progressChan, PhaseSystemPackages, 0.1, 0.2, "Building hyprutils..."); err != nil {
return fmt.Errorf("failed to build hyprutils: %w", err)
}
installUtilsCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install ./build")
installUtilsCmd.Dir = hyprutilsDir
if err := installUtilsCmd.Run(); err != nil {
return fmt.Errorf("failed to install hyprutils: %w", err)
}
// Install hyprwayland-scanner
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.2,
Step: "Building hyprwayland-scanner dependency...",
IsComplete: false,
CommandInfo: "git clone https://github.com/hyprwm/hyprwayland-scanner.git",
}
scannerDir := filepath.Join(cacheDir, "hyprwayland-scanner-build")
if err := os.MkdirAll(scannerDir, 0755); err != nil {
return fmt.Errorf("failed to create scanner directory: %w", err)
}
defer os.RemoveAll(scannerDir)
cloneScannerCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/hyprwm/hyprwayland-scanner.git", scannerDir)
if err := cloneScannerCmd.Run(); err != nil {
return fmt.Errorf("failed to clone hyprwayland-scanner: %w", err)
}
configureScannerCmd := exec.CommandContext(ctx, "cmake",
"-DCMAKE_INSTALL_PREFIX=/usr",
"-B", "build")
configureScannerCmd.Dir = scannerDir
configureScannerCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir)
if err := m.runWithProgressStep(configureScannerCmd, progressChan, PhaseSystemPackages, 0.2, 0.25, "Configuring hyprwayland-scanner..."); err != nil {
return fmt.Errorf("failed to configure hyprwayland-scanner: %w", err)
}
buildScannerCmd := exec.CommandContext(ctx, "cmake", "--build", "build", "-j")
buildScannerCmd.Dir = scannerDir
buildScannerCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir)
if err := m.runWithProgressStep(buildScannerCmd, progressChan, PhaseSystemPackages, 0.25, 0.35, "Building hyprwayland-scanner..."); err != nil {
return fmt.Errorf("failed to build hyprwayland-scanner: %w", err)
}
installScannerCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install build")
installScannerCmd.Dir = scannerDir
if err := installScannerCmd.Run(); err != nil {
return fmt.Errorf("failed to install hyprwayland-scanner: %w", err)
}
// Now build hyprpicker
tmpDir := filepath.Join(cacheDir, "hyprpicker-build")
if err := os.MkdirAll(tmpDir, 0755); err != nil {
return fmt.Errorf("failed to create temp directory: %w", err)
@@ -485,7 +575,7 @@ func (m *ManualPackageInstaller) installHyprpicker(ctx context.Context, sudoPass
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.2,
Progress: 0.35,
Step: "Cloning hyprpicker repository...",
IsComplete: false,
CommandInfo: "git clone https://github.com/hyprwm/hyprpicker.git",
@@ -498,16 +588,39 @@ func (m *ManualPackageInstaller) installHyprpicker(ctx context.Context, sudoPass
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.4,
Step: "Building hyprpicker...",
Progress: 0.45,
Step: "Configuring hyprpicker build...",
IsComplete: false,
CommandInfo: "make all",
CommandInfo: "cmake -B build -S . -DCMAKE_BUILD_TYPE=Release",
}
buildCmd := exec.CommandContext(ctx, "make", "all")
configureCmd := exec.CommandContext(ctx, "cmake",
"--no-warn-unused-cli",
"-DCMAKE_BUILD_TYPE:STRING=Release",
"-DCMAKE_INSTALL_PREFIX:PATH=/usr",
"-S", ".",
"-B", "./build")
configureCmd.Dir = tmpDir
configureCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir)
output, err := configureCmd.CombinedOutput()
if err != nil {
m.log(fmt.Sprintf("cmake configure failed. Output:\n%s", string(output)))
return fmt.Errorf("failed to configure hyprpicker: %w\nCMake output:\n%s", err, string(output))
}
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.55,
Step: "Building hyprpicker...",
IsComplete: false,
CommandInfo: "cmake --build build --target hyprpicker",
}
buildCmd := exec.CommandContext(ctx, "cmake", "--build", "./build", "--config", "Release", "--target", "hyprpicker")
buildCmd.Dir = tmpDir
buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir)
if err := buildCmd.Run(); err != nil {
if err := m.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.55, 0.8, "Building hyprpicker..."); err != nil {
return fmt.Errorf("failed to build hyprpicker: %w", err)
}
@@ -517,11 +630,11 @@ func (m *ManualPackageInstaller) installHyprpicker(ctx context.Context, sudoPass
Step: "Installing hyprpicker...",
IsComplete: false,
NeedsSudo: true,
CommandInfo: "sudo make install",
CommandInfo: "sudo cmake --install build",
}
installCmd := ExecSudoCommand(ctx, sudoPassword,
fmt.Sprintf("cd %s && make install", tmpDir))
installCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install ./build")
installCmd.Dir = tmpDir
if err := installCmd.Run(); err != nil {
return fmt.Errorf("failed to install hyprpicker: %w", err)
}
@@ -642,22 +755,20 @@ func (m *ManualPackageInstaller) installMatugen(ctx context.Context, sudoPasswor
return nil
}
func (m *ManualPackageInstaller) installDankMaterialShell(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
func (m *ManualPackageInstaller) installDankMaterialShell(ctx context.Context, variant deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
m.log("Installing DankMaterialShell (DMS)...")
// Always install/update the DMS binary
if err := m.installDMSBinary(ctx, sudoPassword, progressChan); err != nil {
m.logError("Failed to install DMS binary", err)
}
// Handle DMS config - clone if missing, pull if exists
dmsPath := filepath.Join(os.Getenv("HOME"), ".config/quickshell/dms")
if _, err := os.Stat(dmsPath); os.IsNotExist(err) {
// Config doesn't exist, clone it
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.90,
Step: "Cloning DankMaterialShell config...",
Step: "Cloning DankMaterialShell...",
IsComplete: false,
CommandInfo: "git clone https://github.com/AvengeMedia/DankMaterialShell.git",
}
@@ -667,81 +778,88 @@ func (m *ManualPackageInstaller) installDankMaterialShell(ctx context.Context, s
return fmt.Errorf("failed to create quickshell config directory: %w", err)
}
tmpRepoPath := filepath.Join(os.TempDir(), "dms-clone-tmp")
defer os.RemoveAll(tmpRepoPath)
cloneCmd := exec.CommandContext(ctx, "git", "clone",
"https://github.com/AvengeMedia/DankMaterialShell.git", tmpRepoPath)
"https://github.com/AvengeMedia/DankMaterialShell.git", dmsPath)
if err := cloneCmd.Run(); err != nil {
return fmt.Errorf("failed to clone DankMaterialShell: %w", err)
}
if !forceDMSGit {
fetchCmd := exec.CommandContext(ctx, "git", "-C", tmpRepoPath, "fetch", "--tags")
if err := fetchCmd.Run(); err == nil {
tagCmd := exec.CommandContext(ctx, "git", "-C", tmpRepoPath, "describe", "--tags", "--abbrev=0", "origin/master")
if tagOutput, err := tagCmd.Output(); err == nil {
latestTag := strings.TrimSpace(string(tagOutput))
checkoutCmd := exec.CommandContext(ctx, "git", "-C", tmpRepoPath, "checkout", latestTag)
if err := checkoutCmd.Run(); err == nil {
m.log(fmt.Sprintf("Checked out latest tag: %s", latestTag))
}
}
}
if forceDMSGit || variant == deps.VariantGit {
m.log("Using git variant (master branch)")
return nil
}
srcPath := filepath.Join(tmpRepoPath, "quickshell")
cpCmd := exec.CommandContext(ctx, "cp", "-r", srcPath, dmsPath)
if err := cpCmd.Run(); err != nil {
return fmt.Errorf("failed to copy quickshell directory: %w", err)
tagCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "describe", "--tags", "--abbrev=0", "origin/master")
tagOutput, err := tagCmd.Output()
if err != nil {
m.log("Using default branch (no tags found)")
return nil
}
m.log("DankMaterialShell config cloned successfully")
} else {
// Config exists, update it
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.90,
Step: "Updating DankMaterialShell config...",
IsComplete: false,
CommandInfo: "Updating ~/.config/quickshell/dms",
latestTag := strings.TrimSpace(string(tagOutput))
checkoutCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "checkout", latestTag)
if err := checkoutCmd.Run(); err != nil {
m.logError(fmt.Sprintf("Failed to checkout tag %s", latestTag), err)
return nil
}
tmpRepoPath := filepath.Join(os.TempDir(), "dms-update-tmp")
defer os.RemoveAll(tmpRepoPath)
cloneCmd := exec.CommandContext(ctx, "git", "clone",
"https://github.com/AvengeMedia/DankMaterialShell.git", tmpRepoPath)
if err := cloneCmd.Run(); err != nil {
m.logError("Failed to clone DankMaterialShell for update", err)
} else {
if !forceDMSGit {
fetchCmd := exec.CommandContext(ctx, "git", "-C", tmpRepoPath, "fetch", "--tags")
if err := fetchCmd.Run(); err == nil {
tagCmd := exec.CommandContext(ctx, "git", "-C", tmpRepoPath, "describe", "--tags", "--abbrev=0", "origin/master")
if tagOutput, err := tagCmd.Output(); err == nil {
latestTag := strings.TrimSpace(string(tagOutput))
checkoutCmd := exec.CommandContext(ctx, "git", "-C", tmpRepoPath, "checkout", latestTag)
_ = checkoutCmd.Run()
}
}
}
srcPath := filepath.Join(tmpRepoPath, "quickshell")
rsyncCmd := exec.CommandContext(ctx, "rsync", "-a", "--delete", srcPath+"/", dmsPath+"/")
if err := rsyncCmd.Run(); err != nil {
cpCmd := exec.CommandContext(ctx, "cp", "-rf", srcPath+"/.", dmsPath+"/")
if err := cpCmd.Run(); err != nil {
m.logError("Failed to update DankMaterialShell config", err)
} else {
m.log("DankMaterialShell config updated successfully")
}
} else {
m.log("DankMaterialShell config updated successfully")
}
}
m.log(fmt.Sprintf("Checked out latest tag: %s", latestTag))
m.log("DankMaterialShell cloned successfully")
return nil
}
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.90,
Step: "Updating DankMaterialShell...",
IsComplete: false,
CommandInfo: "Updating ~/.config/quickshell/dms",
}
fetchCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "fetch", "origin", "--tags", "--force")
if err := fetchCmd.Run(); err != nil {
m.logError("Failed to fetch updates", err)
return nil
}
if forceDMSGit || variant == deps.VariantGit {
branchCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "rev-parse", "--abbrev-ref", "HEAD")
branchOutput, err := branchCmd.Output()
if err != nil {
m.logError("Failed to get current branch", err)
return nil
}
branch := strings.TrimSpace(string(branchOutput))
if branch == "" {
branch = "master"
}
pullCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "pull", "origin", branch)
if err := pullCmd.Run(); err != nil {
m.logError("Failed to pull updates", err)
return nil
}
m.log("DankMaterialShell updated successfully (git variant)")
return nil
}
latestTagCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "describe", "--tags", "--abbrev=0", "origin/master")
tagOutput, err := latestTagCmd.Output()
if err != nil {
m.logError("Failed to get latest tag", err)
return nil
}
latestTag := strings.TrimSpace(string(tagOutput))
checkoutCmd := exec.CommandContext(ctx, "git", "-C", dmsPath, "checkout", latestTag)
if err := checkoutCmd.Run(); err != nil {
m.logError(fmt.Sprintf("Failed to checkout tag %s", latestTag), err)
return nil
}
m.log(fmt.Sprintf("Updated to tag: %s", latestTag))
return nil
}

View File

@@ -6,7 +6,7 @@ import (
"os/exec"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
func init() {
@@ -308,7 +308,7 @@ func (n *NixOSDistribution) InstallPackages(ctx context.Context, dependencies []
return fmt.Errorf("failed to install prerequisites: %w", err)
}
nixpkgsPkgs, flakePkgs := n.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
nixpkgsPkgs, flakePkgs, _ := n.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
// Phase 2: Nixpkgs Packages
if len(nixpkgsPkgs) > 0 {
@@ -362,10 +362,15 @@ func (n *NixOSDistribution) InstallPackages(ctx context.Context, dependencies []
return nil
}
func (n *NixOSDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string) {
func (n *NixOSDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string, map[string]deps.PackageVariant) {
nixpkgsPkgs := []string{}
flakePkgs := []string{}
variantMap := make(map[string]deps.PackageVariant)
for _, dep := range dependencies {
variantMap[dep.Name] = dep.Variant
}
packageMap := n.GetPackageMapping(wm)
for _, dep := range dependencies {
@@ -391,7 +396,7 @@ func (n *NixOSDistribution) categorizePackages(dependencies []deps.Dependency, w
}
}
return nixpkgsPkgs, flakePkgs
return nixpkgsPkgs, flakePkgs, variantMap
}
func (n *NixOSDistribution) installNixpkgsPackages(ctx context.Context, packages []string, progressChan chan<- InstallProgressMsg) error {

View File

@@ -8,7 +8,7 @@ import (
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
func init() {
@@ -294,7 +294,7 @@ func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies
return fmt.Errorf("failed to install prerequisites: %w", err)
}
systemPkgs, manualPkgs := o.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
systemPkgs, manualPkgs, variantMap := o.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
// Phase 2: System Packages (Zypper)
if len(systemPkgs) > 0 {
@@ -320,7 +320,7 @@ func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies
IsComplete: false,
LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")),
}
if err := o.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil {
if err := o.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install manual packages: %w", err)
}
}
@@ -346,7 +346,7 @@ func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies
return nil
}
func (o *OpenSUSEDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string) {
func (o *OpenSUSEDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []string, map[string]deps.PackageVariant) {
systemPkgs := []string{}
manualPkgs := []string{}
@@ -380,7 +380,7 @@ func (o *OpenSUSEDistribution) categorizePackages(dependencies []deps.Dependency
}
}
return systemPkgs, manualPkgs
return systemPkgs, manualPkgs, variantMap
}
func (o *OpenSUSEDistribution) installZypperPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
@@ -406,8 +406,7 @@ func (o *OpenSUSEDistribution) installZypperPackages(ctx context.Context, packag
return o.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.40, 0.60)
}
// installQuickshell overrides the base implementation to set openSUSE-specific CFLAGS
func (o *OpenSUSEDistribution) installQuickshell(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
func (o *OpenSUSEDistribution) installQuickshell(ctx context.Context, variant deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
o.log("Installing quickshell from source (with openSUSE-specific build flags)...")
homeDir := os.Getenv("HOME")
@@ -435,10 +434,9 @@ func (o *OpenSUSEDistribution) installQuickshell(ctx context.Context, sudoPasswo
}
var cloneCmd *exec.Cmd
if forceQuickshellGit {
if forceQuickshellGit || variant == deps.VariantGit {
cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir)
} else {
// Get latest tag from repository
latestTag := o.getLatestQuickshellTag(ctx)
if latestTag != "" {
o.log(fmt.Sprintf("Using latest quickshell tag: %s", latestTag))
@@ -524,8 +522,8 @@ func (o *OpenSUSEDistribution) installQuickshell(ctx context.Context, sudoPasswo
CommandInfo: "sudo cmake --install build",
}
installCmd := ExecSudoCommand(ctx, sudoPassword,
fmt.Sprintf("cd %s && cmake --install build", tmpDir))
installCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install build")
installCmd.Dir = tmpDir
if err := installCmd.Run(); err != nil {
return fmt.Errorf("failed to install quickshell: %w", err)
}
@@ -573,15 +571,13 @@ func (o *OpenSUSEDistribution) installRust(ctx context.Context, sudoPassword str
return nil
}
// InstallManualPackages overrides the base implementation to use openSUSE-specific builds
func (o *OpenSUSEDistribution) InstallManualPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
func (o *OpenSUSEDistribution) InstallManualPackages(ctx context.Context, packages []string, variantMap map[string]deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
if len(packages) == 0 {
return nil
}
o.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", ")))
// Install Rust if needed for matugen
for _, pkg := range packages {
if pkg == "matugen" {
if err := o.installRust(ctx, sudoPassword, progressChan); err != nil {
@@ -592,13 +588,13 @@ func (o *OpenSUSEDistribution) InstallManualPackages(ctx context.Context, packag
}
for _, pkg := range packages {
variant := variantMap[pkg]
if pkg == "quickshell" {
if err := o.installQuickshell(ctx, sudoPassword, progressChan); err != nil {
if err := o.installQuickshell(ctx, variant, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install quickshell: %w", err)
}
} else {
// Use the base ManualPackageInstaller for other packages
if err := o.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, sudoPassword, progressChan); err != nil {
if err := o.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, variantMap, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install %s: %w", pkg, err)
}
}

View File

@@ -8,7 +8,7 @@ import (
"strconv"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/errdefs"
"github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs"
)
// DistroInfo contains basic information about a distribution

View File

@@ -8,7 +8,7 @@ import (
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)
func init() {
@@ -263,7 +263,7 @@ func (u *UbuntuDistribution) InstallPackages(ctx context.Context, dependencies [
return fmt.Errorf("failed to install prerequisites: %w", err)
}
systemPkgs, ppaPkgs, manualPkgs := u.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
systemPkgs, ppaPkgs, manualPkgs, variantMap := u.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)
// Phase 2: Enable PPA repositories
if len(ppaPkgs) > 0 {
@@ -329,7 +329,7 @@ func (u *UbuntuDistribution) InstallPackages(ctx context.Context, dependencies [
IsComplete: false,
LogOutput: fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")),
}
if err := u.InstallManualPackages(ctx, manualPkgs, sudoPassword, progressChan); err != nil {
if err := u.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install manual packages: %w", err)
}
}
@@ -355,11 +355,16 @@ func (u *UbuntuDistribution) InstallPackages(ctx context.Context, dependencies [
return nil
}
func (u *UbuntuDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string) {
func (u *UbuntuDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string, map[string]deps.PackageVariant) {
systemPkgs := []string{}
ppaPkgs := []PackageMapping{}
manualPkgs := []string{}
variantMap := make(map[string]deps.PackageVariant)
for _, dep := range dependencies {
variantMap[dep.Name] = dep.Variant
}
packageMap := u.GetPackageMapping(wm)
for _, dep := range dependencies {
@@ -387,7 +392,7 @@ func (u *UbuntuDistribution) categorizePackages(dependencies []deps.Dependency,
}
}
return systemPkgs, ppaPkgs, manualPkgs
return systemPkgs, ppaPkgs, manualPkgs, variantMap
}
func (u *UbuntuDistribution) extractPackageNames(packages []PackageMapping) []string {
@@ -729,8 +734,7 @@ func (u *UbuntuDistribution) installGhosttyUbuntu(ctx context.Context, sudoPassw
return nil
}
// Override InstallManualPackages for Ubuntu to handle Ubuntu-specific installations
func (u *UbuntuDistribution) InstallManualPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
func (u *UbuntuDistribution) InstallManualPackages(ctx context.Context, packages []string, variantMap map[string]deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
if len(packages) == 0 {
return nil
}
@@ -744,8 +748,7 @@ func (u *UbuntuDistribution) InstallManualPackages(ctx context.Context, packages
return fmt.Errorf("failed to install ghostty: %w", err)
}
default:
// Use the base ManualPackageInstaller for other packages
if err := u.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, sudoPassword, progressChan); err != nil {
if err := u.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, variantMap, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install %s: %w", pkg, err)
}
}

View File

@@ -6,7 +6,7 @@ import (
"os/exec"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
tea "github.com/charmbracelet/bubbletea"
)

View File

@@ -5,9 +5,9 @@ import (
"os"
"os/exec"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/config"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/distros"
"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/distros"
)
type Detector struct {

View File

@@ -4,7 +4,7 @@ import (
"os/exec"
"time"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
tea "github.com/charmbracelet/bubbletea"
)

View File

@@ -9,9 +9,9 @@ import (
"strings"
"time"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/distros"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/greeter"
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
"github.com/AvengeMedia/DankMaterialShell/core/internal/distros"
"github.com/AvengeMedia/DankMaterialShell/core/internal/greeter"
tea "github.com/charmbracelet/bubbletea"
)

View File

@@ -3,7 +3,7 @@ package dms
import (
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/plugins"
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
tea "github.com/charmbracelet/bubbletea"
)

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/tui"
"github.com/AvengeMedia/DankMaterialShell/core/internal/tui"
"github.com/charmbracelet/lipgloss"
)

View File

@@ -9,8 +9,8 @@ import (
"path/filepath"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/config"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/distros"
"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
"github.com/AvengeMedia/DankMaterialShell/core/internal/distros"
)
// DetectDMSPath checks for DMS installation following XDG Base Directory specification
@@ -227,6 +227,7 @@ func SetupParentDirectoryACLs(logFunc func(string), sudoPassword string) error {
{filepath.Join(homeDir, ".local"), ".local directory"},
{filepath.Join(homeDir, ".cache"), ".cache directory"},
{filepath.Join(homeDir, ".local", "state"), ".local/state directory"},
{filepath.Join(homeDir, ".local", "share"), ".local/share directory"},
}
logFunc("\nSetting up parent directory ACLs for greeter user access...")
@@ -239,8 +240,8 @@ func SetupParentDirectoryACLs(logFunc func(string), sudoPassword string) error {
}
}
// Set ACL to allow greeter user execute (traverse) permission
if err := runSudoCmd(sudoPassword, "setfacl", "-m", "u:greeter:x", dir.path); err != nil {
// Set ACL to allow greeter user read+execute permission (for session discovery)
if err := runSudoCmd(sudoPassword, "setfacl", "-m", "u:greeter:rx", dir.path); err != nil {
logFunc(fmt.Sprintf("⚠ Warning: Failed to set ACL on %s: %v", dir.desc, err))
logFunc(fmt.Sprintf(" You may need to run manually: setfacl -m u:greeter:x %s", dir.path))
continue
@@ -287,6 +288,8 @@ func SetupDMSGroup(logFunc func(string), sudoPassword string) error {
{filepath.Join(homeDir, ".local", "state", "DankMaterialShell"), "DankMaterialShell state"},
{filepath.Join(homeDir, ".cache", "quickshell"), "quickshell cache"},
{filepath.Join(homeDir, ".config", "quickshell"), "quickshell config"},
{filepath.Join(homeDir, ".local", "share", "wayland-sessions"), "wayland sessions"},
{filepath.Join(homeDir, ".local", "share", "xsessions"), "xsessions"},
}
for _, dir := range configDirs {
@@ -342,7 +345,7 @@ func SyncDMSConfigs(dmsPath string, logFunc func(string), sudoPassword string) e
desc: "state (wallpaper configuration)",
},
{
source: filepath.Join(homeDir, ".cache", "quickshell", "dankshell", "dms-colors.json"),
source: filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json"),
target: filepath.Join(cacheDir, "colors.json"),
desc: "wallpaper based theming",
},

View File

@@ -4,8 +4,7 @@ import (
"fmt"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/hyprland"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/keybinds"
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds"
)
type HyprlandProvider struct {
@@ -26,7 +25,7 @@ func (h *HyprlandProvider) Name() string {
}
func (h *HyprlandProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
section, err := hyprland.ParseKeys(h.configPath)
section, err := ParseHyprlandKeys(h.configPath)
if err != nil {
return nil, fmt.Errorf("failed to parse hyprland config: %w", err)
}
@@ -41,7 +40,7 @@ func (h *HyprlandProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
}, nil
}
func (h *HyprlandProvider) convertSection(section *hyprland.Section, subcategory string, categorizedBinds map[string][]keybinds.Keybind) {
func (h *HyprlandProvider) convertSection(section *HyprlandSection, subcategory string, categorizedBinds map[string][]keybinds.Keybind) {
currentSubcat := subcategory
if section.Name != "" {
currentSubcat = section.Name
@@ -86,7 +85,7 @@ func (h *HyprlandProvider) categorizeByDispatcher(dispatcher string) string {
}
}
func (h *HyprlandProvider) convertKeybind(kb *hyprland.KeyBinding, subcategory string) keybinds.Keybind {
func (h *HyprlandProvider) convertKeybind(kb *HyprlandKeyBinding, subcategory string) keybinds.Keybind {
key := h.formatKey(kb)
desc := kb.Comment
@@ -108,7 +107,7 @@ func (h *HyprlandProvider) generateDescription(dispatcher, params string) string
return dispatcher
}
func (h *HyprlandProvider) formatKey(kb *hyprland.KeyBinding) string {
func (h *HyprlandProvider) formatKey(kb *HyprlandKeyBinding) string {
parts := make([]string, 0, len(kb.Mods)+1)
parts = append(parts, kb.Mods...)
parts = append(parts, kb.Key)

View File

@@ -1,4 +1,4 @@
package hyprland
package providers
import (
"os"
@@ -15,7 +15,7 @@ const (
var ModSeparators = []rune{'+', ' '}
type KeyBinding struct {
type HyprlandKeyBinding struct {
Mods []string `json:"mods"`
Key string `json:"key"`
Dispatcher string `json:"dispatcher"`
@@ -23,25 +23,25 @@ type KeyBinding struct {
Comment string `json:"comment"`
}
type Section struct {
Children []Section `json:"children"`
Keybinds []KeyBinding `json:"keybinds"`
Name string `json:"name"`
type HyprlandSection struct {
Children []HyprlandSection `json:"children"`
Keybinds []HyprlandKeyBinding `json:"keybinds"`
Name string `json:"name"`
}
type Parser struct {
type HyprlandParser struct {
contentLines []string
readingLine int
}
func NewParser() *Parser {
return &Parser{
func NewHyprlandParser() *HyprlandParser {
return &HyprlandParser{
contentLines: []string{},
readingLine: 0,
}
}
func (p *Parser) ReadContent(directory string) error {
func (p *HyprlandParser) ReadContent(directory string) error {
expandedDir := os.ExpandEnv(directory)
expandedDir = filepath.Clean(expandedDir)
if strings.HasPrefix(expandedDir, "~") {
@@ -87,7 +87,7 @@ func (p *Parser) ReadContent(directory string) error {
return nil
}
func autogenerateComment(dispatcher, params string) string {
func hyprlandAutogenerateComment(dispatcher, params string) string {
switch dispatcher {
case "resizewindow":
return "Resize window"
@@ -196,7 +196,7 @@ func autogenerateComment(dispatcher, params string) string {
}
}
func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
func (p *HyprlandParser) getKeybindAtLine(lineNumber int) *HyprlandKeyBinding {
line := p.contentLines[lineNumber]
parts := strings.SplitN(line, "=", 2)
if len(parts) < 2 {
@@ -232,7 +232,7 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
return nil
}
} else {
comment = autogenerateComment(dispatcher, params)
comment = hyprlandAutogenerateComment(dispatcher, params)
}
var modList []string
@@ -256,7 +256,7 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
}
}
return &KeyBinding{
return &HyprlandKeyBinding{
Mods: modList,
Key: key,
Dispatcher: dispatcher,
@@ -265,7 +265,7 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
}
}
func (p *Parser) getBindsRecursive(currentContent *Section, scope int) *Section {
func (p *HyprlandParser) getBindsRecursive(currentContent *HyprlandSection, scope int) *HyprlandSection {
titleRegex := regexp.MustCompile(TitleRegex)
for p.readingLine < len(p.contentLines) {
@@ -283,9 +283,9 @@ func (p *Parser) getBindsRecursive(currentContent *Section, scope int) *Section
sectionName := strings.TrimSpace(line[headingScope+1:])
p.readingLine++
childSection := &Section{
Children: []Section{},
Keybinds: []KeyBinding{},
childSection := &HyprlandSection{
Children: []HyprlandSection{},
Keybinds: []HyprlandKeyBinding{},
Name: sectionName,
}
result := p.getBindsRecursive(childSection, headingScope)
@@ -312,18 +312,18 @@ func (p *Parser) getBindsRecursive(currentContent *Section, scope int) *Section
return currentContent
}
func (p *Parser) ParseKeys() *Section {
func (p *HyprlandParser) ParseKeys() *HyprlandSection {
p.readingLine = 0
rootSection := &Section{
Children: []Section{},
Keybinds: []KeyBinding{},
rootSection := &HyprlandSection{
Children: []HyprlandSection{},
Keybinds: []HyprlandKeyBinding{},
Name: "",
}
return p.getBindsRecursive(rootSection, 0)
}
func ParseKeys(path string) (*Section, error) {
parser := NewParser()
func ParseHyprlandKeys(path string) (*HyprlandSection, error) {
parser := NewHyprlandParser()
if err := parser.ReadContent(path); err != nil {
return nil, err
}

View File

@@ -1,4 +1,4 @@
package hyprland
package providers
import (
"os"
@@ -6,7 +6,7 @@ import (
"testing"
)
func TestAutogenerateComment(t *testing.T) {
func TestHyprlandAutogenerateComment(t *testing.T) {
tests := []struct {
dispatcher string
params string
@@ -51,25 +51,25 @@ func TestAutogenerateComment(t *testing.T) {
for _, tt := range tests {
t.Run(tt.dispatcher+"_"+tt.params, func(t *testing.T) {
result := autogenerateComment(tt.dispatcher, tt.params)
result := hyprlandAutogenerateComment(tt.dispatcher, tt.params)
if result != tt.expected {
t.Errorf("autogenerateComment(%q, %q) = %q, want %q",
t.Errorf("hyprlandAutogenerateComment(%q, %q) = %q, want %q",
tt.dispatcher, tt.params, result, tt.expected)
}
})
}
}
func TestGetKeybindAtLine(t *testing.T) {
func TestHyprlandGetKeybindAtLine(t *testing.T) {
tests := []struct {
name string
line string
expected *KeyBinding
expected *HyprlandKeyBinding
}{
{
name: "basic_keybind",
line: "bind = SUPER, Q, killactive",
expected: &KeyBinding{
expected: &HyprlandKeyBinding{
Mods: []string{"SUPER"},
Key: "Q",
Dispatcher: "killactive",
@@ -80,7 +80,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_with_params",
line: "bind = SUPER, left, movefocus, l",
expected: &KeyBinding{
expected: &HyprlandKeyBinding{
Mods: []string{"SUPER"},
Key: "left",
Dispatcher: "movefocus",
@@ -91,7 +91,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_with_comment",
line: "bind = SUPER, T, exec, kitty # Open terminal",
expected: &KeyBinding{
expected: &HyprlandKeyBinding{
Mods: []string{"SUPER"},
Key: "T",
Dispatcher: "exec",
@@ -107,7 +107,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_multiple_mods",
line: "bind = SUPER+SHIFT, F, fullscreen, 0",
expected: &KeyBinding{
expected: &HyprlandKeyBinding{
Mods: []string{"SUPER", "SHIFT"},
Key: "F",
Dispatcher: "fullscreen",
@@ -118,7 +118,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_no_mods",
line: "bind = , Print, exec, screenshot",
expected: &KeyBinding{
expected: &HyprlandKeyBinding{
Mods: []string{},
Key: "Print",
Dispatcher: "exec",
@@ -130,7 +130,7 @@ func TestGetKeybindAtLine(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := NewParser()
parser := NewHyprlandParser()
parser.contentLines = []string{tt.line}
result := parser.getKeybindAtLine(0)
@@ -171,7 +171,7 @@ func TestGetKeybindAtLine(t *testing.T) {
}
}
func TestParseKeysWithSections(t *testing.T) {
func TestHyprlandParseKeysWithSections(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "hyprland.conf")
@@ -191,9 +191,9 @@ bind = SUPER, T, exec, kitty # Terminal
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseKeys(tmpDir)
section, err := ParseHyprlandKeys(tmpDir)
if err != nil {
t.Fatalf("ParseKeys failed: %v", err)
t.Fatalf("ParseHyprlandKeys failed: %v", err)
}
if len(section.Children) != 2 {
@@ -236,7 +236,7 @@ bind = SUPER, T, exec, kitty # Terminal
}
}
func TestParseKeysWithCommentBinds(t *testing.T) {
func TestHyprlandParseKeysWithCommentBinds(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "test.conf")
@@ -249,9 +249,9 @@ bind = SUPER, B, exec, app2
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseKeys(tmpDir)
section, err := ParseHyprlandKeys(tmpDir)
if err != nil {
t.Fatalf("ParseKeys failed: %v", err)
t.Fatalf("ParseHyprlandKeys failed: %v", err)
}
if len(section.Keybinds) != 3 {
@@ -269,7 +269,7 @@ bind = SUPER, B, exec, app2
}
}
func TestReadContentMultipleFiles(t *testing.T) {
func TestHyprlandReadContentMultipleFiles(t *testing.T) {
tmpDir := t.TempDir()
file1 := filepath.Join(tmpDir, "a.conf")
@@ -285,7 +285,7 @@ func TestReadContentMultipleFiles(t *testing.T) {
t.Fatalf("Failed to write file2: %v", err)
}
parser := NewParser()
parser := NewHyprlandParser()
if err := parser.ReadContent(tmpDir); err != nil {
t.Fatalf("ReadContent failed: %v", err)
}
@@ -296,7 +296,7 @@ func TestReadContentMultipleFiles(t *testing.T) {
}
}
func TestReadContentErrors(t *testing.T) {
func TestHyprlandReadContentErrors(t *testing.T) {
tests := []struct {
name string
path string
@@ -313,7 +313,7 @@ func TestReadContentErrors(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := ParseKeys(tt.path)
_, err := ParseHyprlandKeys(tt.path)
if err == nil {
t.Error("Expected error, got nil")
}
@@ -321,7 +321,7 @@ func TestReadContentErrors(t *testing.T) {
}
}
func TestReadContentWithTildeExpansion(t *testing.T) {
func TestHyprlandReadContentWithTildeExpansion(t *testing.T) {
homeDir, err := os.UserHomeDir()
if err != nil {
t.Skip("Cannot get home directory")
@@ -343,7 +343,7 @@ func TestReadContentWithTildeExpansion(t *testing.T) {
t.Skip("Cannot create relative path")
}
parser := NewParser()
parser := NewHyprlandParser()
tildePathMatch := "~/" + relPath
err = parser.ReadContent(tildePathMatch)
@@ -352,8 +352,8 @@ func TestReadContentWithTildeExpansion(t *testing.T) {
}
}
func TestKeybindWithParamsContainingCommas(t *testing.T) {
parser := NewParser()
func TestHyprlandKeybindWithParamsContainingCommas(t *testing.T) {
parser := NewHyprlandParser()
parser.contentLines = []string{"bind = SUPER, R, exec, notify-send 'Title' 'Message, with comma'"}
result := parser.getKeybindAtLine(0)
@@ -368,7 +368,7 @@ func TestKeybindWithParamsContainingCommas(t *testing.T) {
}
}
func TestEmptyAndCommentLines(t *testing.T) {
func TestHyprlandEmptyAndCommentLines(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "test.conf")
@@ -385,9 +385,9 @@ bind = SUPER, T, exec, kitty
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseKeys(tmpDir)
section, err := ParseHyprlandKeys(tmpDir)
if err != nil {
t.Fatalf("ParseKeys failed: %v", err)
t.Fatalf("ParseHyprlandKeys failed: %v", err)
}
if len(section.Keybinds) != 2 {

View File

@@ -6,7 +6,7 @@ import (
"os"
"path/filepath"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/keybinds"
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds"
)
type JSONFileProvider struct {

View File

@@ -4,8 +4,7 @@ import (
"fmt"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/keybinds"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/mangowc"
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds"
)
type MangoWCProvider struct {
@@ -26,7 +25,7 @@ func (m *MangoWCProvider) Name() string {
}
func (m *MangoWCProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
keybinds_list, err := mangowc.ParseKeys(m.configPath)
keybinds_list, err := ParseMangoWCKeys(m.configPath)
if err != nil {
return nil, fmt.Errorf("failed to parse mangowc config: %w", err)
}
@@ -83,7 +82,7 @@ func (m *MangoWCProvider) categorizeByCommand(command string) string {
}
}
func (m *MangoWCProvider) convertKeybind(kb *mangowc.KeyBinding) keybinds.Keybind {
func (m *MangoWCProvider) convertKeybind(kb *MangoWCKeyBinding) keybinds.Keybind {
key := m.formatKey(kb)
desc := kb.Comment
@@ -104,7 +103,7 @@ func (m *MangoWCProvider) generateDescription(command, params string) string {
return command
}
func (m *MangoWCProvider) formatKey(kb *mangowc.KeyBinding) string {
func (m *MangoWCProvider) formatKey(kb *MangoWCKeyBinding) string {
parts := make([]string, 0, len(kb.Mods)+1)
parts = append(parts, kb.Mods...)
parts = append(parts, kb.Key)

View File

@@ -1,4 +1,4 @@
package mangowc
package providers
import (
"os"
@@ -8,12 +8,12 @@ import (
)
const (
HideComment = "[hidden]"
MangoWCHideComment = "[hidden]"
)
var ModSeparators = []rune{'+', ' '}
var MangoWCModSeparators = []rune{'+', ' '}
type KeyBinding struct {
type MangoWCKeyBinding struct {
Mods []string `json:"mods"`
Key string `json:"key"`
Command string `json:"command"`
@@ -21,19 +21,19 @@ type KeyBinding struct {
Comment string `json:"comment"`
}
type Parser struct {
type MangoWCParser struct {
contentLines []string
readingLine int
}
func NewParser() *Parser {
return &Parser{
func NewMangoWCParser() *MangoWCParser {
return &MangoWCParser{
contentLines: []string{},
readingLine: 0,
}
}
func (p *Parser) ReadContent(path string) error {
func (p *MangoWCParser) ReadContent(path string) error {
expandedPath := os.ExpandEnv(path)
expandedPath = filepath.Clean(expandedPath)
if strings.HasPrefix(expandedPath, "~") {
@@ -82,7 +82,7 @@ func (p *Parser) ReadContent(path string) error {
return nil
}
func autogenerateComment(command, params string) string {
func mangowcAutogenerateComment(command, params string) string {
switch command {
case "spawn", "spawn_shell":
return params
@@ -196,7 +196,7 @@ func autogenerateComment(command, params string) string {
}
}
func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
func (p *MangoWCParser) getKeybindAtLine(lineNumber int) *MangoWCKeyBinding {
if lineNumber >= len(p.contentLines) {
return nil
}
@@ -220,7 +220,7 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
comment = strings.TrimSpace(parts[1])
}
if strings.HasPrefix(comment, HideComment) {
if strings.HasPrefix(comment, MangoWCHideComment) {
return nil
}
@@ -239,16 +239,16 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
}
if comment == "" {
comment = autogenerateComment(command, params)
comment = mangowcAutogenerateComment(command, params)
}
var modList []string
if mods != "" && !strings.EqualFold(mods, "none") {
modstring := mods + string(ModSeparators[0])
modstring := mods + string(MangoWCModSeparators[0])
p := 0
for index, char := range modstring {
isModSep := false
for _, sep := range ModSeparators {
for _, sep := range MangoWCModSeparators {
if char == sep {
isModSep = true
break
@@ -265,7 +265,7 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
_ = bindType
return &KeyBinding{
return &MangoWCKeyBinding{
Mods: modList,
Key: key,
Command: command,
@@ -274,8 +274,8 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
}
}
func (p *Parser) ParseKeys() []KeyBinding {
var keybinds []KeyBinding
func (p *MangoWCParser) ParseKeys() []MangoWCKeyBinding {
var keybinds []MangoWCKeyBinding
for lineNumber := 0; lineNumber < len(p.contentLines); lineNumber++ {
line := p.contentLines[lineNumber]
@@ -296,8 +296,8 @@ func (p *Parser) ParseKeys() []KeyBinding {
return keybinds
}
func ParseKeys(path string) ([]KeyBinding, error) {
parser := NewParser()
func ParseMangoWCKeys(path string) ([]MangoWCKeyBinding, error) {
parser := NewMangoWCParser()
if err := parser.ReadContent(path); err != nil {
return nil, err
}

View File

@@ -1,4 +1,4 @@
package mangowc
package providers
import (
"os"
@@ -6,7 +6,7 @@ import (
"testing"
)
func TestAutogenerateComment(t *testing.T) {
func TestMangoWCAutogenerateComment(t *testing.T) {
tests := []struct {
command string
params string
@@ -60,25 +60,25 @@ func TestAutogenerateComment(t *testing.T) {
for _, tt := range tests {
t.Run(tt.command+"_"+tt.params, func(t *testing.T) {
result := autogenerateComment(tt.command, tt.params)
result := mangowcAutogenerateComment(tt.command, tt.params)
if result != tt.expected {
t.Errorf("autogenerateComment(%q, %q) = %q, want %q",
t.Errorf("mangowcAutogenerateComment(%q, %q) = %q, want %q",
tt.command, tt.params, result, tt.expected)
}
})
}
}
func TestGetKeybindAtLine(t *testing.T) {
func TestMangoWCGetKeybindAtLine(t *testing.T) {
tests := []struct {
name string
line string
expected *KeyBinding
expected *MangoWCKeyBinding
}{
{
name: "basic_keybind",
line: "bind=ALT,q,killclient,",
expected: &KeyBinding{
expected: &MangoWCKeyBinding{
Mods: []string{"ALT"},
Key: "q",
Command: "killclient",
@@ -89,7 +89,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_with_params",
line: "bind=ALT,Left,focusdir,left",
expected: &KeyBinding{
expected: &MangoWCKeyBinding{
Mods: []string{"ALT"},
Key: "Left",
Command: "focusdir",
@@ -100,7 +100,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_with_comment",
line: "bind=Alt,t,spawn,kitty # Open terminal",
expected: &KeyBinding{
expected: &MangoWCKeyBinding{
Mods: []string{"Alt"},
Key: "t",
Command: "spawn",
@@ -116,7 +116,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_multiple_mods",
line: "bind=SUPER+SHIFT,Up,exchange_client,up",
expected: &KeyBinding{
expected: &MangoWCKeyBinding{
Mods: []string{"SUPER", "SHIFT"},
Key: "Up",
Command: "exchange_client",
@@ -127,7 +127,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_no_mods",
line: "bind=NONE,Print,spawn,screenshot",
expected: &KeyBinding{
expected: &MangoWCKeyBinding{
Mods: []string{},
Key: "Print",
Command: "spawn",
@@ -138,7 +138,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_multiple_params",
line: "bind=Ctrl,1,view,1,0",
expected: &KeyBinding{
expected: &MangoWCKeyBinding{
Mods: []string{"Ctrl"},
Key: "1",
Command: "view",
@@ -149,7 +149,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "bindl_flag",
line: "bindl=SUPER+ALT,l,spawn,dms ipc call lock lock",
expected: &KeyBinding{
expected: &MangoWCKeyBinding{
Mods: []string{"SUPER", "ALT"},
Key: "l",
Command: "spawn",
@@ -160,7 +160,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_with_spaces",
line: "bind = SUPER, r, reload_config",
expected: &KeyBinding{
expected: &MangoWCKeyBinding{
Mods: []string{"SUPER"},
Key: "r",
Command: "reload_config",
@@ -172,7 +172,7 @@ func TestGetKeybindAtLine(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := NewParser()
parser := NewMangoWCParser()
parser.contentLines = []string{tt.line}
result := parser.getKeybindAtLine(0)
@@ -213,7 +213,7 @@ func TestGetKeybindAtLine(t *testing.T) {
}
}
func TestParseKeys(t *testing.T) {
func TestMangoWCParseKeys(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.conf")
@@ -242,9 +242,9 @@ bind=Ctrl,2,view,2,0
t.Fatalf("Failed to write test config: %v", err)
}
keybinds, err := ParseKeys(configFile)
keybinds, err := ParseMangoWCKeys(configFile)
if err != nil {
t.Fatalf("ParseKeys failed: %v", err)
t.Fatalf("ParseMangoWCKeys failed: %v", err)
}
expectedCount := 7
@@ -267,7 +267,7 @@ bind=Ctrl,2,view,2,0
}
}
func TestReadContentMultipleFiles(t *testing.T) {
func TestMangoWCReadContentMultipleFiles(t *testing.T) {
tmpDir := t.TempDir()
file1 := filepath.Join(tmpDir, "a.conf")
@@ -283,7 +283,7 @@ func TestReadContentMultipleFiles(t *testing.T) {
t.Fatalf("Failed to write file2: %v", err)
}
parser := NewParser()
parser := NewMangoWCParser()
if err := parser.ReadContent(tmpDir); err != nil {
t.Fatalf("ReadContent failed: %v", err)
}
@@ -294,7 +294,7 @@ func TestReadContentMultipleFiles(t *testing.T) {
}
}
func TestReadContentSingleFile(t *testing.T) {
func TestMangoWCReadContentSingleFile(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.conf")
@@ -304,7 +304,7 @@ func TestReadContentSingleFile(t *testing.T) {
t.Fatalf("Failed to write config: %v", err)
}
parser := NewParser()
parser := NewMangoWCParser()
if err := parser.ReadContent(configFile); err != nil {
t.Fatalf("ReadContent failed: %v", err)
}
@@ -315,7 +315,7 @@ func TestReadContentSingleFile(t *testing.T) {
}
}
func TestReadContentErrors(t *testing.T) {
func TestMangoWCReadContentErrors(t *testing.T) {
tests := []struct {
name string
path string
@@ -332,7 +332,7 @@ func TestReadContentErrors(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := ParseKeys(tt.path)
_, err := ParseMangoWCKeys(tt.path)
if err == nil {
t.Error("Expected error, got nil")
}
@@ -340,7 +340,7 @@ func TestReadContentErrors(t *testing.T) {
}
}
func TestReadContentWithTildeExpansion(t *testing.T) {
func TestMangoWCReadContentWithTildeExpansion(t *testing.T) {
homeDir, err := os.UserHomeDir()
if err != nil {
t.Skip("Cannot get home directory")
@@ -362,7 +362,7 @@ func TestReadContentWithTildeExpansion(t *testing.T) {
t.Skip("Cannot create relative path")
}
parser := NewParser()
parser := NewMangoWCParser()
tildePathMatch := "~/" + relPath
err = parser.ReadContent(tildePathMatch)
@@ -371,7 +371,7 @@ func TestReadContentWithTildeExpansion(t *testing.T) {
}
}
func TestEmptyAndCommentLines(t *testing.T) {
func TestMangoWCEmptyAndCommentLines(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.conf")
@@ -388,9 +388,9 @@ bind=Alt,t,spawn,kitty
t.Fatalf("Failed to write test config: %v", err)
}
keybinds, err := ParseKeys(configFile)
keybinds, err := ParseMangoWCKeys(configFile)
if err != nil {
t.Fatalf("ParseKeys failed: %v", err)
t.Fatalf("ParseMangoWCKeys failed: %v", err)
}
if len(keybinds) != 2 {
@@ -398,7 +398,7 @@ bind=Alt,t,spawn,kitty
}
}
func TestInvalidBindLines(t *testing.T) {
func TestMangoWCInvalidBindLines(t *testing.T) {
tests := []struct {
name string
line string
@@ -419,7 +419,7 @@ func TestInvalidBindLines(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := NewParser()
parser := NewMangoWCParser()
parser.contentLines = []string{tt.line}
result := parser.getKeybindAtLine(0)
@@ -430,7 +430,7 @@ func TestInvalidBindLines(t *testing.T) {
}
}
func TestRealWorldConfig(t *testing.T) {
func TestMangoWCRealWorldConfig(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config.conf")
@@ -462,9 +462,9 @@ bind=Ctrl,3,view,3,0
t.Fatalf("Failed to write test config: %v", err)
}
keybinds, err := ParseKeys(configFile)
keybinds, err := ParseMangoWCKeys(configFile)
if err != nil {
t.Fatalf("ParseKeys failed: %v", err)
t.Fatalf("ParseMangoWCKeys failed: %v", err)
}
if len(keybinds) < 14 {

View File

@@ -4,8 +4,6 @@ import (
"os"
"path/filepath"
"testing"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/mangowc"
)
func TestMangoWCProviderName(t *testing.T) {
@@ -88,12 +86,12 @@ func TestMangoWCCategorizeByCommand(t *testing.T) {
func TestMangoWCFormatKey(t *testing.T) {
tests := []struct {
name string
keybind *mangowc.KeyBinding
keybind *MangoWCKeyBinding
expected string
}{
{
name: "single_mod",
keybind: &mangowc.KeyBinding{
keybind: &MangoWCKeyBinding{
Mods: []string{"ALT"},
Key: "q",
},
@@ -101,7 +99,7 @@ func TestMangoWCFormatKey(t *testing.T) {
},
{
name: "multiple_mods",
keybind: &mangowc.KeyBinding{
keybind: &MangoWCKeyBinding{
Mods: []string{"SUPER", "SHIFT"},
Key: "Up",
},
@@ -109,7 +107,7 @@ func TestMangoWCFormatKey(t *testing.T) {
},
{
name: "no_mods",
keybind: &mangowc.KeyBinding{
keybind: &MangoWCKeyBinding{
Mods: []string{},
Key: "Print",
},
@@ -131,13 +129,13 @@ func TestMangoWCFormatKey(t *testing.T) {
func TestMangoWCConvertKeybind(t *testing.T) {
tests := []struct {
name string
keybind *mangowc.KeyBinding
keybind *MangoWCKeyBinding
wantKey string
wantDesc string
}{
{
name: "with_comment",
keybind: &mangowc.KeyBinding{
keybind: &MangoWCKeyBinding{
Mods: []string{"ALT"},
Key: "t",
Command: "spawn",
@@ -149,7 +147,7 @@ func TestMangoWCConvertKeybind(t *testing.T) {
},
{
name: "without_comment",
keybind: &mangowc.KeyBinding{
keybind: &MangoWCKeyBinding{
Mods: []string{"SUPER"},
Key: "r",
Command: "reload_config",
@@ -161,7 +159,7 @@ func TestMangoWCConvertKeybind(t *testing.T) {
},
{
name: "with_params_no_comment",
keybind: &mangowc.KeyBinding{
keybind: &MangoWCKeyBinding{
Mods: []string{"CTRL"},
Key: "1",
Command: "view",

View File

@@ -4,8 +4,7 @@ import (
"fmt"
"strings"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/keybinds"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/sway"
"github.com/AvengeMedia/DankMaterialShell/core/internal/keybinds"
)
type SwayProvider struct {
@@ -26,7 +25,7 @@ func (s *SwayProvider) Name() string {
}
func (s *SwayProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
section, err := sway.ParseKeys(s.configPath)
section, err := ParseSwayKeys(s.configPath)
if err != nil {
return nil, fmt.Errorf("failed to parse sway config: %w", err)
}
@@ -41,7 +40,7 @@ func (s *SwayProvider) GetCheatSheet() (*keybinds.CheatSheet, error) {
}, nil
}
func (s *SwayProvider) convertSection(section *sway.Section, subcategory string, categorizedBinds map[string][]keybinds.Keybind) {
func (s *SwayProvider) convertSection(section *SwaySection, subcategory string, categorizedBinds map[string][]keybinds.Keybind) {
currentSubcat := subcategory
if section.Name != "" {
currentSubcat = section.Name
@@ -89,7 +88,7 @@ func (s *SwayProvider) categorizeByCommand(command string) string {
}
}
func (s *SwayProvider) convertKeybind(kb *sway.KeyBinding, subcategory string) keybinds.Keybind {
func (s *SwayProvider) convertKeybind(kb *SwayKeyBinding, subcategory string) keybinds.Keybind {
key := s.formatKey(kb)
desc := kb.Comment
@@ -104,7 +103,7 @@ func (s *SwayProvider) convertKeybind(kb *sway.KeyBinding, subcategory string) k
}
}
func (s *SwayProvider) formatKey(kb *sway.KeyBinding) string {
func (s *SwayProvider) formatKey(kb *SwayKeyBinding) string {
parts := make([]string, 0, len(kb.Mods)+1)
parts = append(parts, kb.Mods...)
parts = append(parts, kb.Key)

View File

@@ -1,4 +1,4 @@
package sway
package providers
import (
"os"
@@ -8,40 +8,40 @@ import (
)
const (
TitleRegex = "#+!"
HideComment = "[hidden]"
SwayTitleRegex = "#+!"
SwayHideComment = "[hidden]"
)
var ModSeparators = []rune{'+', ' '}
var SwayModSeparators = []rune{'+', ' '}
type KeyBinding struct {
type SwayKeyBinding struct {
Mods []string `json:"mods"`
Key string `json:"key"`
Command string `json:"command"`
Comment string `json:"comment"`
}
type Section struct {
Children []Section `json:"children"`
Keybinds []KeyBinding `json:"keybinds"`
Name string `json:"name"`
type SwaySection struct {
Children []SwaySection `json:"children"`
Keybinds []SwayKeyBinding `json:"keybinds"`
Name string `json:"name"`
}
type Parser struct {
type SwayParser struct {
contentLines []string
readingLine int
variables map[string]string
}
func NewParser() *Parser {
return &Parser{
func NewSwayParser() *SwayParser {
return &SwayParser{
contentLines: []string{},
readingLine: 0,
variables: make(map[string]string),
}
}
func (p *Parser) ReadContent(path string) error {
func (p *SwayParser) ReadContent(path string) error {
expandedPath := os.ExpandEnv(path)
expandedPath = filepath.Clean(expandedPath)
if strings.HasPrefix(expandedPath, "~") {
@@ -88,7 +88,7 @@ func (p *Parser) ReadContent(path string) error {
return nil
}
func (p *Parser) parseVariables() {
func (p *SwayParser) parseVariables() {
setRegex := regexp.MustCompile(`^\s*set\s+\$(\w+)\s+(.+)$`)
for _, line := range p.contentLines {
matches := setRegex.FindStringSubmatch(line)
@@ -100,7 +100,7 @@ func (p *Parser) parseVariables() {
}
}
func (p *Parser) expandVariables(text string) string {
func (p *SwayParser) expandVariables(text string) string {
result := text
for varName, varValue := range p.variables {
result = strings.ReplaceAll(result, "$"+varName, varValue)
@@ -108,7 +108,7 @@ func (p *Parser) expandVariables(text string) string {
return result
}
func autogenerateComment(command string) string {
func swayAutogenerateComment(command string) string {
command = strings.TrimSpace(command)
if strings.HasPrefix(command, "exec ") {
@@ -200,7 +200,7 @@ func autogenerateComment(command string) string {
}
}
func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
func (p *SwayParser) getKeybindAtLine(lineNumber int) *SwayKeyBinding {
if lineNumber >= len(p.contentLines) {
return nil
}
@@ -223,7 +223,7 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
comment = strings.TrimSpace(parts[1])
}
if strings.HasPrefix(comment, HideComment) {
if strings.HasPrefix(comment, SwayHideComment) {
return nil
}
@@ -249,11 +249,11 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
var modList []string
var key string
modstring := keyCombo + string(ModSeparators[0])
modstring := keyCombo + string(SwayModSeparators[0])
pos := 0
for index, char := range modstring {
isModSep := false
for _, sep := range ModSeparators {
for _, sep := range SwayModSeparators {
if char == sep {
isModSep = true
break
@@ -262,7 +262,7 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
if isModSep {
if index-pos > 0 {
part := modstring[pos:index]
if isMod(part) {
if swayIsMod(part) {
modList = append(modList, part)
} else {
key = part
@@ -273,12 +273,12 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
}
if comment == "" {
comment = autogenerateComment(command)
comment = swayAutogenerateComment(command)
}
_ = flags
return &KeyBinding{
return &SwayKeyBinding{
Mods: modList,
Key: key,
Command: command,
@@ -286,7 +286,7 @@ func (p *Parser) getKeybindAtLine(lineNumber int) *KeyBinding {
}
}
func isMod(s string) bool {
func swayIsMod(s string) bool {
s = strings.ToLower(s)
if s == "mod1" || s == "mod2" || s == "mod3" || s == "mod4" || s == "mod5" ||
s == "shift" || s == "control" || s == "ctrl" || s == "alt" || s == "super" ||
@@ -307,8 +307,8 @@ func isMod(s string) bool {
return false
}
func (p *Parser) getBindsRecursive(currentContent *Section, scope int) *Section {
titleRegex := regexp.MustCompile(TitleRegex)
func (p *SwayParser) getBindsRecursive(currentContent *SwaySection, scope int) *SwaySection {
titleRegex := regexp.MustCompile(SwayTitleRegex)
for p.readingLine < len(p.contentLines) {
line := p.contentLines[p.readingLine]
@@ -325,9 +325,9 @@ func (p *Parser) getBindsRecursive(currentContent *Section, scope int) *Section
sectionName := strings.TrimSpace(line[headingScope+1:])
p.readingLine++
childSection := &Section{
Children: []Section{},
Keybinds: []KeyBinding{},
childSection := &SwaySection{
Children: []SwaySection{},
Keybinds: []SwayKeyBinding{},
Name: sectionName,
}
result := p.getBindsRecursive(childSection, headingScope)
@@ -348,18 +348,18 @@ func (p *Parser) getBindsRecursive(currentContent *Section, scope int) *Section
return currentContent
}
func (p *Parser) ParseKeys() *Section {
func (p *SwayParser) ParseKeys() *SwaySection {
p.readingLine = 0
rootSection := &Section{
Children: []Section{},
Keybinds: []KeyBinding{},
rootSection := &SwaySection{
Children: []SwaySection{},
Keybinds: []SwayKeyBinding{},
Name: "",
}
return p.getBindsRecursive(rootSection, 0)
}
func ParseKeys(path string) (*Section, error) {
parser := NewParser()
func ParseSwayKeys(path string) (*SwaySection, error) {
parser := NewSwayParser()
if err := parser.ReadContent(path); err != nil {
return nil, err
}

View File

@@ -1,4 +1,4 @@
package sway
package providers
import (
"os"
@@ -6,7 +6,7 @@ import (
"testing"
)
func TestAutogenerateComment(t *testing.T) {
func TestSwayAutogenerateComment(t *testing.T) {
tests := []struct {
command string
expected string
@@ -46,25 +46,25 @@ func TestAutogenerateComment(t *testing.T) {
for _, tt := range tests {
t.Run(tt.command, func(t *testing.T) {
result := autogenerateComment(tt.command)
result := swayAutogenerateComment(tt.command)
if result != tt.expected {
t.Errorf("autogenerateComment(%q) = %q, want %q",
t.Errorf("swayAutogenerateComment(%q) = %q, want %q",
tt.command, result, tt.expected)
}
})
}
}
func TestGetKeybindAtLine(t *testing.T) {
func TestSwayGetKeybindAtLine(t *testing.T) {
tests := []struct {
name string
line string
expected *KeyBinding
expected *SwayKeyBinding
}{
{
name: "basic_keybind",
line: "bindsym Mod4+q kill",
expected: &KeyBinding{
expected: &SwayKeyBinding{
Mods: []string{"Mod4"},
Key: "q",
Command: "kill",
@@ -74,7 +74,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_with_exec",
line: "bindsym Mod4+t exec kitty",
expected: &KeyBinding{
expected: &SwayKeyBinding{
Mods: []string{"Mod4"},
Key: "t",
Command: "exec kitty",
@@ -84,7 +84,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_with_comment",
line: "bindsym Mod4+Space exec dms ipc call spotlight toggle # Open launcher",
expected: &KeyBinding{
expected: &SwayKeyBinding{
Mods: []string{"Mod4"},
Key: "Space",
Command: "exec dms ipc call spotlight toggle",
@@ -99,7 +99,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_multiple_mods",
line: "bindsym Mod4+Shift+e exit",
expected: &KeyBinding{
expected: &SwayKeyBinding{
Mods: []string{"Mod4", "Shift"},
Key: "e",
Command: "exit",
@@ -109,7 +109,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_no_mods",
line: "bindsym Print exec grim screenshot.png",
expected: &KeyBinding{
expected: &SwayKeyBinding{
Mods: []string{},
Key: "Print",
Command: "exec grim screenshot.png",
@@ -119,7 +119,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_with_flags",
line: "bindsym --release Mod4+x exec notify-send released",
expected: &KeyBinding{
expected: &SwayKeyBinding{
Mods: []string{"Mod4"},
Key: "x",
Command: "exec notify-send released",
@@ -129,7 +129,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_focus_direction",
line: "bindsym Mod4+Left focus left",
expected: &KeyBinding{
expected: &SwayKeyBinding{
Mods: []string{"Mod4"},
Key: "Left",
Command: "focus left",
@@ -139,7 +139,7 @@ func TestGetKeybindAtLine(t *testing.T) {
{
name: "keybind_workspace",
line: "bindsym Mod4+1 workspace number 1",
expected: &KeyBinding{
expected: &SwayKeyBinding{
Mods: []string{"Mod4"},
Key: "1",
Command: "workspace number 1",
@@ -150,7 +150,7 @@ func TestGetKeybindAtLine(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parser := NewParser()
parser := NewSwayParser()
parser.contentLines = []string{tt.line}
result := parser.getKeybindAtLine(0)
@@ -188,7 +188,7 @@ func TestGetKeybindAtLine(t *testing.T) {
}
}
func TestVariableExpansion(t *testing.T) {
func TestSwayVariableExpansion(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config")
@@ -204,9 +204,9 @@ bindsym $mod+d exec $menu
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseKeys(configFile)
section, err := ParseSwayKeys(configFile)
if err != nil {
t.Fatalf("ParseKeys failed: %v", err)
t.Fatalf("ParseSwayKeys failed: %v", err)
}
if len(section.Keybinds) != 2 {
@@ -229,7 +229,7 @@ bindsym $mod+d exec $menu
}
}
func TestParseKeysWithSections(t *testing.T) {
func TestSwayParseKeysWithSections(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config")
@@ -251,9 +251,9 @@ bindsym $mod+t exec kitty # Terminal
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseKeys(tmpDir)
section, err := ParseSwayKeys(tmpDir)
if err != nil {
t.Fatalf("ParseKeys failed: %v", err)
t.Fatalf("ParseSwayKeys failed: %v", err)
}
if len(section.Children) != 2 {
@@ -296,7 +296,7 @@ bindsym $mod+t exec kitty # Terminal
}
}
func TestReadContentErrors(t *testing.T) {
func TestSwayReadContentErrors(t *testing.T) {
tests := []struct {
name string
path string
@@ -313,7 +313,7 @@ func TestReadContentErrors(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := ParseKeys(tt.path)
_, err := ParseSwayKeys(tt.path)
if err == nil {
t.Error("Expected error, got nil")
}
@@ -321,7 +321,7 @@ func TestReadContentErrors(t *testing.T) {
}
}
func TestReadContentWithTildeExpansion(t *testing.T) {
func TestSwayReadContentWithTildeExpansion(t *testing.T) {
homeDir, err := os.UserHomeDir()
if err != nil {
t.Skip("Cannot get home directory")
@@ -343,7 +343,7 @@ func TestReadContentWithTildeExpansion(t *testing.T) {
t.Skip("Cannot create relative path")
}
parser := NewParser()
parser := NewSwayParser()
tildePathMatch := "~/" + relPath
err = parser.ReadContent(tildePathMatch)
@@ -352,7 +352,7 @@ func TestReadContentWithTildeExpansion(t *testing.T) {
}
}
func TestEmptyAndCommentLines(t *testing.T) {
func TestSwayEmptyAndCommentLines(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config")
@@ -369,9 +369,9 @@ bindsym Mod4+t exec kitty
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseKeys(configFile)
section, err := ParseSwayKeys(configFile)
if err != nil {
t.Fatalf("ParseKeys failed: %v", err)
t.Fatalf("ParseSwayKeys failed: %v", err)
}
if len(section.Keybinds) != 2 {
@@ -379,7 +379,7 @@ bindsym Mod4+t exec kitty
}
}
func TestRealWorldConfig(t *testing.T) {
func TestSwayRealWorldConfig(t *testing.T) {
tmpDir := t.TempDir()
configFile := filepath.Join(tmpDir, "config")
@@ -408,9 +408,9 @@ bindsym $mod+Shift+1 move container to workspace number 1
t.Fatalf("Failed to write test config: %v", err)
}
section, err := ParseKeys(configFile)
section, err := ParseSwayKeys(configFile)
if err != nil {
t.Fatalf("ParseKeys failed: %v", err)
t.Fatalf("ParseSwayKeys failed: %v", err)
}
if len(section.Keybinds) < 9 {
@@ -444,7 +444,7 @@ bindsym $mod+Shift+1 move container to workspace number 1
}
}
func TestIsMod(t *testing.T) {
func TestSwayIsMod(t *testing.T) {
tests := []struct {
input string
expected bool
@@ -462,9 +462,9 @@ func TestIsMod(t *testing.T) {
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result := isMod(tt.input)
result := swayIsMod(tt.input)
if result != tt.expected {
t.Errorf("isMod(%q) = %v, want %v", tt.input, result, tt.expected)
t.Errorf("swayIsMod(%q) = %v, want %v", tt.input, result, tt.expected)
}
})
}

View File

@@ -4,8 +4,6 @@ import (
"os"
"path/filepath"
"testing"
"github.com/AvengeMedia/DankMaterialShell/backend/internal/sway"
)
func TestSwayProviderName(t *testing.T) {
@@ -76,12 +74,12 @@ func TestSwayCategorizeByCommand(t *testing.T) {
func TestSwayFormatKey(t *testing.T) {
tests := []struct {
name string
keybind *sway.KeyBinding
keybind *SwayKeyBinding
expected string
}{
{
name: "single_mod",
keybind: &sway.KeyBinding{
keybind: &SwayKeyBinding{
Mods: []string{"Mod4"},
Key: "q",
},
@@ -89,7 +87,7 @@ func TestSwayFormatKey(t *testing.T) {
},
{
name: "multiple_mods",
keybind: &sway.KeyBinding{
keybind: &SwayKeyBinding{
Mods: []string{"Mod4", "Shift"},
Key: "e",
},
@@ -97,7 +95,7 @@ func TestSwayFormatKey(t *testing.T) {
},
{
name: "no_mods",
keybind: &sway.KeyBinding{
keybind: &SwayKeyBinding{
Mods: []string{},
Key: "Print",
},
@@ -119,13 +117,13 @@ func TestSwayFormatKey(t *testing.T) {
func TestSwayConvertKeybind(t *testing.T) {
tests := []struct {
name string
keybind *sway.KeyBinding
keybind *SwayKeyBinding
wantKey string
wantDesc string
}{
{
name: "with_comment",
keybind: &sway.KeyBinding{
keybind: &SwayKeyBinding{
Mods: []string{"Mod4"},
Key: "t",
Command: "exec kitty",
@@ -136,7 +134,7 @@ func TestSwayConvertKeybind(t *testing.T) {
},
{
name: "without_comment",
keybind: &sway.KeyBinding{
keybind: &SwayKeyBinding{
Mods: []string{"Mod4"},
Key: "r",
Command: "reload",

View File

@@ -1,4 +1,4 @@
package logger
package log
import (
"bufio"

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