Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions ProductivityCake.AppDir/.DirIcon
20 changes: 20 additions & 0 deletions ProductivityCake.AppDir/AppRun
Original file line number Diff line number Diff line change
@@ -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" "$@"
9 changes: 9 additions & 0 deletions ProductivityCake.AppDir/ProductivityCake.desktop
Original file line number Diff line number Diff line change
@@ -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
Binary file added ProductivityCake.AppDir/productivitycake.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion ProductivityCake/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
BindingPlugins.DataValidators.RemoveAt(0);

Check warning on line 36 in ProductivityCake/App.axaml.cs

View workflow job for this annotation

GitHub Actions / build

Using member 'Avalonia.Data.Core.Plugins.BindingPlugins.DataValidators.get' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. PropertyAccessors might require unreferenced code.

Check warning on line 36 in ProductivityCake/App.axaml.cs

View workflow job for this annotation

GitHub Actions / build

Using member 'Avalonia.Data.Core.Plugins.BindingPlugins.DataValidators.get' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. PropertyAccessors might require unreferenced code.

var collection = new ServiceCollection();
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<MainWindowViewModel>();
Expand Down Expand Up @@ -68,12 +71,12 @@
{
// Get an array of plugins to remove
var dataValidationPluginsToRemove =
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();

Check warning on line 74 in ProductivityCake/App.axaml.cs

View workflow job for this annotation

GitHub Actions / build

Using member 'Avalonia.Data.Core.Plugins.BindingPlugins.DataValidators.get' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. PropertyAccessors might require unreferenced code.

Check warning on line 74 in ProductivityCake/App.axaml.cs

View workflow job for this annotation

GitHub Actions / build

Using member 'Avalonia.Data.Core.Plugins.BindingPlugins.DataValidators.get' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. PropertyAccessors might require unreferenced code.

// remove each entry found
foreach (var plugin in dataValidationPluginsToRemove)
{
BindingPlugins.DataValidators.Remove(plugin);

Check warning on line 79 in ProductivityCake/App.axaml.cs

View workflow job for this annotation

GitHub Actions / build

Using member 'Avalonia.Data.Core.Plugins.BindingPlugins.DataValidators.get' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. PropertyAccessors might require unreferenced code.

Check warning on line 79 in ProductivityCake/App.axaml.cs

View workflow job for this annotation

GitHub Actions / build

Using member 'Avalonia.Data.Core.Plugins.BindingPlugins.DataValidators.get' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. PropertyAccessors might require unreferenced code.
}
}

Expand Down
4 changes: 2 additions & 2 deletions ProductivityCake/ProductivityCake.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
</PropertyGroup>

<ItemGroup>
<AvaloniaResource Include="Assets\**" Exclude="Assets\alarm.mp3;Assets\Icons.axaml"/>
<Content Include="Assets\alarm.mp3">
<AvaloniaResource Include="Assets/**" Exclude="Assets/alarm.mp3;Assets/Icons.axaml"/>
<Content Include="Assets/alarm.mp3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
Expand Down
4 changes: 3 additions & 1 deletion ProductivityCake/Services/JsonDataService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down Expand Up @@ -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

Expand Down
116 changes: 116 additions & 0 deletions build-appimage.sh
Original file line number Diff line number Diff line change
@@ -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 ""