diff --git a/.gitignore b/.gitignore index f7c1890..d85a17b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,15 @@ riderModule.iml # Build and publish output publish/ +*.AppImage +appimagetool +appimagetool-*.AppImage +ProductivityCake.AppDir/usr/ +squashfs-root/ + +# Debug symbols +*.dbg +*.pdb # Ignore all markdown files except README.md and .github folder diff --git a/ProductivityCake.AppDir/.DirIcon b/ProductivityCake.AppDir/.DirIcon new file mode 120000 index 0000000..700bf81 --- /dev/null +++ b/ProductivityCake.AppDir/.DirIcon @@ -0,0 +1 @@ +productivitycake.png \ No newline at end of file diff --git a/ProductivityCake.AppDir/AppRun b/ProductivityCake.AppDir/AppRun new file mode 100755 index 0000000..303b16e --- /dev/null +++ b/ProductivityCake.AppDir/AppRun @@ -0,0 +1,20 @@ +#!/bin/bash + +# AppRun script for ProductivityCake +SELF=$(readlink -f "$0") +HERE=${SELF%/*} + +# Export library paths - include both usr/lib and usr/bin for .so files +export LD_LIBRARY_PATH="${HERE}/usr/lib:${HERE}/usr/bin:${LD_LIBRARY_PATH}" +export PATH="${HERE}/usr/bin:${PATH}" + +# Set data directory to user's home to avoid read-only filesystem issues +# This ensures the app writes data to ~/.local/share/ProductivityCake instead of the AppImage mount +export XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" +export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" + +# Change to user's home directory (not the AppImage mount point) +cd "$HOME" + +# Run the application +exec "${HERE}/usr/bin/ProductivityCake" "$@" diff --git a/ProductivityCake.AppDir/ProductivityCake.desktop b/ProductivityCake.AppDir/ProductivityCake.desktop new file mode 100644 index 0000000..8f3155f --- /dev/null +++ b/ProductivityCake.AppDir/ProductivityCake.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=ProductivityCake +Exec=ProductivityCake +Icon=productivitycake +Type=Application +Categories=Utility;Office;ProjectManagement; +Comment=A simple and elegant Pomodoro timer with project management +Terminal=false +StartupWMClass=ProductivityCake diff --git a/ProductivityCake.AppDir/productivitycake.png b/ProductivityCake.AppDir/productivitycake.png new file mode 100644 index 0000000..4af642a Binary files /dev/null and b/ProductivityCake.AppDir/productivitycake.png differ diff --git a/ProductivityCake/App.axaml.cs b/ProductivityCake/App.axaml.cs index 05c7cea..77cb10d 100644 --- a/ProductivityCake/App.axaml.cs +++ b/ProductivityCake/App.axaml.cs @@ -39,7 +39,10 @@ public override void OnFrameworkInitializationCompleted() collection.AddCommonServices(); _services = collection.BuildServiceProvider(); - var dataDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data"); + + // Use user's home directory for data storage (works with AppImage) + var homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + var dataDirectory = Path.Combine(homeDir, ".local", "share", "ProductivityCake", "Data"); Directory.CreateDirectory(dataDirectory); var vm = _services.GetRequiredService(); diff --git a/ProductivityCake/ProductivityCake.csproj b/ProductivityCake/ProductivityCake.csproj index 810243a..a8f3e20 100644 --- a/ProductivityCake/ProductivityCake.csproj +++ b/ProductivityCake/ProductivityCake.csproj @@ -23,8 +23,8 @@ - - + + PreserveNewest PreserveNewest diff --git a/ProductivityCake/Services/JsonDataService.cs b/ProductivityCake/Services/JsonDataService.cs index 9249c97..4ff363c 100644 --- a/ProductivityCake/Services/JsonDataService.cs +++ b/ProductivityCake/Services/JsonDataService.cs @@ -18,7 +18,9 @@ public class JsonDataService : IJsonDataService public JsonDataService(string fileName) { - var dataDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data"); + // Use user's home directory for data storage (works with AppImage) + var homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + var dataDirectory = Path.Combine(homeDir, ".local", "share", "ProductivityCake", "Data"); Directory.CreateDirectory(dataDirectory); _filePath = Path.Combine(dataDirectory, fileName); diff --git a/README.md b/README.md index 10440c5..417558b 100644 --- a/README.md +++ b/README.md @@ -67,13 +67,21 @@ A modern, lightweight desktop application for managing projects, tasks, and time ### Linux -1. Download `ProductivityCake-linux-x64.tar.gz` from [Releases](https://github.com/amuza2/ProductivityCake/releases) -2. Extract the archive: +**Option 1: AppImage (Recommended - Works on all distros)** + +1. Download `ProductivityCake-x.x.x-x86_64.AppImage` from [Releases](https://github.com/amuza2/ProductivityCake/releases) +2. Make it executable and run: ```bash - tar -xzf ProductivityCake-linux-x64.tar.gz + chmod +x ProductivityCake-*.AppImage + ./ProductivityCake-*.AppImage ``` -3. Run the application: + +**Option 2: Standalone Binary** + +1. Download `ProductivityCake-linux-x64.tar.gz` from [Releases](https://github.com/amuza2/ProductivityCake/releases) +2. Extract and run: ```bash + tar -xzf ProductivityCake-linux-x64.tar.gz ./ProductivityCake ``` @@ -157,6 +165,30 @@ cd publish/linux-x64 tar -czf ProductivityCake-linux-x64.tar.gz ProductivityCake alarm.mp3 ``` +### Build AppImage (Universal Linux Package) + +**Prerequisites for AppImage:** +```bash +# On Arch/EndeavourOS +sudo pacman -S fuse2 + +# On Ubuntu/Debian +sudo apt install fuse libfuse2 +``` + +**Build the AppImage:** +```bash +chmod +x build-appimage.sh +./build-appimage.sh +``` + +This creates a universal `ProductivityCake-1.1.0-x86_64.AppImage` that works on: +- ✅ Arch Linux / EndeavourOS / Manjaro +- ✅ Ubuntu / Debian / Linux Mint +- ✅ Fedora / RHEL / CentOS +- ✅ openSUSE +- ✅ Any Linux distro with FUSE support + ## 🏗️ Technology Stack diff --git a/build-appimage.sh b/build-appimage.sh new file mode 100755 index 0000000..1411103 --- /dev/null +++ b/build-appimage.sh @@ -0,0 +1,116 @@ +#!/bin/bash + +# ProductivityCake AppImage Build Script +# This script builds a universal AppImage for all Linux distributions + +set -e # Exit on error + +VERSION="1.1.0" +ARCH="x86_64" +APP_NAME="ProductivityCake" +APPDIR="${APP_NAME}.AppDir" + +echo "🎂 Building ${APP_NAME} AppImage v${VERSION}..." +echo "" + +# Check for required tools +if ! command -v appimagetool &> /dev/null; then + echo "⚠️ appimagetool not found. Downloading..." + wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${ARCH}.AppImage -O appimagetool + chmod +x appimagetool + APPIMAGETOOL="./appimagetool" +else + APPIMAGETOOL="appimagetool" +fi + +# Clean previous builds +echo "🧹 Cleaning previous builds..." +rm -rf ./publish/linux-x64 +rm -rf ./${APPDIR}/usr + +# Publish the application +echo "📦 Publishing native AOT binary..." +dotnet publish ProductivityCake/ProductivityCake.csproj \ + -c Release \ + -r linux-x64 \ + --self-contained \ + -o ./publish/linux-x64 + +echo "" +echo "✅ Build completed successfully!" +echo "" + +# Create AppDir structure +echo "📁 Creating AppImage directory structure..." +mkdir -p ${APPDIR}/usr/bin +mkdir -p ${APPDIR}/usr/lib +mkdir -p ${APPDIR}/usr/share/applications +mkdir -p ${APPDIR}/usr/share/icons/hicolor/256x256/apps + +# Copy application files +echo "📋 Copying application files..." +cp ./publish/linux-x64/ProductivityCake ${APPDIR}/usr/bin/ + +# Copy shared libraries +if [ -f "./publish/linux-x64/libSkiaSharp.so" ]; then + cp ./publish/linux-x64/libSkiaSharp.so ${APPDIR}/usr/lib/ + echo "✅ Copied libSkiaSharp.so" +fi +if [ -f "./publish/linux-x64/libHarfBuzzSharp.so" ]; then + cp ./publish/linux-x64/libHarfBuzzSharp.so ${APPDIR}/usr/lib/ + echo "✅ Copied libHarfBuzzSharp.so" +fi + +# Copy any other .so files +for lib in ./publish/linux-x64/*.so; do + if [ -f "$lib" ]; then + cp "$lib" ${APPDIR}/usr/lib/ + echo "✅ Copied $(basename $lib)" + fi +done + +# Copy alarm.mp3 (check multiple locations) +if [ -f "./publish/linux-x64/alarm.mp3" ]; then + cp ./publish/linux-x64/alarm.mp3 ${APPDIR}/usr/bin/ + echo "✅ Copied alarm.mp3 from publish directory" +elif [ -f "./publish/linux-x64/Assets/alarm.mp3" ]; then + cp ./publish/linux-x64/Assets/alarm.mp3 ${APPDIR}/usr/bin/ + echo "✅ Copied alarm.mp3 from Assets directory" +elif [ -f "./ProductivityCake/Assets/alarm.mp3" ]; then + cp ./ProductivityCake/Assets/alarm.mp3 ${APPDIR}/usr/bin/ + echo "✅ Copied alarm.mp3 from source Assets directory" +else + echo "⚠️ Warning: alarm.mp3 not found, sound notifications may not work" +fi + +# Copy icon (convert from PNG to use as app icon) +if [ -f "ProductivityCake/Assets/icons8-cake-96.png" ]; then + cp ProductivityCake/Assets/icons8-cake-96.png ${APPDIR}/productivitycake.png + cp ProductivityCake/Assets/icons8-cake-96.png ${APPDIR}/usr/share/icons/hicolor/256x256/apps/productivitycake.png +else + echo "⚠️ Warning: Icon file not found" +fi + +# Copy desktop file +cp ${APPDIR}/ProductivityCake.desktop ${APPDIR}/usr/share/applications/ + +# Make AppRun executable +chmod +x ${APPDIR}/AppRun + +# Build AppImage +echo "" +echo "🔨 Building AppImage..." +ARCH=${ARCH} ${APPIMAGETOOL} ${APPDIR} ${APP_NAME}-${VERSION}-${ARCH}.AppImage + +echo "" +echo "✅ AppImage created successfully!" +echo "" +echo "📦 Output: ./${APP_NAME}-${VERSION}-${ARCH}.AppImage" +echo "" +echo "🚀 To run the AppImage:" +echo " chmod +x ${APP_NAME}-${VERSION}-${ARCH}.AppImage" +echo " ./${APP_NAME}-${VERSION}-${ARCH}.AppImage" +echo "" +echo "📤 To distribute:" +echo " Upload ${APP_NAME}-${VERSION}-${ARCH}.AppImage to GitHub Releases" +echo ""