Skip to content
Open
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
13,276 changes: 13,276 additions & 0 deletions build/generated-sources.json

Large diffs are not rendered by default.

75 changes: 75 additions & 0 deletions build/io.solidtime.app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# yaml-language-server: $schema=https://github.com/flatpak/flatpak-builder/raw/refs/heads/main/data/flatpak-manifest.schema.json

app-id: io.solidtime.app
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk
base: org.electronjs.Electron2.BaseApp
base-version: '25.08'
sdk-extensions:
- org.freedesktop.Sdk.Extension.node24
command: run.sh
separate-locales: false
finish-args:
- --share=ipc
- --device=dri
- --socket=wayland
- --socket=fallback-x11
- --socket=pulseaudio
- --share=network
- --env=XCURSOR_PATH=/run/host/user-share/icons:/run/host/share/icons
# System tray
- --talk-name=org.kde.StatusNotifierWatcher
# Notifications
- --talk-name=org.freedesktop.Notifications
# Window activity tracking for GNOME
- --talk-name=org.gnome.Shell.Extensions
- --filesystem=~/.local/share/gnome-shell/extensions/x-win@miniben90.org:create
# Window activity tracking for KDE
- --talk-name=org.kde.KWin
- --talk-name=org.freedesktop.DBus
- --own-name=io.solidtime.desktop.ActivityTracker
build-options:
append-path: /usr/lib/sdk/node24/bin
env:
NPM_CONFIG_LOGLEVEL: info
modules:
- name: solidtime
buildsystem: simple
subdir: main
build-options:
env:
XDG_CACHE_HOME: /run/build/solidtime/flatpak-node/cache
npm_config_cache: /run/build/solidtime/flatpak-node/npm-cache
npm_config_offline: 'true'
build-commands:
# Install npm dependencies
- npm install --offline
# Build the app
- |
. ../flatpak-node/electron-builder-arch-args.sh
npm run build:linux -- $ELECTRON_BUILDER_ARCH_ARGS --dir
# Bundle app and dependencies
- cp -a dist/linux*unpacked /app/main
# Install app wrapper
- install -Dm755 -t /app/bin/ ../run.sh
# Install AppStream metadata
- install -Dm644 resources/solidtime.metainfo.xml /app/share/metainfo/io.solidtime.app.metainfo.xml
# Install desktop file and icon
- install -Dm644 resources/solidtime.desktop /app/share/applications/io.solidtime.app.desktop
- install -Dm644 resources/solidtime.svg /app/share/icons/hicolor/scalable/apps/io.solidtime.app.svg
sources:
# - type: archive
# url: https://github.com/flathub/electron-sample-app/archive/1.0.1.tar.gz
# sha256: a2feb3f1cf002a2e4e8900f718cc5c54db4ad174e48bfcfbddcd588c7b716d5b
# dest: main
- type: dir
dest: main
path: ..
# Wrapper to launch the app
# Regenerate with flatpak-node-generator npm package-lock.json -o build/generated-sources.json
- generated-sources.json
- type: script
dest-filename: run.sh
commands:
- zypak-wrapper.sh /app/main/solidtime "$@"
16,334 changes: 10,186 additions & 6,148 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "solidtime",
"version": "0.0.70-beta",
"description": "Desktop App for solidtime - the modern open-source time tracker",
"desktopName": "io.solidtime.app",
"main": "./out/main/index.js",
"author": {
"name": "solidtime",
Expand Down Expand Up @@ -49,6 +50,7 @@
"@sentry/vite-plugin": "^5.2.0",
"@solidtime/api": "^0.0.6",
"@solidtime/ui": "^0.0.21",
"axios": "^1.15.1",
"better-sqlite3": "^12.9.0",
"dbus-next": "^0.10.2",
"drizzle-orm": "^0.45.2",
Expand Down
9 changes: 9 additions & 0 deletions resources/solidtime.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Name=Solidtime
Comment=Time tracking app
Exec=run.sh %u
Icon=io.solidtime.app
Terminal=false
Categories=Utility;
MimeType=x-scheme-handler/solidtime;
37 changes: 37 additions & 0 deletions resources/solidtime.metainfo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>io.solidtime.app</id>
<name>Solidtime</name>
<summary>Modern open-source time tracking application for Freelancers and Agencies</summary>
<description>
<p>Time tracking: Track your time with a modern and easy-to-use interface</p>
<p>Projects: Create and manage projects and assign project members</p>
<p>Tasks: Create and manage tasks and assign tasks to projects</p>
<p>Clients: Create and manage clients and assign clients to projects</p>
<p>Billable rates: Set billable rates for projects, project members, organization members and organizations</p>
<p>Multiple organizations: Create and manage multiple organizations with one account</p>
<p>Roles and permissions: Create and manage organizations</p>
<p>Import: Import your time tracking data from other time tracking applications (Supported: Toggl, Clockify, Timeentry CSV)</p>
</description>
<metadata_license>CC0-1.0</metadata_license>
<project_license>AGPL-3.0</project_license>
<developer_name>Solidtime</developer_name>
<launchable type="desktop-id">io.solidtime.app.desktop</launchable>
<url type="homepage">https://github.com/solidtime-io/solidtime-desktop</url>
<url type="bugtracker">https://github.com/solidtime-io/solidtime-desktop/issues</url>
<url type="vcs-browser">https://github.com/solidtime-io/solidtime-desktop</url>
<screenshots>
<screenshot type="default">
<caption>Solidtime</caption>
<image>https://github.com/solidtime-io/solidtime/raw/main/docs/solidtime-banner.png</image>
</screenshot>
</screenshots>
<releases>
<release version="0.2.6" date="2026-04-20">
<description>
<p>Update for the 25.08 runtime.</p>
</description>
</release>
</releases>
<content_rating type="oars-1.1"/>
</component>
8 changes: 5 additions & 3 deletions src/main/deeplink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { app, BrowserWindow } from 'electron'
export function registerDeeplinkListeners(mainWindow: BrowserWindow) {
// handle linux and windows deeplinks
app.on('second-instance', (_, commandLine) => {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.show()
mainWindow.focus()
}
// the commandLine is array of strings in which last element is deep link url
mainWindow.webContents.send('openDeeplink', commandLine.pop())
// Search rather than pop() as zypak may append sandbox flags after the URL
const url = commandLine.find((arg) => arg.startsWith('solidtime://'))
if (url) {
mainWindow.webContents.send('openDeeplink', url)
}
})
// handle mac os deeplinks
app.on('open-url', (_, url) => {
Expand Down
4 changes: 4 additions & 0 deletions src/main/env.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export function isE2ETesting(): boolean {
return process.env.E2E_TESTING === 'true'
}

export function isFlatpak(): boolean {
return !!process.env.FLATPAK_ID
}
27 changes: 17 additions & 10 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import * as Sentry from '@sentry/electron/main'
import path from 'node:path'
import { stopIdleMonitoring } from './idleMonitor'

import { isE2ETesting } from './env'
import { isE2ETesting, isFlatpak } from './env'

// Global error handlers to capture full error details
process.on('uncaughtException', (error) => {
Expand Down Expand Up @@ -56,28 +56,35 @@ if (!isE2ETesting()) {
}
}

initializeAutoUpdater()
if (!isFlatpak()) {
initializeAutoUpdater()
}

Sentry.init({
dsn: 'https://cc0104f2ce88d4490bbde2750b6483c4@o4507102829543424.ingest.de.sentry.io/4507783414939728',
})

if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient('solidtime', process.execPath, [
path.resolve(process.argv[1]),
])
// In Flatpak the protocol handler is registered via the .desktop file; skip runtime registration
if (!isFlatpak()) {
if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient('solidtime', process.execPath, [
path.resolve(process.argv[1]),
])
}
} else {
app.setAsDefaultProtocolClient('solidtime')
}
} else {
app.setAsDefaultProtocolClient('solidtime')
}

function createWindow(): void {
// Create the browser window.
const mainWindow = initializeMainWindow(icon)
registerMainWindowListeners(mainWindow)
registerDeeplinkListeners(mainWindow)
registerAutoUpdateListeners(mainWindow)
if (!isFlatpak()) {
registerAutoUpdateListeners(mainWindow)
}

const miniWindow = initializeMiniWindow(icon)
registerMiniWindowListeners(miniWindow)
Expand Down
13 changes: 9 additions & 4 deletions src/main/tray.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { app, BrowserWindow, ipcMain, Menu, nativeImage, Tray, nativeTheme } from 'electron'
import { app, BrowserWindow, ipcMain, Menu, Tray, nativeTheme } from 'electron'
import activeTrayIcon from '../../resources/solidtime_trayTemplate@4x.png?asset'
import inactiveTrayIcon from '../../resources/solidtime_emptyTemplate@4x.png?asset'
import activeTrayIconInverted from '../../resources/solidtime_trayTemplate_inverted@4x.png?asset'
Expand All @@ -24,6 +24,11 @@ function getIconPath(active: boolean) {
return active ? activeTrayIcon : inactiveTrayIcon
}

// On Linux, always use the inverted version as system trays are dark
if (process.platform === 'linux') {
return active ? activeTrayIconInverted : inactiveTrayIconInverted
}

// On other platforms, manually handle dark mode
const isDarkMode = nativeTheme.shouldUseDarkColors
if (active) {
Expand Down Expand Up @@ -74,14 +79,14 @@ function buildMenu(mainWindow: BrowserWindow, timeEntry: TimeEntry | null) {
}

export function initializeTray(mainWindow: Electron.BrowserWindow) {
const tray = new Tray(nativeImage.createFromPath(getIconPath(false)))
const tray = new Tray(getIconPath(false))
tray.setToolTip('solidtime')
tray.setTitle('')
tray.setContextMenu(buildMenu(mainWindow, null))

nativeTheme.on('updated', () => {
const isRunning = isTimerRunning(currentTrayTimeEntry)
tray.setImage(nativeImage.createFromPath(getIconPath(isRunning)))
tray.setImage(getIconPath(isRunning))
})

return tray
Expand All @@ -104,7 +109,7 @@ export function registerTrayListeners(tray: Tray, mainWindow: BrowserWindow) {
const timeEntry = JSON.parse(serializedTimeEntry) as TimeEntry
currentTrayTimeEntry = timeEntry
const isRunning = isTimerRunning(timeEntry)
tray.setImage(nativeImage.createFromPath(getIconPath(isRunning)))
tray.setImage(getIconPath(isRunning))
tray.setToolTip(
isRunning ? 'solidtime - Timer is running' : 'solidtime - Timer is stopped'
)
Expand Down
1 change: 1 addition & 0 deletions src/preload/interface.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export interface IElectronAPI {
getXWinExtensionStatus: () => Promise<XWinExtensionStatus>
installXWinExtension: () => Promise<XWinExtensionActionResult>
enableXWinExtension: () => Promise<XWinExtensionActionResult>
isFlatpak: boolean
}

declare global {
Expand Down
1 change: 1 addition & 0 deletions src/preload/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ if (process.contextIsolated || true) {
getXWinExtensionStatus: () => ipcRenderer.invoke('getXWinExtensionStatus'),
installXWinExtension: () => ipcRenderer.invoke('installXWinExtension'),
enableXWinExtension: () => ipcRenderer.invoke('enableXWinExtension'),
isFlatpak: !!process.env.FLATPAK_ID,
})
} catch (error) {
console.error(error)
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/src/pages/SettingsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type LinuxXWinExtensionStatus = {
}

const router = useRouter()
const isFlatpak = window.electronAPI.isFlatpak
const queryClient = useQueryClient()
const preferredColor = usePreferredColorScheme()

Expand Down Expand Up @@ -551,6 +552,7 @@ watch(activityTrackingEnabled, (enabled) => {
</div>

<div
v-if="!isFlatpak"
class="bg-card-background rounded-lg border border-card-background-separator p-6 mb-6">
<div class="mb-4 text-lg font-medium">Updates</div>
<div class="flex items-center space-x-4">
Expand Down