From b5345af2b108084027888405301e74122fb0395c Mon Sep 17 00:00:00 2001 From: Robert Kleinschmager Date: Wed, 24 Jun 2026 20:30:40 +0200 Subject: [PATCH 1/2] chore: setup some agentic coding stuff --- .gitignore | 7 +++ AGENTS.md | 105 +++++++++++++++++++++++++++++++++++++ doc/ui-architecture.md | 115 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 AGENTS.md create mode 100644 doc/ui-architecture.md diff --git a/.gitignore b/.gitignore index fb6f41ea..7d7c43bc 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,10 @@ build /gen-src # mac stuff **/.DS_Store + +# KI / Agent stuff + +.agentbridge +opencode.jsonc +opencode.jsonc.tui-migration.bak +tui.json diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..4ec3f6f0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,105 @@ +# AGENTS.md - SimpleTimeTracking (STT) + +## Purpose + +SimpleTimeTracking (STT) is a cross-platform desktop application for **fast, unobtrusive time tracking**. +It prioritizes the individual user's workflow over management reporting — design goal is to let you start/stop tracking with minimal friction and copy results into other systems. + +## Use Cases + +- **Start/stop working** on a task with a single command or button click +- **Resume** the last or any previous activity +- **Search** across historical activities by comment text +- **Report** on time spent per activity/group (day, period, search filter) +- **Overtime** calculation against configured worktime rules +- **CSV/Jira export** for integration with other systems +- **Dual interface**: JavaFX GUI for desktop use, CLI for scripting/terminal use +- **Automatic backup** and item logging + +## Architecture + +### Tech Stack + +| Layer | Technology | +|---|---| +| Language | **Kotlin** (JVM), with some Java source files | +| UI | **JavaFX 21**, ControlsFX, RichTextFX | +| Build | **Gradle** (Kotlin DSL), JDK 21+ | +| DI | **Dagger 2** (annotation-based, `kapt`) | +| Parsing | **ANTLR 4** for command text parsing | +| Config | **YAML** (SnakeYAML) | +| Event Bus | **MBassador** (in-process pub/sub) | +| Testing | **JUnit 4**, **AssertJ**, **Mockito** (+ mockito-kotlin), TestFX | + +### Module / Package Layout + +All source under `src/main/kotlin/org/stt/`: + +``` +org.stt/ +├── cli/ # CLI entry points (Main, ReportPrinter, FormatConverter) +├── command/ # Command pattern: Commands.kt, CommandHandler.kt, CommandFormatter (ANTLR) +├── config/ # Configuration loading from YAML, config classes +├── connector/jira/ # Jira integration (REST client) +├── csv/ # CSV import/export +├── event/ # Event bus classes (NotifyUser, ShuttingDown, TimePassedEvent) +├── gui/ # JavaFX UI (MainWindow, ActivitiesController, ReportController, Settings) +├── model/ # Domain model (TimeTrackingItem, ReportingItem) +├── persistence/ # ItemReader / ItemWriter interfaces + STT file format implementation +├── query/ # Query/filter logic over time tracking items (Criteria, WorkTimeQueries) +├── reporting/ # Report generation (SummingReportGenerator, OvertimeReportGenerator) +├── text/ # Text completion, categorization, grouping (CommonPrefixGrouper, JiraExpansion) +├── time/ # Date/time utilities, duration rounding +├── update/ # Update check mechanism +├── validation/ # Input validation +``` + +> See [doc/ui-architecture.md](doc/ui-architecture.md) for a detailed breakdown of UI components, controllers, data objects, and event bus wiring. + +### Key Design Patterns + +- **Command pattern**: `Command` (sealed hierarchy) + `CommandHandler` interface (Visitor), parsed via ANTLR grammar +- **Dependency Injection**: Dagger `@Module` / `@Provides` / `@Inject`; components are `Dagger*Application` (e.g., `DaggerCLIApplication`, `DaggerUIApplication`) +- **Event-driven**: `MBassador` event bus for decoupled UI updates (time ticks, shutdown, notifications) +- **Repository**: `ItemReader` / `ItemWriter` interfaces abstract storage; `STTItemReader` / `STTItemWriter` implement the plain-text file format +- **Service lifecycle**: `Service` interface with `start()`/`stop()` for config, backup, and logging services +- **Data stored**: A plain-text file (`.stt/activities`) with one record per line + +## Code Style + +- **Language**: Kotlin (prefer immutable data classes, `val`, extension functions) +- **Naming**: `camelCase` for methods/variables, `PascalCase` for classes, no underscores +- **Test naming**: `should[Expectation]` — e.g., `shouldCreateItemWithoutEnd` +- **Test structure**: GIVEN / WHEN / THEN comment annotations, `sut` (system under test) variable +- **Imports**: explicit single imports (no wildcard `.*` except for standard lib / assertions) +- **Nullability**: explicit nullable types with `?`, prefer `?:` elvis operator +- **DI**: constructor injection via `@Inject`, module-provided bindings for platform/third-party types +- **Logging**: `java.util.logging.Logger` (`Logger.getLogger(...)`) +- **File format**: one time-tracking record per line, human-readable text + +## Build & Test + +```bash +./gradlew build # compile + test + assemble fat jar +./gradlew test # run all tests (JUnit 4) +./gradlew check # + static analysis (SonarQube if configured) +./gradlew dist # jlink + jpackage for native distribution +./gradlew run # compile and start the GUI application +``` + +Output fat jar: `build/libs/STT-.jar` + +## Commit Convention + +Use [Conventional Commits](https://www.conventionalcommits.org/): + +``` +feat: add resume-last-activity CLI command +fix: report crashes on empty activity list +refactor: extract DurationRounder from ReportGenerator +test: add overtime calculation edge cases +docs: update README with new CLI usage +chore: bump Dagger to 2.50 +``` + +Scopes (optional): `cli`, `gui`, `persistence`, `query`, `reporting`, `config`, `time`, `deps` \ No newline at end of file diff --git a/doc/ui-architecture.md b/doc/ui-architecture.md new file mode 100644 index 00000000..cdb61633 --- /dev/null +++ b/doc/ui-architecture.md @@ -0,0 +1,115 @@ +# SimpleTimeTracking — UI Architecture + +## Overview + +Single-window, **tab-based JavaFX interface** with four tabs. All panels live inside a `TabPane`; there are no separate windows. + +``` +UIMain (JavaFX Application entry point) + └── DaggerUIApplication (DI component) + └── MainWindowController (orchestrator) + ├── ActivitiesController + ├── ReportController + ├── SettingsController + └── InfoController +``` + +--- + +## 1. Activities Tab — `ActivitiesController.kt` + `ActivitiesPanel.fxml` + +Primary workspace: enter new time entries, view/search history, edit/delete/continue/stop items. + +| UI Element | Purpose | Data Object | +|---|---|---| +| **Command text area** (`StyleClassedTextArea` via RichTextFX) | Type activity text (ANTLR grammar); `Ctrl+Enter` executes, `Ctrl+Space` auto-completes | Parsed into `Command` sealed hierarchy (`StartItem`, `StopItem`, `DeleteItem`, `EditItem`, ...) | +| **Activity list** (`ListView`) | Scrollable list of all items, grouped by day with date headers | `ObservableArrayList` — full in-memory set | +| **Per-item action buttons** (continue, stop, edit, delete) | Appear on hover via `FadeTransition` in `TimeTrackingItemCellWithActions` | Operates on a single `TimeTrackingItem` | +| **Search/filter field** | Real-time list filtering | Filters `allItems` by activity text match via `TimeTrackingListFilter` | +| **Worktime pane** (`WorktimePane`, embedded `FlowPane`) | Shows remaining worktime today (or overtime) and weekly total | Queries `WorkTimeQueries`; updates every 1s via `TimePassedEvent` | + +**Data flow**: User types → `CommandFormatter` (ANTLR) parses → `ValidatingCommandHandler` validates → `CommandHandler` persists to `.stt/activities` → `ItemModified` events fire on the bus → all listeners refresh. + +**Dialogs triggered from this tab**: +- Delete-confirm: `STTOptionDialogs.showDeleteOrKeepDialog()` +- Overlap warning: `STTOptionDialogs.showItemCoversOtherItemsDialog()` +- Bulk-rename prompt: `STTOptionDialogs.showRenameDialog()` +- No-current-item: `STTOptionDialogs.showNoCurrentItemAndItemIsLaterDialog()` + +--- + +## 2. Report Tab — `ReportController.kt` + `ReportPanel.fxml` + +Daily report with date picker, grouped activity table, and summary sidebar. + +| UI Element | Purpose | Data Object | +|---|---|---| +| **DatePicker** (custom day-cell rendering) | Select a date; tracked days are highlighted | Returns `LocalDate` | +| **Report table** (`TableView`) | Grouped activities: comment, raw duration, rounded duration | `ReportListItem` (comment, isBreak, duration, roundedDuration) | +| **Summary labels** | Sidebar: total, effective, break, uncovered, non-effective, start/end | Computed from `SummingReportGenerator.Report` (`List` + duration totals) | + +**Data flow**: Date selected → `ReportBinding` (an `ObjectBinding`) computes a `Report` via `SummingReportGenerator` using a `Criteria` query → `MappedListBinding` transforms into `ReportListItem` rows. + +--- + +## 3. Settings Tab — `SettingsController.kt` (no FXML, programmatic UI) + +ControlsFX `PropertySheet` populated from config beans. + +| UI Element | Purpose | Data Object | +|---|---|---| +| **ControlsFX `PropertySheet`** | Auto-generated editable property grid | `ConfigRoot`, `ActivitiesConfig`, `BackupConfig`, `WorktimeConfig`, `JiraConfig`, `CommonPrefixGrouperConfig`, `ReportConfig`, `CliConfig` | +| **Custom editors** | `PathSetting` (file chooser button), `PasswordSetting` (obfuscated field) | Wrapper types for special config values | + +Persistence happens on shutdown via `ConfigServiceFacade`. + +--- + +## 4. Info Tab — `InfoController.kt` + `InfoPanel.fxml` + +App metadata and update check. + +| UI Element | Purpose | Data Object | +|---|---|---| +| **Version / Commit labels** | Display app metadata | `@Named("version")`, `@Named("commit hash")` strings | +| **"Check for updates" button** | Triggers async `UpdateChecker` | `UpdateChecker` queries a remote URL | +| **Project homepage link** | Opens browser | Hardcoded URL | + +--- + +## Event Bus Wiring + +All controllers communicate through a shared `MBassador` singleton (Dagger-provided). + +| Publisher | Event | Listeners | +|---|---|---| +| `ActivitiesController` | `ItemInserted`, `ItemDeleted`, `ItemReplaced` | `ActivitiesController` (self-refresh), `ReportController.OnItemChangeListener`, `WorktimePane` | +| `UIMain` (1s timer) | `TimePassedEvent` | `WorktimePane` (recalc worktime) | +| `MainWindowController` (ESC/close) | `ShuttingDown` | `UIMain` (service stop + `Platform.exit()`) | +| Any controller | `NotifyUser` | `MainWindowController` (show ControlsFX `Notification`) | +| `BulkRenameHelper` (inside ActivitiesController) | `ItemReplaced` (monitors single edits) | Detects single-item rename and prompts to rename all matching items | + +--- + +## Model Classes (Core) + +| Class | Key Fields | Role | +|---|---|---| +| `TimeTrackingItem` | `activity: String`, `start: LocalDateTime`, `end: LocalDateTime?` | Fundamental persistence unit — one line in `.stt/activities` | +| `ReportingItem` | `duration`, `roundedDuration`, `comment`, `isBreak` | Aggregate view for report rows | +| `SummingReportGenerator.Report` | `reportingItems`, `start`, `end`, `uncoveredDuration`, etc. | Complete daily report payload | +| `Command` (sealed) | Subtypes: `StartItem`, `StopItem`, `DeleteItem`, `EditItem`, ... | Parsed user input from command text area | +| `Criteria` | `start`, `end`, `activity` | Query specification for filtering items | +| `ItemModified` / `ItemInserted` / `ItemDeleted` / `ItemReplaced` | Respective payload fields | Event bus messages | + +## Config Objects (mapped to Settings tab) + +| Class | Purpose | +|---|---| +| `ActivitiesConfig` | UI behavior: grouping, filtering duplicates, close-on-continue/stop, ask-before-delete, delete-closes-gaps | +| `BackupConfig` | Backup file settings | +| `WorktimeConfig` | Worktime rules for overtime/remaining-time calculation | +| `JiraConfig` | Jira REST API credentials and settings | +| `CommonPrefixGrouperConfig` | Text grouping settings | +| `ReportConfig` | Report formatting options | +| `CliConfig` | CLI encoding settings | From 653d7ef37f12360890a60c4bdc727f9d35fb6b60 Mon Sep 17 00:00:00 2001 From: Robert Kleinschmager Date: Mon, 17 Aug 2026 21:07:40 +0200 Subject: [PATCH 2/2] ci: updated deprecated upload-artifact@v3 to v4 --- .github/workflows/common.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index a860dd74..96c67789 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -22,7 +22,7 @@ jobs: - name: Execute Gradle build run: ./gradlew ${{ inputs.gradle-tasks }} --scan - name: Upload dists - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: dist + name: dist-${{ inputs.runs-on }} path: build/dist/**/*