Skip to content

Commit 9e6aa8e

Browse files
authored
Merge pull request #3 from Hermesiss/develop
macOS support Dark mode
2 parents 2dcd56d + c2afb98 commit 9e6aa8e

8 files changed

Lines changed: 265 additions & 10 deletions

File tree

.github/workflows/build-and-release.yml

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,49 @@ jobs:
108108
${{ steps.find-installer.outputs.deb_path }}
109109
dist/DevNullifier-linux-unpacked.zip
110110
111+
build-macos:
112+
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true)
113+
runs-on: macos-latest
114+
115+
steps:
116+
- name: Checkout repository
117+
uses: actions/checkout@v4
118+
119+
- name: Setup Node.js
120+
uses: actions/setup-node@v4
121+
with:
122+
node-version: "18"
123+
cache: "npm"
124+
125+
- name: Install dependencies
126+
run: npm ci
127+
128+
- name: Build application
129+
run: npm run build:mac
130+
131+
- name: Create ZIP archive from unpacked
132+
run: |
133+
cd dist
134+
zip -r DevNullifier-mac-unpacked.zip mac/
135+
136+
- name: Find installer files
137+
id: find-installer
138+
run: |
139+
DMG=$(find dist -name "*.dmg" | head -1)
140+
echo "dmg_path=$DMG" >> $GITHUB_OUTPUT
141+
142+
- name: Upload macOS artifacts
143+
uses: actions/upload-artifact@v4
144+
with:
145+
name: macos-artifacts
146+
path: |
147+
${{ steps.find-installer.outputs.dmg_path }}
148+
dist/DevNullifier-mac-unpacked.zip
149+
111150
create-release:
112151
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true)
113152
runs-on: ubuntu-latest
114-
needs: [build-windows, build-linux]
153+
needs: [build-windows, build-linux, build-macos]
115154

116155
permissions:
117156
contents: write
@@ -132,6 +171,12 @@ jobs:
132171
name: linux-artifacts
133172
path: artifacts/linux/
134173

174+
- name: Download macOS artifacts
175+
uses: actions/download-artifact@v4
176+
with:
177+
name: macos-artifacts
178+
path: artifacts/macos/
179+
135180
- name: Create Release
136181
id: create_release
137182
uses: actions/create-release@v1
@@ -177,6 +222,12 @@ jobs:
177222
echo "appimage_path=$appimage" >> $GITHUB_OUTPUT
178223
echo "deb_path=$deb" >> $GITHUB_OUTPUT
179224
225+
- name: Find macOS files
226+
id: find-macos-files
227+
run: |
228+
dmg=$(find artifacts/macos -name "*.dmg" | head -1)
229+
echo "dmg_path=$dmg" >> $GITHUB_OUTPUT
230+
180231
- name: Upload Linux AppImage
181232
uses: actions/upload-release-asset@v1
182233
env:
@@ -206,3 +257,23 @@ jobs:
206257
asset_path: artifacts/linux/DevNullifier-linux-unpacked.zip
207258
asset_name: ${{ github.ref_name == 'develop' && 'DevNullifier-linux-unpacked-dev.zip' || 'DevNullifier-linux-unpacked.zip' }}
208259
asset_content_type: application/zip
260+
261+
- name: Upload macOS DMG
262+
uses: actions/upload-release-asset@v1
263+
env:
264+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
265+
with:
266+
upload_url: ${{ steps.create_release.outputs.upload_url }}
267+
asset_path: ${{ steps.find-macos-files.outputs.dmg_path }}
268+
asset_name: ${{ github.ref_name == 'develop' && 'DevNullifier-mac-x64-dev.dmg' || 'DevNullifier-mac-x64.dmg' }}
269+
asset_content_type: application/octet-stream
270+
271+
- name: Upload macOS ZIP Archive
272+
uses: actions/upload-release-asset@v1
273+
env:
274+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
275+
with:
276+
upload_url: ${{ steps.create_release.outputs.upload_url }}
277+
asset_path: artifacts/macos/DevNullifier-mac-unpacked.zip
278+
asset_name: ${{ github.ref_name == 'develop' && 'DevNullifier-mac-unpacked-dev.zip' || 'DevNullifier-mac-unpacked.zip' }}
279+
asset_content_type: application/zip

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22

33
All notable changes to DevNullifier will be documented in this file.
44

5+
## [1.2.2] - Dark Mode
6+
7+
### Added
8+
9+
- **Dark Mode**: Add dark mode to the application
10+
11+
## [1.2.1] - macOS Support
12+
13+
### Added
14+
15+
- **macOS Support**: Add support for macOS operating systems
16+
- **DMG Support**: Add support for DMG packaging
17+
- **ZIP Support**: Add support for ZIP packaging
18+
519
## [1.2.0] - Linux Support
620

