mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
Compare commits
220 Commits
monorepo
...
0864179085
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0864179085 | ||
|
|
8de77f283d | ||
|
|
004a014000 | ||
|
|
80f6eb94aa | ||
|
|
4035c9cc5f | ||
|
|
3a365f6807 | ||
|
|
9920a0a59f | ||
|
|
c17bb9e171 | ||
|
|
03073f6875 | ||
|
|
609caf6e5f | ||
|
|
411141ff88 | ||
|
|
3e472e18bd | ||
|
|
e5b6fbd12a | ||
|
|
c2787f1282 | ||
|
|
df940124b1 | ||
|
|
5288d042ca | ||
|
|
fa98a27c90 | ||
|
|
d341a5a60b | ||
|
|
7f15227de1 | ||
|
|
bb45240665 | ||
|
|
29f84aeab5 | ||
|
|
5a52edcad8 | ||
|
|
b078e23aa1 | ||
|
|
7fa87125b5 | ||
|
|
f618df46d8 | ||
|
|
ee03853901 | ||
|
|
6c4a9bcfb8 | ||
|
|
1bec20ecef | ||
|
|
08c9bf570d | ||
|
|
5e77a10a81 | ||
|
|
3bc6461e2a | ||
|
|
d3194e15e2 | ||
|
|
2db79ef202 | ||
|
|
b3c07edef6 | ||
|
|
b773fdca34 | ||
|
|
2e9f9f7b7e | ||
|
|
30cbfe729d | ||
|
|
b036da2446 | ||
|
|
c8a9fb1674 | ||
|
|
43bea80cad | ||
|
|
23538c0323 | ||
|
|
2ae911230d | ||
|
|
5ce1cb87ea | ||
|
|
2a37028b6a | ||
|
|
8130feb2a0 | ||
|
|
c49a875ec2 | ||
|
|
2a002304b9 | ||
|
|
d9522818ae | ||
|
|
800588e121 | ||
|
|
991c31ebdb | ||
|
|
48f77e1691 | ||
|
|
42de6fd074 | ||
|
|
62845b470c | ||
|
|
fd20986cf8 | ||
|
|
61369cde9e | ||
|
|
644384ce8b | ||
|
|
97c11a2482 | ||
|
|
1e7e1c2d78 | ||
|
|
1c7201fb04 | ||
|
|
61ec0c697a | ||
|
|
4b5fce1bfc | ||
|
|
6cc6e7c8e9 | ||
|
|
89298fce30 | ||
|
|
a3a27e07fa | ||
|
|
4f32376f22 | ||
|
|
58bf189941 | ||
|
|
bcfa508da5 | ||
|
|
c0ae3ef58b | ||
|
|
1e70d7b4c3 | ||
|
|
f8dc6ad2bc | ||
|
|
e22482988f | ||
|
|
4eb896629d | ||
|
|
b310e66275 | ||
|
|
b39da1bea7 | ||
|
|
fa575d0574 | ||
|
|
dfe2f3771b | ||
|
|
46caeb0445 | ||
|
|
59cc9c7006 | ||
|
|
12e91534eb | ||
|
|
d9da88ceb5 | ||
|
|
2dbfec0307 | ||
|
|
09cf8c9641 | ||
|
|
f1bed4d6a3 | ||
|
|
2ed6c33c83 | ||
|
|
7ad532ed17 | ||
|
|
92fe8c5b14 | ||
|
|
8e95572589 | ||
|
|
62da862a66 | ||
|
|
993e34f548 | ||
|
|
e39465aece | ||
|
|
8fd616b680 | ||
|
|
cc054b27de | ||
|
|
dfdaa82245 | ||
|
|
99a307e0ad | ||
|
|
5ddea836a1 | ||
|
|
208d92aa06 | ||
|
|
6ef9ddd4f3 | ||
|
|
1c92d39185 | ||
|
|
c0f072217c | ||
|
|
542562f988 | ||
|
|
4e6f0d5e87 | ||
|
|
10639a5ead | ||
|
|
06d668e710 | ||
|
|
d1472dfcba | ||
|
|
ccb4da3cd8 | ||
|
|
46e96b49f0 | ||
|
|
984cfe7f98 | ||
|
|
d769300137 | ||
|
|
d175d66828 | ||
|
|
c1a314332e | ||
|
|
046ac59d21 | ||
|
|
00c06f07d0 | ||
|
|
3e2ab40c6a | ||
|
|
350ffd0052 | ||
|
|
ecd1a622d2 | ||
|
|
f13968aa61 | ||
|
|
4d1ffde54c | ||
|
|
d69017a706 | ||
|
|
f2deaeccdb | ||
|
|
ea9b0d2a79 | ||
|
|
2e6dbedb8b | ||
|
|
6f359df8f9 | ||
|
|
f6db20cd06 | ||
|
|
6287fae065 | ||
|
|
e441607ce3 | ||
|
|
b5379a95fa | ||
|
|
64ec5be919 | ||
|
|
3916512d66 | ||
|
|
e2f426a1bd | ||
|
|
aa1df8dfcf | ||
|
|
67557555f2 | ||
|
|
4cb652abd9 | ||
|
|
d11868b99f | ||
|
|
1798417e6a | ||
|
|
43dc3e5bb1 | ||
|
|
91891a14ed | ||
|
|
20f7d60147 | ||
|
|
7e17e7d37a | ||
|
|
cbb244f785 | ||
|
|
1c264d858b | ||
|
|
217037c2ae | ||
|
|
b4dbd0b69c | ||
|
|
89a2b5c00b | ||
|
|
929b6dae1a | ||
|
|
52fe493da9 | ||
|
|
3e6be3e762 | ||
|
|
7a8cc449b9 | ||
|
|
8f5a9d6e9f | ||
|
|
1c5e31fea9 | ||
|
|
fd08ae18ab | ||
|
|
a7eb3de06e | ||
|
|
8902dd7c44 | ||
|
|
6387d8400c | ||
|
|
597cacb9cc | ||
|
|
3e285ad9ff | ||
|
|
cc1fa89790 | ||
|
|
b0ed007751 | ||
|
|
e1e2650d2b | ||
|
|
b23f17b633 | ||
|
|
818e40b2df | ||
|
|
5685e39631 | ||
|
|
72534b7674 | ||
|
|
328490d23d | ||
|
|
97a0696930 | ||
|
|
cb4e0660e0 | ||
|
|
67c642de4c | ||
|
|
0d7c2e1024 | ||
|
|
16a779a41b | ||
|
|
c4ca3c8644 | ||
|
|
aabcbe34f3 | ||
|
|
f06626e441 | ||
|
|
c4e1a71776 | ||
|
|
77e6c16bd2 | ||
|
|
9d1fac3570 | ||
|
|
b7aeaa7fc5 | ||
|
|
f6d8c9ff61 | ||
|
|
0490794d6c | ||
|
|
335c83dd3c | ||
|
|
91da720c26 | ||
|
|
b6ac744a68 | ||
|
|
526c4092fd | ||
|
|
ed06dda384 | ||
|
|
6465b11e9b | ||
|
|
b2879878a1 | ||
|
|
3e17b086fb | ||
|
|
0545e6bcda | ||
|
|
27a907433f | ||
|
|
69616800e3 | ||
|
|
abf1f53432 | ||
|
|
881c5f75cb | ||
|
|
4e45796ade | ||
|
|
1ce4ea5230 | ||
|
|
f2a2437baa | ||
|
|
508dc9db1e | ||
|
|
a914e3557f | ||
|
|
f489dc062f | ||
|
|
a7e09f4850 | ||
|
|
8ea97530d4 | ||
|
|
13ab54e83a | ||
|
|
4bc40325cb | ||
|
|
58d9355ea3 | ||
|
|
d46b7528e7 | ||
|
|
1858597fc9 | ||
|
|
83cce5afe4 | ||
|
|
201bd8dc1f | ||
|
|
b62ba69060 | ||
|
|
5d2f5557e5 | ||
|
|
cf75c1aad0 | ||
|
|
76a60df88b | ||
|
|
9322c79b4e | ||
|
|
12365edcf0 | ||
|
|
5efc1f9dad | ||
|
|
ab976cbb24 | ||
|
|
db584b7897 | ||
|
|
0fdc0748cf | ||
|
|
2e79c21dc2 | ||
|
|
5490a230bd | ||
|
|
a6b059b30d | ||
|
|
712e6011aa | ||
|
|
68f6f87410 |
6
.github/workflows/go-ci.yml
vendored
6
.github/workflows/go-ci.yml
vendored
@@ -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: |
|
||||
|
||||
164
.github/workflows/release.yml
vendored
164
.github/workflows/release.yml
vendored
@@ -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
|
||||
|
||||
@@ -364,6 +386,68 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
trigger-obs-update:
|
||||
runs-on: ubuntu-latest
|
||||
needs: release
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install OSC
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y osc
|
||||
|
||||
mkdir -p ~/.config/osc
|
||||
cat > ~/.config/osc/oscrc << EOF
|
||||
[general]
|
||||
apiurl = https://api.opensuse.org
|
||||
|
||||
[https://api.opensuse.org]
|
||||
user = ${{ secrets.OBS_USERNAME }}
|
||||
pass = ${{ secrets.OBS_PASSWORD }}
|
||||
EOF
|
||||
chmod 600 ~/.config/osc/oscrc
|
||||
|
||||
- name: Update OBS packages
|
||||
run: |
|
||||
VERSION="${{ github.ref_name }}"
|
||||
cd distro
|
||||
bash scripts/obs-upload.sh dms "Update to $VERSION"
|
||||
|
||||
trigger-ppa-update:
|
||||
runs-on: ubuntu-latest
|
||||
needs: release
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
debhelper \
|
||||
devscripts \
|
||||
dput \
|
||||
lftp \
|
||||
build-essential \
|
||||
fakeroot \
|
||||
dpkg-dev
|
||||
|
||||
- name: Configure GPG
|
||||
env:
|
||||
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
run: |
|
||||
echo "$GPG_KEY" | gpg --import
|
||||
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | awk '{print $2}' | cut -d'/' -f2)
|
||||
echo "DEBSIGN_KEYID=$GPG_KEY_ID" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload to PPA
|
||||
run: |
|
||||
VERSION="${{ github.ref_name }}"
|
||||
cd distro/ubuntu/ppa
|
||||
bash create-and-upload.sh ../dms dms questing
|
||||
|
||||
copr-build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: release
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: DMS Copr Stable Release (Manual)
|
||||
name: DMS Copr Stable Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -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
|
||||
238
.github/workflows/run-obs.yml
vendored
Normal file
238
.github/workflows/run-obs.yml
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
name: Update OBS Packages
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package:
|
||||
description: 'Package to update (dms, dms-git, or all)'
|
||||
required: false
|
||||
default: 'all'
|
||||
rebuild_release:
|
||||
description: 'Release number for rebuilds (e.g., 2, 3, 4 to increment spec Release)'
|
||||
required: false
|
||||
default: ''
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
schedule:
|
||||
- cron: '0 */3 * * *' # Every 3 hours for dms-git builds
|
||||
|
||||
jobs:
|
||||
check-updates:
|
||||
name: Check for updates
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
has_updates: ${{ steps.check.outputs.has_updates }}
|
||||
packages: ${{ steps.check.outputs.packages }}
|
||||
version: ${{ steps.check.outputs.version }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install OSC
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y osc
|
||||
|
||||
mkdir -p ~/.config/osc
|
||||
cat > ~/.config/osc/oscrc << EOF
|
||||
[general]
|
||||
apiurl = https://api.opensuse.org
|
||||
|
||||
[https://api.opensuse.org]
|
||||
user = ${{ secrets.OBS_USERNAME }}
|
||||
pass = ${{ secrets.OBS_PASSWORD }}
|
||||
EOF
|
||||
chmod 600 ~/.config/osc/oscrc
|
||||
|
||||
- name: Check for updates
|
||||
id: check
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
|
||||
echo "packages=dms" >> $GITHUB_OUTPUT
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
||||
echo "Triggered by tag: $VERSION (always update)"
|
||||
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||
echo "packages=dms-git" >> $GITHUB_OUTPUT
|
||||
echo "Checking if dms-git source has changed..."
|
||||
|
||||
# Get latest commit hash from master branch
|
||||
LATEST_COMMIT=$(git rev-parse origin/master 2>/dev/null || git rev-parse master 2>/dev/null || echo "")
|
||||
|
||||
if [[ -z "$LATEST_COMMIT" ]]; then
|
||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
||||
echo "Could not determine git commit, proceeding with update"
|
||||
else
|
||||
# Check OBS for last uploaded commit
|
||||
OBS_BASE="$HOME/.cache/osc-checkouts"
|
||||
mkdir -p "$OBS_BASE"
|
||||
OBS_PROJECT="home:AvengeMedia:dms-git"
|
||||
|
||||
if [[ -d "$OBS_BASE/$OBS_PROJECT/dms-git" ]]; then
|
||||
cd "$OBS_BASE/$OBS_PROJECT/dms-git"
|
||||
osc up -q 2>/dev/null || true
|
||||
|
||||
# Check tarball age - if older than 3 hours, update needed
|
||||
if [[ -f "dms-git-source.tar.gz" ]]; then
|
||||
TARBALL_MTIME=$(stat -c%Y "dms-git-source.tar.gz" 2>/dev/null || echo "0")
|
||||
CURRENT_TIME=$(date +%s)
|
||||
AGE_SECONDS=$((CURRENT_TIME - TARBALL_MTIME))
|
||||
AGE_HOURS=$((AGE_SECONDS / 3600))
|
||||
|
||||
# If tarball is older than 3 hours, check for new commits
|
||||
if [[ $AGE_HOURS -ge 3 ]]; then
|
||||
# Check if there are new commits in the last 3 hours
|
||||
cd "${{ github.workspace }}"
|
||||
NEW_COMMITS=$(git log --since="3 hours ago" --oneline origin/master 2>/dev/null | wc -l)
|
||||
|
||||
if [[ $NEW_COMMITS -gt 0 ]]; then
|
||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
||||
echo "📋 New commits detected in last 3 hours, update needed"
|
||||
else
|
||||
echo "has_updates=false" >> $GITHUB_OUTPUT
|
||||
echo "📋 No new commits in last 3 hours, skipping update"
|
||||
fi
|
||||
else
|
||||
echo "has_updates=false" >> $GITHUB_OUTPUT
|
||||
echo "📋 Recent upload exists (< 3 hours), skipping update"
|
||||
fi
|
||||
else
|
||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
||||
echo "📋 No existing tarball in OBS, update needed"
|
||||
fi
|
||||
cd "${{ github.workspace }}"
|
||||
else
|
||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
||||
echo "📋 First upload to OBS, update needed"
|
||||
fi
|
||||
fi
|
||||
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
|
||||
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
|
||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
||||
echo "Manual trigger: ${{ github.event.inputs.package }}"
|
||||
else
|
||||
echo "packages=all" >> $GITHUB_OUTPUT
|
||||
echo "has_updates=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
update-obs:
|
||||
name: Upload to OBS
|
||||
needs: check-updates
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
needs.check-updates.outputs.has_updates == 'true'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Determine packages to update
|
||||
id: packages
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/ ]]; then
|
||||
echo "packages=dms" >> $GITHUB_OUTPUT
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Triggered by tag: $VERSION"
|
||||
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
||||
echo "Triggered by schedule: updating git package"
|
||||
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
|
||||
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
|
||||
echo "Manual trigger: ${{ github.event.inputs.package }}"
|
||||
else
|
||||
echo "packages=${{ needs.check-updates.outputs.packages }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Update dms-git spec version
|
||||
if: contains(steps.packages.outputs.packages, 'dms-git') || steps.packages.outputs.packages == 'all'
|
||||
run: |
|
||||
# Get commit info for dms-git versioning
|
||||
COMMIT_HASH=$(git rev-parse --short=8 HEAD)
|
||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
||||
BASE_VERSION=$(grep -oP '^Version:\s+\K[0-9.]+' distro/opensuse/dms.spec | head -1 || echo "0.6.2")
|
||||
|
||||
NEW_VERSION="${BASE_VERSION}+git${COMMIT_COUNT}.${COMMIT_HASH}"
|
||||
echo "📦 Updating dms-git.spec to version: $NEW_VERSION"
|
||||
|
||||
# Update version in spec
|
||||
sed -i "s/^Version:.*/Version: $NEW_VERSION/" distro/opensuse/dms-git.spec
|
||||
|
||||
# Add changelog entry
|
||||
DATE_STR=$(date "+%a %b %d %Y")
|
||||
CHANGELOG_ENTRY="* $DATE_STR Avenge Media <AvengeMedia.US@gmail.com> - ${NEW_VERSION}-1\n- Git snapshot (commit $COMMIT_COUNT: $COMMIT_HASH)"
|
||||
sed -i "/%changelog/a\\$CHANGELOG_ENTRY" distro/opensuse/dms-git.spec
|
||||
|
||||
- name: Update dms stable version
|
||||
if: steps.packages.outputs.version != ''
|
||||
run: |
|
||||
VERSION="${{ steps.packages.outputs.version }}"
|
||||
VERSION_NO_V="${VERSION#v}"
|
||||
echo "Updating packaging to version $VERSION_NO_V"
|
||||
|
||||
# Update openSUSE dms spec (stable only)
|
||||
sed -i "s/^Version:.*/Version: $VERSION_NO_V/" distro/opensuse/dms.spec
|
||||
|
||||
# Update Debian _service files
|
||||
for service in distro/debian/*/_service; do
|
||||
if [[ -f "$service" ]]; then
|
||||
sed -i "s|<param name=\"revision\">v[0-9.]*</param>|<param name=\"revision\">$VERSION</param>|" "$service"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Install OSC
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y osc
|
||||
|
||||
mkdir -p ~/.config/osc
|
||||
cat > ~/.config/osc/oscrc << EOF
|
||||
[general]
|
||||
apiurl = https://api.opensuse.org
|
||||
|
||||
[https://api.opensuse.org]
|
||||
user = ${{ secrets.OBS_USERNAME }}
|
||||
pass = ${{ secrets.OBS_PASSWORD }}
|
||||
EOF
|
||||
chmod 600 ~/.config/osc/oscrc
|
||||
|
||||
- name: Upload to OBS
|
||||
env:
|
||||
FORCE_REBUILD: ${{ github.event_name == 'workflow_dispatch' && 'true' || '' }}
|
||||
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
|
||||
run: |
|
||||
PACKAGES="${{ steps.packages.outputs.packages }}"
|
||||
MESSAGE="Automated update from GitHub Actions"
|
||||
|
||||
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
||||
MESSAGE="Update to ${{ steps.packages.outputs.version }}"
|
||||
fi
|
||||
|
||||
if [[ "$PACKAGES" == "all" ]]; then
|
||||
bash distro/scripts/obs-upload.sh dms "$MESSAGE"
|
||||
bash distro/scripts/obs-upload.sh dms-git "Automated git update"
|
||||
else
|
||||
bash distro/scripts/obs-upload.sh "$PACKAGES" "$MESSAGE"
|
||||
fi
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "### OBS Package Update Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Packages**: ${{ steps.packages.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
|
||||
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
||||
echo "- **Version**: ${{ steps.packages.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
if [[ "${{ needs.check-updates.outputs.has_updates }}" == "false" ]]; then
|
||||
echo "- **Status**: Skipped (no changes detected)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "- **Project**: https://build.opensuse.org/project/show/home:AvengeMedia" >> $GITHUB_STEP_SUMMARY
|
||||
108
.github/workflows/run-ppa.yml
vendored
Normal file
108
.github/workflows/run-ppa.yml
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
name: Update PPA Packages
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package:
|
||||
description: 'Package to upload (dms, dms-git, or all)'
|
||||
required: false
|
||||
default: 'dms-git'
|
||||
rebuild_release:
|
||||
description: 'Release number for rebuilds (e.g., 2, 3, 4 for ppa2, ppa3, ppa4)'
|
||||
required: false
|
||||
default: ''
|
||||
schedule:
|
||||
- cron: '0 */3 * * *' # Every 3 hours for dms-git builds
|
||||
|
||||
jobs:
|
||||
upload-ppa:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
debhelper \
|
||||
devscripts \
|
||||
dput \
|
||||
lftp \
|
||||
build-essential \
|
||||
fakeroot \
|
||||
dpkg-dev
|
||||
|
||||
- name: Configure GPG
|
||||
env:
|
||||
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
run: |
|
||||
echo "$GPG_KEY" | gpg --import
|
||||
GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | awk '{print $2}' | cut -d'/' -f2)
|
||||
echo "DEBSIGN_KEYID=$GPG_KEY_ID" >> $GITHUB_ENV
|
||||
|
||||
- name: Determine packages to upload
|
||||
id: packages
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||
echo "packages=dms-git" >> $GITHUB_OUTPUT
|
||||
echo "Triggered by schedule: uploading git package"
|
||||
elif [[ -n "${{ github.event.inputs.package }}" ]]; then
|
||||
echo "packages=${{ github.event.inputs.package }}" >> $GITHUB_OUTPUT
|
||||
echo "Manual trigger: ${{ github.event.inputs.package }}"
|
||||
else
|
||||
echo "packages=dms-git" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Upload to PPA
|
||||
env:
|
||||
REBUILD_RELEASE: ${{ github.event.inputs.rebuild_release }}
|
||||
run: |
|
||||
PACKAGES="${{ steps.packages.outputs.packages }}"
|
||||
|
||||
if [[ "$PACKAGES" == "all" ]]; then
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Uploading dms to PPA..."
|
||||
if [ -n "$REBUILD_RELEASE" ]; then
|
||||
echo "🔄 Using rebuild release number: ppa$REBUILD_RELEASE"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
bash distro/scripts/ppa-upload.sh "distro/ubuntu/dms" dms questing
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Uploading dms-git to PPA..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
bash distro/scripts/ppa-upload.sh "distro/ubuntu/dms-git" dms-git questing
|
||||
else
|
||||
PPA_NAME="$PACKAGES"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Uploading $PACKAGES to PPA..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
bash distro/scripts/ppa-upload.sh "distro/ubuntu/$PACKAGES" "$PPA_NAME" questing
|
||||
fi
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "### PPA Package Upload Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Packages**: ${{ steps.packages.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
PACKAGES="${{ steps.packages.outputs.packages }}"
|
||||
if [[ "$PACKAGES" == "all" ]]; then
|
||||
echo "- **PPA dms**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **PPA dms-git**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
|
||||
elif [[ "$PACKAGES" == "dms" ]]; then
|
||||
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms/+packages" >> $GITHUB_STEP_SUMMARY
|
||||
elif [[ "$PACKAGES" == "dms-git" ]]; then
|
||||
echo "- **PPA**: https://launchpad.net/~avengemedia/+archive/ubuntu/dms-git/+packages" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [[ -n "${{ steps.packages.outputs.version }}" ]]; then
|
||||
echo "- **Version**: ${{ steps.packages.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Builds will appear once Launchpad processes the uploads." >> $GITHUB_STEP_SUMMARY
|
||||
20
.github/workflows/update-vendor-hash.yml
vendored
20
.github/workflows/update-vendor-hash.yml
vendored
@@ -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
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -27,7 +27,6 @@ qrc_*.cpp
|
||||
ui_*.h
|
||||
*.qmlc
|
||||
*.jsc
|
||||
Makefile*
|
||||
*build-*
|
||||
*.qm
|
||||
*.prl
|
||||
@@ -137,3 +136,9 @@ go.work.sum
|
||||
# .vscode/
|
||||
|
||||
bin/
|
||||
|
||||
# Extracted source trees in Ubuntu package directories
|
||||
distro/ubuntu/*/dms-git-repo/
|
||||
distro/ubuntu/*/DankMaterialShell-*/
|
||||
distro/ubuntu/danklinux/*/dsearch-*/
|
||||
distro/ubuntu/danklinux/*/dgop-*/
|
||||
|
||||
@@ -2,28 +2,42 @@
|
||||
|
||||
Contributions are welcome and encouraged.
|
||||
|
||||
## Formatting
|
||||
To contribute fork this repository, make your changes, and open a pull request.
|
||||
|
||||
The preferred tool for formatting files is [qmlfmt](https://github.com/jesperhh/qmlfmt) (also available on aur as qmlfmt-git). It actually kinda sucks, but `qmlformat` doesn't work with null safe operators and ternarys and pragma statements and a bunch of other things that are supported.
|
||||
## VSCode Setup
|
||||
|
||||
We need some consistent style, so this at least gives the same formatter that Qt Creator uses.
|
||||
This is a monorepo, the easiest thing to do is to open an editor in either `quickshell`, `core`, or both depending on which part of the project you are working on.
|
||||
|
||||
You can configure it to format on save in vscode by configuring the "custom local formatters" extension then adding this to settings json.
|
||||
### QML (`quickshell` directory)
|
||||
|
||||
1. Install the [QML Extension](https://doc.qt.io/vscodeext/)
|
||||
2. Configure `ctrl+shift+p` -> user preferences (json) with qmlls path
|
||||
|
||||
```json
|
||||
"customLocalFormatters.formatters": [
|
||||
{
|
||||
"command": "sh -c \"qmlfmt -t 4 -i 4 -b 250 | sed 's/pragma ComponentBehavior$/pragma ComponentBehavior: Bound/g'\"",
|
||||
"languages": ["qml"]
|
||||
}
|
||||
],
|
||||
"[qml]": {
|
||||
"editor.defaultFormatter": "jkillian.custom-local-formatters",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
{
|
||||
"qt-qml.doNotAskForQmllsDownload": true,
|
||||
"qt-qml.qmlls.customExePath": "/usr/lib/qt6/bin/qmlls"
|
||||
}
|
||||
```
|
||||
|
||||
Sometimes it just breaks code though. Like turning `"_\""` into `"_""`, so you may not want to do formatOnSave.
|
||||
3. Create empty `.qmlls.ini` file in `quickshell/` directory
|
||||
|
||||
```bash
|
||||
cd quickshell
|
||||
touch .qmlls.ini
|
||||
```
|
||||
|
||||
4. Restart dms to generate the `.qmlls.ini` file
|
||||
|
||||
5. Make your changes, test, and open a pull request.
|
||||
|
||||
### GO (`core` directory)
|
||||
|
||||
1. Install the [Go Extension](https://code.visualstudio.com/docs/languages/go)
|
||||
2. Ensure code is formatted with `make fmt`
|
||||
3. Add appropriate test coverage and ensure tests pass with `make test`
|
||||
4. Run `go mod tidy`
|
||||
5. Open pull request
|
||||
|
||||
## Pull request
|
||||
|
||||
|
||||
29
README.md
29
README.md
@@ -15,15 +15,15 @@
|
||||
[](https://github.com/AvengeMedia/DankMaterialShell/releases)
|
||||
[](https://aur.archlinux.org/packages/dms-shell-bin)
|
||||
[)](https://aur.archlinux.org/packages/dms-shell-git)
|
||||
[](https://ko-fi.com/avengemediallc)
|
||||
[](https://ko-fi.com/danklinux)
|
||||
|
||||
</div>
|
||||
|
||||
DankMaterialShell is a complete desktop shell for [niri](https://github.com/YaLTeR/niri), [Hyprland](https://hyprland.org/), [MangoWC](https://github.com/DreamMaoMao/mangowc), [Sway](https://swaywm.org), and other Wayland compositors. It replaces waybar, swaylock, swayidle, mako, fuzzel, polkit, and everything else you'd normally stitch together to make a desktop.
|
||||
DankMaterialShell is a complete desktop shell for [niri](https://github.com/YaLTeR/niri), [Hyprland](https://hyprland.org/), [MangoWC](https://github.com/DreamMaoMao/mangowc), [Sway](https://swaywm.org), [labwc](https://labwc.github.io/), and other Wayland compositors. It replaces waybar, swaylock, swayidle, mako, fuzzel, polkit, and everything else you'd normally stitch together to make a desktop.
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
@@ -103,7 +105,7 @@ Extend functionality with the [plugin registry](https://plugins.danklinux.com).
|
||||
|
||||
## Supported Compositors
|
||||
|
||||
Works best with [niri](https://github.com/YaLTeR/niri), [Hyprland](https://hyprland.org/), [Sway](https://swaywm.org/), and [MangoWC](https://github.com/DreamMaoMao/mangowc) with full workspace switching, overview integration, and monitor management. Other Wayland compositors work with reduced features.
|
||||
Works best with [niri](https://github.com/YaLTeR/niri), [Hyprland](https://hyprland.org/), [Sway](https://swaywm.org/), [MangoWC](https://github.com/DreamMaoMao/mangowc), and [labwc](https://labwc.github.io/) with full workspace switching, overview integration, and monitor management. Other Wayland compositors work with reduced features.
|
||||
|
||||
[Compositor configuration guide](https://danklinux.com/docs/dankmaterialshell/compositors)
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -182,6 +183,10 @@ For documentation contributions, see [DankLinux-Docs](https://github.com/AvengeM
|
||||
- [soramanew](https://github.com/soramanew) - [Caelestia](https://github.com/caelestia-dots/shell) inspiration
|
||||
- [end-4](https://github.com/end-4) - [dots-hyprland](https://github.com/end-4/dots-hyprland) inspiration
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#AvengeMedia/DankMaterialShell&type=date&legend=top-left)
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See [LICENSE](LICENSE) for details.
|
||||
|
||||
@@ -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
48
core/.mockery.yml
Normal 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/DankMaterialShell/core/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
157
core/Makefile
Normal 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"
|
||||
@@ -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
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -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...")
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -368,6 +368,7 @@ func getCommonCommands() []*cobra.Command {
|
||||
pluginsCmd,
|
||||
dank16Cmd,
|
||||
brightnessCmd,
|
||||
dpmsCmd,
|
||||
keybindsCmd,
|
||||
greeterCmd,
|
||||
setupCmd,
|
||||
@@ -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))
|
||||
}
|
||||
84
core/cmd/dms/commands_dpms.go
Normal file
84
core/cmd/dms/commands_dpms.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var dpmsCmd = &cobra.Command{
|
||||
Use: "dpms",
|
||||
Short: "Control display power management",
|
||||
}
|
||||
|
||||
var dpmsOnCmd = &cobra.Command{
|
||||
Use: "on [output]",
|
||||
Short: "Turn display(s) on",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Run: runDPMSOn,
|
||||
}
|
||||
|
||||
var dpmsOffCmd = &cobra.Command{
|
||||
Use: "off [output]",
|
||||
Short: "Turn display(s) off",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Run: runDPMSOff,
|
||||
}
|
||||
|
||||
var dpmsListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List outputs",
|
||||
Args: cobra.NoArgs,
|
||||
Run: runDPMSList,
|
||||
}
|
||||
|
||||
func init() {
|
||||
dpmsCmd.AddCommand(dpmsOnCmd, dpmsOffCmd, dpmsListCmd)
|
||||
}
|
||||
|
||||
func runDPMSOn(cmd *cobra.Command, args []string) {
|
||||
outputName := ""
|
||||
if len(args) > 0 {
|
||||
outputName = args[0]
|
||||
}
|
||||
|
||||
client, err := newDPMSClient()
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
if err := client.SetDPMS(outputName, true); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func runDPMSOff(cmd *cobra.Command, args []string) {
|
||||
outputName := ""
|
||||
if len(args) > 0 {
|
||||
outputName = args[0]
|
||||
}
|
||||
|
||||
client, err := newDPMSClient()
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
if err := client.SetDPMS(outputName, false); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func runDPMSList(cmd *cobra.Command, args []string) {
|
||||
client, err := newDPMSClient()
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
for _, output := range client.ListOutputs() {
|
||||
fmt.Println(output)
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
345
core/cmd/dms/dpms_client.go
Normal file
345
core/cmd/dms/dpms_client.go
Normal file
@@ -0,0 +1,345 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_power"
|
||||
wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
|
||||
)
|
||||
|
||||
type cmd struct {
|
||||
fn func()
|
||||
done chan error
|
||||
}
|
||||
|
||||
type dpmsClient struct {
|
||||
display *wlclient.Display
|
||||
ctx *wlclient.Context
|
||||
powerMgr *wlr_output_power.ZwlrOutputPowerManagerV1
|
||||
outputs map[string]*outputState
|
||||
mu sync.Mutex
|
||||
syncRound int
|
||||
done bool
|
||||
err error
|
||||
cmdq chan cmd
|
||||
stopChan chan struct{}
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
type outputState struct {
|
||||
wlOutput *wlclient.Output
|
||||
powerCtrl *wlr_output_power.ZwlrOutputPowerV1
|
||||
name string
|
||||
mode uint32
|
||||
failed bool
|
||||
waitCh chan struct{}
|
||||
wantMode *uint32
|
||||
}
|
||||
|
||||
func (c *dpmsClient) post(fn func()) {
|
||||
done := make(chan error, 1)
|
||||
select {
|
||||
case c.cmdq <- cmd{fn: fn, done: done}:
|
||||
<-done
|
||||
case <-c.stopChan:
|
||||
}
|
||||
}
|
||||
|
||||
func (c *dpmsClient) waylandActor() {
|
||||
defer c.wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-c.stopChan:
|
||||
return
|
||||
case cmd := <-c.cmdq:
|
||||
cmd.fn()
|
||||
close(cmd.done)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newDPMSClient() (*dpmsClient, error) {
|
||||
display, err := wlclient.Connect("")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to Wayland: %w", err)
|
||||
}
|
||||
|
||||
c := &dpmsClient{
|
||||
display: display,
|
||||
ctx: display.Context(),
|
||||
outputs: make(map[string]*outputState),
|
||||
cmdq: make(chan cmd, 128),
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
c.wg.Add(1)
|
||||
go c.waylandActor()
|
||||
|
||||
registry, err := display.GetRegistry()
|
||||
if err != nil {
|
||||
display.Context().Close()
|
||||
return nil, fmt.Errorf("failed to get registry: %w", err)
|
||||
}
|
||||
|
||||
registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
|
||||
switch e.Interface {
|
||||
case wlr_output_power.ZwlrOutputPowerManagerV1InterfaceName:
|
||||
powerMgr := wlr_output_power.NewZwlrOutputPowerManagerV1(c.ctx)
|
||||
version := e.Version
|
||||
if version > 1 {
|
||||
version = 1
|
||||
}
|
||||
if err := registry.Bind(e.Name, e.Interface, version, powerMgr); err == nil {
|
||||
c.powerMgr = powerMgr
|
||||
}
|
||||
|
||||
case "wl_output":
|
||||
output := wlclient.NewOutput(c.ctx)
|
||||
version := e.Version
|
||||
if version > 4 {
|
||||
version = 4
|
||||
}
|
||||
if err := registry.Bind(e.Name, e.Interface, version, output); err == nil {
|
||||
outputID := fmt.Sprintf("output-%d", output.ID())
|
||||
state := &outputState{
|
||||
wlOutput: output,
|
||||
name: outputID,
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
c.outputs[outputID] = state
|
||||
c.mu.Unlock()
|
||||
|
||||
output.SetNameHandler(func(ev wlclient.OutputNameEvent) {
|
||||
c.mu.Lock()
|
||||
delete(c.outputs, state.name)
|
||||
state.name = ev.Name
|
||||
c.outputs[ev.Name] = state
|
||||
c.mu.Unlock()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
syncCallback, err := display.Sync()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, fmt.Errorf("failed to sync display: %w", err)
|
||||
}
|
||||
syncCallback.SetDoneHandler(func(e wlclient.CallbackDoneEvent) {
|
||||
c.handleSync()
|
||||
})
|
||||
|
||||
for !c.done {
|
||||
if err := c.ctx.Dispatch(); err != nil {
|
||||
c.Close()
|
||||
return nil, fmt.Errorf("dispatch error: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.err != nil {
|
||||
c.Close()
|
||||
return nil, c.err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *dpmsClient) handleSync() {
|
||||
c.syncRound++
|
||||
|
||||
switch c.syncRound {
|
||||
case 1:
|
||||
if c.powerMgr == nil {
|
||||
c.err = fmt.Errorf("wlr-output-power-management protocol not supported by compositor")
|
||||
c.done = true
|
||||
return
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
for _, state := range c.outputs {
|
||||
powerCtrl, err := c.powerMgr.GetOutputPower(state.wlOutput)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
state.powerCtrl = powerCtrl
|
||||
|
||||
powerCtrl.SetModeHandler(func(e wlr_output_power.ZwlrOutputPowerV1ModeEvent) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if state.powerCtrl == nil {
|
||||
return
|
||||
}
|
||||
state.mode = e.Mode
|
||||
if state.wantMode != nil && e.Mode == *state.wantMode && state.waitCh != nil {
|
||||
close(state.waitCh)
|
||||
state.wantMode = nil
|
||||
}
|
||||
})
|
||||
|
||||
powerCtrl.SetFailedHandler(func(e wlr_output_power.ZwlrOutputPowerV1FailedEvent) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if state.powerCtrl == nil {
|
||||
return
|
||||
}
|
||||
state.failed = true
|
||||
if state.waitCh != nil {
|
||||
close(state.waitCh)
|
||||
state.wantMode = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
c.mu.Unlock()
|
||||
|
||||
syncCallback, err := c.display.Sync()
|
||||
if err != nil {
|
||||
c.err = fmt.Errorf("failed to sync display: %w", err)
|
||||
c.done = true
|
||||
return
|
||||
}
|
||||
syncCallback.SetDoneHandler(func(e wlclient.CallbackDoneEvent) {
|
||||
c.handleSync()
|
||||
})
|
||||
|
||||
default:
|
||||
c.done = true
|
||||
}
|
||||
}
|
||||
|
||||
func (c *dpmsClient) ListOutputs() []string {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
names := make([]string, 0, len(c.outputs))
|
||||
for name := range c.outputs {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func (c *dpmsClient) SetDPMS(outputName string, on bool) error {
|
||||
var mode uint32
|
||||
if on {
|
||||
mode = uint32(wlr_output_power.ZwlrOutputPowerV1ModeOn)
|
||||
} else {
|
||||
mode = uint32(wlr_output_power.ZwlrOutputPowerV1ModeOff)
|
||||
}
|
||||
|
||||
var setErr error
|
||||
c.post(func() {
|
||||
c.mu.Lock()
|
||||
var waitStates []*outputState
|
||||
|
||||
if outputName == "" || outputName == "all" {
|
||||
if len(c.outputs) == 0 {
|
||||
c.mu.Unlock()
|
||||
setErr = fmt.Errorf("no outputs found")
|
||||
return
|
||||
}
|
||||
|
||||
for _, state := range c.outputs {
|
||||
if state.powerCtrl == nil {
|
||||
continue
|
||||
}
|
||||
state.wantMode = &mode
|
||||
state.waitCh = make(chan struct{})
|
||||
state.failed = false
|
||||
waitStates = append(waitStates, state)
|
||||
state.powerCtrl.SetMode(mode)
|
||||
}
|
||||
} else {
|
||||
state, ok := c.outputs[outputName]
|
||||
if !ok {
|
||||
c.mu.Unlock()
|
||||
setErr = fmt.Errorf("output not found: %s", outputName)
|
||||
return
|
||||
}
|
||||
if state.powerCtrl == nil {
|
||||
c.mu.Unlock()
|
||||
setErr = fmt.Errorf("output %s has nil powerCtrl", outputName)
|
||||
return
|
||||
}
|
||||
state.wantMode = &mode
|
||||
state.waitCh = make(chan struct{})
|
||||
state.failed = false
|
||||
waitStates = append(waitStates, state)
|
||||
state.powerCtrl.SetMode(mode)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
|
||||
deadline := time.Now().Add(10 * time.Second)
|
||||
|
||||
for _, state := range waitStates {
|
||||
c.mu.Lock()
|
||||
ch := state.waitCh
|
||||
c.mu.Unlock()
|
||||
|
||||
done := false
|
||||
for !done {
|
||||
if err := c.ctx.Dispatch(); err != nil {
|
||||
setErr = fmt.Errorf("dispatch error: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
c.mu.Lock()
|
||||
if state.failed {
|
||||
setErr = fmt.Errorf("compositor reported failed for %s", state.name)
|
||||
c.mu.Unlock()
|
||||
return
|
||||
}
|
||||
c.mu.Unlock()
|
||||
done = true
|
||||
default:
|
||||
if time.Now().After(deadline) {
|
||||
setErr = fmt.Errorf("timeout waiting for mode change on %s", state.name)
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
for _, state := range waitStates {
|
||||
if state.powerCtrl != nil {
|
||||
state.powerCtrl.Destroy()
|
||||
state.powerCtrl = nil
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
|
||||
c.display.Roundtrip()
|
||||
})
|
||||
|
||||
return setErr
|
||||
}
|
||||
|
||||
func (c *dpmsClient) Close() {
|
||||
close(c.stopChan)
|
||||
c.wg.Wait()
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
for _, state := range c.outputs {
|
||||
if state.powerCtrl != nil {
|
||||
state.powerCtrl.Destroy()
|
||||
}
|
||||
}
|
||||
c.outputs = nil
|
||||
|
||||
if c.powerMgr != nil {
|
||||
c.powerMgr.Destroy()
|
||||
c.powerMgr = nil
|
||||
}
|
||||
|
||||
if c.display != nil {
|
||||
c.ctx.Close()
|
||||
c.display = nil
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -57,6 +57,11 @@ func getRuntimeDir() string {
|
||||
return os.TempDir()
|
||||
}
|
||||
|
||||
func hasSystemdRun() bool {
|
||||
_, err := exec.LookPath("systemd-run")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func getPIDFilePath() string {
|
||||
return filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.pid", os.Getpid()))
|
||||
}
|
||||
@@ -165,6 +170,10 @@ func runShellInteractive(session bool) {
|
||||
cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules)
|
||||
}
|
||||
|
||||
if isSessionManaged && hasSystemdRun() {
|
||||
cmd.Env = append(cmd.Env, "DMS_DEFAULT_LAUNCH_PREFIX=systemd-run --user --scope")
|
||||
}
|
||||
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" {
|
||||
if !strings.HasPrefix(configPath, homeDir) {
|
||||
@@ -387,6 +396,10 @@ func runShellDaemon(session bool) {
|
||||
cmd.Env = append(cmd.Env, "QT_LOGGING_RULES="+qtRules)
|
||||
}
|
||||
|
||||
if isSessionManaged && hasSystemdRun() {
|
||||
cmd.Env = append(cmd.Env, "DMS_DEFAULT_LAUNCH_PREFIX=systemd-run --user --scope")
|
||||
}
|
||||
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err == nil && os.Getenv("DMS_DISABLE_HOT_RELOAD") == "" {
|
||||
if !strings.HasPrefix(configPath, homeDir) {
|
||||
@@ -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
26
core/cmd/dms/utils.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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=
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
|
||||
)
|
||||
|
||||
type ConfigDeployer struct {
|
||||
@@ -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"
|
||||
)
|
||||
@@ -125,6 +125,8 @@ windowrulev2 = noborder, class:^(kitty)$
|
||||
windowrulev2 = float, class:^(firefox)$, title:^(Picture-in-Picture)$
|
||||
windowrulev2 = float, class:^(zoom)$
|
||||
|
||||
# DMS windows floating by default
|
||||
windowrulev2 = float, class:^(org.quickshell)$
|
||||
windowrulev2 = opacity 0.9 0.9, floating:0, focus:0
|
||||
|
||||
layerrule = noanim, ^(quickshell)$
|
||||
@@ -218,6 +218,11 @@ window-rule {
|
||||
geometry-corner-radius 12
|
||||
clip-to-geometry true
|
||||
}
|
||||
// Open dms windows as floating by default
|
||||
window-rule {
|
||||
match app-id=r#"org.quickshell$"#
|
||||
open-floating true
|
||||
}
|
||||
binds {
|
||||
// === System & Overview ===
|
||||
Mod+D { spawn "niri" "msg" "action" "toggle-overview"; }
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/backend/internal/deps"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -19,10 +19,12 @@ func init() {
|
||||
Register("fedora-asahi-remix", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||
return NewFedoraDistribution(config, logChan)
|
||||
})
|
||||
|
||||
Register("bluefin", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||
return NewFedoraDistribution(config, logChan)
|
||||
})
|
||||
Register("ultramarine", "#00078b", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||
return NewFedoraDistribution(config, logChan)
|
||||
})
|
||||
}
|
||||
|
||||
type FedoraDistribution struct {
|
||||
@@ -165,7 +167,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 +205,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 +316,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 +371,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 +397,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 +434,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 +485,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)
|
||||
@@ -506,6 +508,14 @@ func (f *FedoraDistribution) installDNFPackages(ctx context.Context, packages []
|
||||
f.log(fmt.Sprintf("Installing DNF packages: %s", strings.Join(packages, ", ")))
|
||||
|
||||
args := []string{"dnf", "install", "-y"}
|
||||
|
||||
for _, pkg := range packages {
|
||||
if pkg == "niri" || pkg == "niri-git" {
|
||||
args = append(args, "--setopt=install_weak_deps=False")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
args = append(args, packages...)
|
||||
|
||||
progressChan <- InstallProgressMsg{
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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 {
|
||||
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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",
|
||||
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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",
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user