feat: tooltips, two-column timer, font selector, tray behavior, icons, readme

- Custom tooltip directive (WCAG AAA) on every button in the app
- Two-column timer layout with sticky hero and recent entries sidebar
- Timer font selector with 16 monospace Google Fonts and live preview
- UI font selector with 15+ Google Fonts
- Close-to-tray and minimize-to-tray settings
- New app icons (no-glow variants), platform icon set
- Mini timer pop-out window
- Favorites strip with drag-reorder and inline actions
- Comprehensive README with feature documentation
- Remove tracked files that belong in gitignore
This commit is contained in:
Your Name
2026-02-21 01:15:57 +02:00
parent ef6255042d
commit ee82abe63e
144 changed files with 13351 additions and 3456 deletions

View File

@@ -19,6 +19,7 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
"ALTER TABLE clients ADD COLUMN tax_id TEXT",
"ALTER TABLE clients ADD COLUMN payment_terms TEXT",
"ALTER TABLE clients ADD COLUMN notes TEXT",
"ALTER TABLE clients ADD COLUMN currency TEXT",
];
for sql in &migration_columns {
match conn.execute(sql, []) {
@@ -52,6 +53,8 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
"ALTER TABLE projects ADD COLUMN budget_amount REAL DEFAULT NULL",
"ALTER TABLE projects ADD COLUMN rounding_override INTEGER DEFAULT NULL",
"ALTER TABLE projects ADD COLUMN timeline_override TEXT DEFAULT NULL",
"ALTER TABLE projects ADD COLUMN notes TEXT",
"ALTER TABLE projects ADD COLUMN currency TEXT",
];
for sql in &project_migrations {
match conn.execute(sql, []) {
@@ -76,9 +79,10 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
[],
)?;
// Migrate tasks table - add estimated_hours column (safe to re-run)
// Migrate tasks table (safe to re-run)
let task_migrations = [
"ALTER TABLE tasks ADD COLUMN estimated_hours REAL DEFAULT NULL",
"ALTER TABLE tasks ADD COLUMN hourly_rate REAL DEFAULT NULL",
];
for sql in &task_migrations {
match conn.execute(sql, []) {
@@ -302,6 +306,22 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
[],
)?;
// Migrate calendar_events table - add description column (safe to re-run)
let calendar_migrations = [
"ALTER TABLE calendar_events ADD COLUMN description TEXT",
];
for sql in &calendar_migrations {
match conn.execute(sql, []) {
Ok(_) => {}
Err(e) => {
let msg = e.to_string();
if !msg.contains("duplicate column") {
return Err(e);
}
}
}
}
conn.execute(
"CREATE TABLE IF NOT EXISTS timesheet_locks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -345,6 +365,38 @@ pub fn init_db(conn: &Connection) -> Result<(), rusqlite::Error> {
[],
)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS invoice_payments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
invoice_id INTEGER NOT NULL,
amount REAL NOT NULL,
date TEXT NOT NULL,
method TEXT,
notes TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (invoice_id) REFERENCES invoices(id) ON DELETE CASCADE
)",
[],
)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS recurring_invoices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
client_id INTEGER NOT NULL,
template_id TEXT,
line_items_json TEXT NOT NULL,
tax_rate REAL DEFAULT 0,
discount REAL DEFAULT 0,
notes TEXT,
recurrence_rule TEXT NOT NULL,
next_due_date TEXT NOT NULL,
enabled INTEGER DEFAULT 1,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (client_id) REFERENCES clients(id)
)",
[],
)?;
// Insert default settings
conn.execute(
"INSERT OR IGNORE INTO settings (key, value) VALUES ('hourly_rate', '50')",