721
### Added

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
A cross-platform cleaner for application data and developer caches (node_modules, .cache, Library, Binary, Intermediate, etc.) built with Electron, Vue 3, and Vuetify 3.
44

5+
Now with dark mode!
6+
57
**Supported Platforms:**
68

79
- Windows (AppData: Local, Roaming, LocalLow)
810
- Linux (User directories: ~/.config, ~/.cache, ~/.local/share, ~/.local/state, /tmp)
11+
- macOS (User directories: ~/Library/Caches, ~/Library/Application Support, ~/Library/Logs)
912

1013
## Download
1114

@@ -26,6 +29,7 @@ Cleans application data folders that accumulate temporary files, caches, and jun
2629

2730
- **Windows**: Temp folders, cache folders, crash dumps, pending files, log files in AppData (Local, Roaming, LocalLow)
2831
- **Linux**: Cache and temp folders in ~/.config, ~/.cache, ~/.local/share, ~/.local/state, /tmp
32+
- **macOS**: Cache and temp folders in ~/Library/Caches, ~/Library/Application Support, ~/Library/Logs
2933

3034
## Dev Cleaner
3135

@@ -87,6 +91,16 @@ build.bat # Build for production
8791
run.bat # Run built application
8892
```
8993

94+
**Using macOS:**
95+
96+
```bash
97+
npm install # Install dependencies
98+
npm run dev # Run in development
99+
npm run build # Build for production
100+
```
101+
102+
**Note for macOS users:** The app is unsigned, so you'll need to right-click → "Open" → "Open" the first time to bypass Gatekeeper.
103+
90104
## Usage
91105

92106
1. Launch the application
@@ -109,6 +123,7 @@ npm run build:renderer # Build renderer (Vue app)
109123
npm run build # Build entire app
110124
npm run build:win # Build for Windows
111125
npm run build:linux # Build for Linux
126+
npm run build:mac # Build for macOS
112127
```
113128

114129
## License

package.json

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "devnullifier",
3-
"version": "1.2.0",
3+
"version": "1.2.2",
44
"description": "DevNullifier: Clean application data, dev caches (node_modules, .cache, Library, Binary, Intermediate, etc) with Electron, Vue 3, and Vuetify 3.",
55
"main": "src/main/main.js",
66
"scripts": {
@@ -11,7 +11,8 @@
1111
"build:renderer": "vite build",
1212
"build:main": "electron-builder --publish=never",
1313
"build:linux": "npm run build:renderer && electron-builder --linux --publish=never",
14-
"build:win": "npm run build:renderer && electron-builder --win --publish=never"
14+
"build:win": "npm run build:renderer && electron-builder --win --publish=never",
15+
"build:mac": "npm run build:renderer && electron-builder --mac --publish=never"
1516
},
1617
"keywords": [
1718
"electron",
@@ -98,6 +99,26 @@
9899
"category": "Utility",
99100
"icon": "src/assets/icon-256.png"
100101
},
102+
"mac": {
103+
"target": [
104+
{
105+
"target": "dmg",
106+
"arch": [
107+
"x64",
108+
"arm64"
109+
]
110+
},
111+
{
112+
"target": "dir",
113+
"arch": [
114+
"x64",
115+
"arm64"
116+
]
117+
}
118+
],
119+
"category": "public.app-category.utilities",
120+
"icon": "src/assets/icon-512.png"
121+
},
101122
"nsis": {
102123
"oneClick": false,
103124
"allowToChangeInstallationDirectory": true

src/main/appDataCleaner.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const KEYWORDS = [
1414
"pending"
1515
];
1616

17-
// Get AppData paths (Windows) or equivalent user directories (Linux)
17+
// Get AppData paths (Windows) or equivalent user directories (Linux/macOS)
1818
function getAppDataPaths() {
1919
const paths = [];
2020

@@ -48,6 +48,24 @@ function getAppDataPaths() {
4848
paths.push(dirPath);
4949
}
5050
});
51+
} else if (process.platform === "darwin") {
52+
const homeDir = os.homedir();
53+
54+
// Common macOS user directories where apps store data
55+
const macOSPaths = [
56+
path.join(homeDir, "Library/Caches"), // Application caches
57+
path.join(homeDir, "Library/Application Support"), // Application data
58+
path.join(homeDir, "Library/Logs"), // Application logs
59+
path.join(homeDir, "Library/Preferences"), // Application preferences
60+
"/tmp" // System temporary files
61+
];
62+
63+
// Add paths that exist
64+
macOSPaths.forEach(dirPath => {
65+
if (fsSync.existsSync(dirPath)) {
66+
paths.push(dirPath);
67+
}
68+
});
5169
}
5270

5371
return paths;

src/renderer/App.vue

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,25 @@
55
<v-icon left>mdi-broom</v-icon>
66
DevNullifier
77
</v-app-bar-title>
8+
9+
<v-spacer></v-spacer>
10+
11+
<v-menu>
12+
<template v-slot:activator="{ props }">
13+
<v-btn icon v-bind="props">
14+
<v-icon>{{ getThemeIcon() }}</v-icon>
15+
</v-btn>
16+
</template>
17+
<v-list>
18+
<v-list-item v-for="mode in themeModes" :key="mode.value" @click="setThemeMode(mode.value)"
19+
:class="{ 'v-list-item--active': themeMode === mode.value }">
20+
<template v-slot:prepend>
21+
<v-icon>{{ mode.icon }}</v-icon>
22+
</template>
23+
<v-list-item-title>{{ mode.title }}</v-list-item-title>
24+
</v-list-item>
25+
</v-list>
26+
</v-menu>
827
</v-app-bar>
928

1029
<v-main>
@@ -32,18 +51,91 @@
3251
</v-container>
3352
</v-main>
3453

35-
36-
3754
<NotificationSnackbar v-model="showSnackbar" :text="snackbarText" :color="snackbarColor" />
3855
</v-app>
3956
</template>
4057

4158
<script setup>
42-
import { ref } from 'vue'
59+
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
60+
import { useTheme } from 'vuetify'
4361
import AppDataCleaner from './components/AppDataCleaner.vue'
4462
import DeveloperCleaner from './components/DeveloperCleaner.vue'
4563
import NotificationSnackbar from './components/NotificationSnackbar.vue'
4664
65+
// Theme functionality
66+
const theme = useTheme()
67+
const themeMode = ref('system') // 'light', 'dark', 'system'
68+
let mediaQuery = null
69+
70+
// Theme mode options
71+
const themeModes = [
72+
{ value: 'light', title: 'Light', icon: 'mdi-brightness-7' },
73+
{ value: 'dark', title: 'Dark', icon: 'mdi-brightness-4' },
74+
{ value: 'system', title: 'System', icon: 'mdi-brightness-auto' }
75+
]
76+
77+
// Get system theme preference
78+
const getSystemTheme = () => {
79+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
80+
}
81+
82+
// Apply theme based on mode
83+
const applyTheme = () => {
84+
if (themeMode.value === 'system') {
85+
theme.global.name.value = getSystemTheme()
86+
} else {
87+
theme.global.name.value = themeMode.value
88+
}
89+
}
90+
91+
// Get appropriate icon for current theme
92+
const getThemeIcon = () => {
93+
if (themeMode.value === 'system') {
94+
return 'mdi-brightness-auto'
95+
}
96+
return theme.global.name.value === 'dark' ? 'mdi-brightness-4' : 'mdi-brightness-7'
97+
}
98+
99+
// Set theme mode
100+
const setThemeMode = (mode) => {
101+
themeMode.value = mode
102+
applyTheme()
103+
}
104+
105+
// Handle system theme changes
106+
const handleSystemThemeChange = (e) => {
107+
if (themeMode.value === 'system') {
108+
theme.global.name.value = e.matches ? 'dark' : 'light'
109+
}
110+
}
111+
112+
// Load theme mode from localStorage on startup
113+
onMounted(() => {
114+
const savedThemeMode = localStorage.getItem('devnullifier-theme-mode')
115+
if (savedThemeMode && ['light', 'dark', 'system'].includes(savedThemeMode)) {
116+
themeMode.value = savedThemeMode
117+
}
118+
119+
// Apply initial theme
120+
applyTheme()
121+
122+
// Set up system theme change listener
123+
mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
124+
mediaQuery.addEventListener('change', handleSystemThemeChange)
125+
})
126+
127+
// Cleanup on unmount
128+
onUnmounted(() => {
129+
if (mediaQuery) {
130+
mediaQuery.removeEventListener('change', handleSystemThemeChange)
131+
}
132+
})
133+
134+
// Watch for theme mode changes and save to localStorage
135+
watch(themeMode, (newMode) => {
136+
localStorage.setItem('devnullifier-theme-mode', newMode)
137+
})
138+
47139
// Reactive state
48140
const activeTab = ref('appdata')
49141
const showSnackbar = ref(false)

0 commit comments

Comments
 (0)