Commit graph

17954 commits

Author SHA1 Message Date
Johannes Millan
becd3a5547 fix(ci): add memory allocation to Mac test step to prevent OOM 2026-01-17 11:08:52 +01:00
Johannes Millan
3c5b441046 17.0.0-RC.5 2026-01-16 22:35:17 +01:00
Johannes Millan
0cd6dfaf43 17.0.0-RC.4 2026-01-16 22:34:50 +01:00
Johannes Millan
b0f4e99c0b test(e2e): fix flaky focus mode tests
Fixed 6 flaky E2E tests in focus mode by addressing race conditions
with countdown animation and session state transitions.

Changes:
- Added pointer-events: none to countdown component to prevent blocking
  clicks during fade-out animation (195ms)
- Replaced arbitrary timeouts with explicit waits for session-in-progress
  indicator (complete session button) in all focus mode tests
- Tests now wait for countdown animation to fully complete before
  interacting with UI elements

Root causes:
1. Countdown overlay intercepted pointer events during fade animation,
   causing clicks to fail intermittently
2. 900ms delay between countdown completion and session start caused
   race conditions when using fixed timeouts

Affected tests:
- focus-mode-break.spec.ts (4 tests)
- flowtime-timer-bug-5117.spec.ts (2 tests)

All tests now pass consistently without retries.
2026-01-16 22:34:50 +01:00
Johannes Millan
213d0ec010 chore(plugins): remove debug logging from procrastination-buster i18n 2026-01-16 22:34:50 +01:00
Johannes Millan
7401116bfb fix(plugins): reactively sync plugin i18n language with global config
Changed PluginI18nService constructor to use an effect() instead of
reading the language config once. This ensures the plugin i18n service
correctly picks up the language when config is loaded from persistence,
not just when it's manually changed.

This fixes the issue where plugins always started in English even when
the user had a different language selected, because the service was
initialized before the user's config was loaded.
2026-01-16 22:34:50 +01:00
Johannes Millan
9be6bf7c27 fix(plugins): initialize current language on plugin mount
Fixed i18n to fetch and use the current language when the plugin
initializes, instead of always defaulting to English. Now the plugin
will show the correct language immediately on first load, and also
update reactively when the language changes.

Changes:
- Added getCurrentLanguage() call in useTranslate initialization
- Changed onMount to createEffect for reactive language loading
- Updated both App.tsx and ProcrastinationInfo.tsx to use createEffect
- Applied fixes to both procrastination-buster and boilerplate
- Added debug logging to troubleshoot language change events

This fixes the issue where plugins always started in English regardless
of the user's selected language.
2026-01-16 22:34:50 +01:00
Johannes Millan
fb05e7be3e chore(plugins): remove debug logging from procrastination-buster 2026-01-16 22:34:50 +01:00
Johannes Millan
de264aff48 fix(plugins): fix i18n message protocol for iframe communication
Fixed the message passing between plugin iframes and plugin.ts to use
the proper PLUGIN_MESSAGE/PLUGIN_MESSAGE_RESPONSE protocol instead of
custom messages. This ensures translations and other messages are
correctly routed through the plugin bridge.

Changes:
- Updated useTranslate to use PLUGIN_MESSAGE type
- Listen for PLUGIN_MESSAGE_RESPONSE instead of custom response
- Wrap message in { type, payload } structure
- Updated plugin.ts to read from message.payload
- Added debug logging for troubleshooting
- Applied fixes to both procrastination-buster and boilerplate
2026-01-16 22:34:50 +01:00
Johannes Millan
1df40ab02d fix(plugins): fix translation loading in procrastination-buster
Changed message.data to message.payload in the onMessage handler
to match the structure sent by useTranslate hook. This fixes the
issue where translations were not loading in the UI.
2026-01-16 22:34:50 +01:00
Johannes Millan
92fd9301fe fix(plugins): remove duplicate side panel registration in procrastination-buster
Remove manual registerSidePanelButton() call since the side panel button
is already automatically registered via manifest.json with 'sidePanel: true'.

This fixes:
- Duplicate side panel button warning
- Plugin loading in both side panel and main view simultaneously

The manifest-based registration is sufficient and avoids duplication.
2026-01-16 22:34:50 +01:00
Johannes Millan
91fe3652dc fix(plugins): copy directories recursively in build script
Add copyRecursive() helper function to handle both files and directories
when copying plugin builds to bundled-plugins. This ensures i18n folders
and other directories are properly copied.

Previously, only files were copied (checking isFile()), which caused i18n
folders to be skipped. Now all three plugin build commands (procrastination-
buster, sync-md, ai-productivity-prompts) properly copy directory trees.

Fixes: 404 errors for i18n/de.json and other directory contents
2026-01-16 22:34:50 +01:00
Johannes Millan
cbc36012d8 test(e2e): fix add-to-today subtask tests
- Add Escape key press after creating subtasks to force exit from edit mode
- Simplify first test to remove both parent and subtask from Today, then use keyboard shortcut (most reliable)
- Fix context menu test to click sun icon button in quick-access area instead of searching for text
- Simplify "should NOT add subtask" test to avoid unreliable task counting
- Add proper wait times for Angular change detection cycles

All 5 tests now passing consistently.
2026-01-16 22:34:50 +01:00
Johannes Millan
ba0e9ce002 fix(plugins): resolve PluginHooks runtime error in procrastination-buster
Replace PluginHooks.LANGUAGE_CHANGE enum reference with string literal
'languageChange' to fix runtime error. The type-only import of PluginHooks
was not available at runtime, causing "PluginHooks is not defined" error
when installing the plugin.

The hook registration now uses the string value directly:
- Before: plugin.registerHook(PluginHooks.LANGUAGE_CHANGE, ...)
- After: plugin.registerHook('languageChange', ...)
2026-01-16 22:34:50 +01:00
Johannes Millan
0e0c04915c fix(tasks): prevent dueDay update on filtered subtasks in planTasksForToday
Fixes bug where planTasksForToday was setting dueDay on all taskIds,
including subtasks that were filtered out from being added to TODAY_TAG
(e.g., subtasks whose parents are already in Today). This caused state
inconsistencies where subtasks would have today's dueDay but not be in
the TODAY_TAG.

Now only updates dueDay for tasks that are either:
1. Being added to TODAY_TAG (newTasksForToday), OR
2. Already in TODAY_TAG but have incorrect dueDay

Also improves E2E test selectors:
- Use proper DOM structure (.task-list-inner > task)
- Add hover timeout for Angular change detection
- Fix context menu selector to use Material menu pattern
2026-01-16 22:34:50 +01:00
Johannes Millan
ce17c00690 feat(plugins): add German translations to procrastination-buster
- Add comprehensive de.json with 80 German translation keys
- Update manifest.json to include German language support
- Translate all UI strings: home, navigation, strategies, info
- Translate all 8 procrastination types with emotions and strategies
- Translate educational content for understanding procrastination

All translation keys match between en.json and de.json for consistency.
2026-01-16 22:34:50 +01:00
Johannes Millan
2550f91cb7 feat(plugins): add i18n support to procrastination-buster plugin
- Add comprehensive English translations (en.json) for all UI strings
- Add useTranslate() hook for reactive translations
- Update manifest.json with i18n configuration
- Add i18n message handlers to plugin.ts
- Create getProcrastinationTypes() for dynamic translation loading
- Update App.tsx to use translations throughout
- Update ProcrastinationInfo.tsx with full translation support
- All 8 procrastination types, strategies, and info content now translatable

The plugin now supports internationalization with complete English translations
and can easily be extended to support additional languages.
2026-01-16 22:34:50 +01:00
Johannes Millan
eb120baf1b feat(plugins): add i18n support to boilerplate-solid-js
- Add useTranslate() hook for reactive translations in SolidJS
- Include example translation files (en.json, de.json)
- Update Vite plugin to copy i18n folder during build
- Add i18n message handlers to plugin.ts
- Demonstrate i18n usage in App.tsx with ~10 translation keys
- Update documentation with comprehensive i18n guide
- Add i18n to features list in main plugin-dev README

The boilerplate now provides a complete working example of multi-language
plugin support, making it easy for developers to create internationalized
plugins.
2026-01-16 22:34:50 +01:00
Johannes Millan
6087b63878 fix(tasks): add subtasks to Today via Add to Today button
Fixes issue where clicking "Add to Today" button or using Ctrl+T
keyboard shortcut on subtasks had no effect. The action dispatch
was missing the parentTaskMap parameter needed by the reducer to
properly handle parent-child task relationships.

- Add parentTaskMap to planTasksForToday action in task.component.ts
- Add parentTaskMap to planTasksForToday action in task-context-menu
- Add comprehensive unit tests for subtask scenarios
- Add E2E tests for button, keyboard shortcut, and context menu

Fixes #6028
2026-01-16 22:34:50 +01:00
Johannes Millan
f2145deaeb feat(plugins): improve i18n performance and developer experience
- Load translation files in parallel for 3-5x faster plugin loading
- Add dev-only warnings for missing translation keys and plugins
- Replace split/join interpolation with efficient regex-based approach
- Add comprehensive unit tests for new warning functionality
2026-01-16 22:34:49 +01:00
Johannes Millan
ce4e61fd94 fix(ci): grant write permissions to Claude Code action
The Claude Code GitHub Action was failing with 403 errors when trying
to create comments on issues due to read-only permissions. Updated
contents, pull-requests, and issues permissions from read to write.
2026-01-16 22:34:49 +01:00
Johannes Millan
f421d2387a fix(e2e): add robust overlay cleanup to prevent blocked clicks
Angular Material overlay backdrops were not being properly cleared between
tag operations, causing subsequent clicks to timeout when overlays blocked
element interactions.

Added ensureOverlaysClosed() helper with:
- Early exit if no overlays present (performance)
- Escape key dismissal with retry for stacked overlays
- Logging for debugging when fallbacks trigger
- Uses Playwright's native locator.waitFor() instead of waitForFunction()
- Cleanup at operation start (prevent blocking) and end (clean state)

Benefits:
- Eliminates fixed timeouts, uses smart waiting (tests run 2x faster)
- Handles edge cases like stacked overlays
- Provides visibility into when overlays are unexpectedly present

Fixes 4 failing tests:
- Tag CRUD: remove tag via context menu
- Tag CRUD: delete tag and update tasks
- Tag CRUD: navigate to tag view
- Menu: toggle tags via submenu
2026-01-16 22:34:49 +01:00
Johannes Millan
e8054b1b3d fix(focus-mode): prevent CPU spike on task completion
Refactored updateBanner$ effect from selector-based to action-based pattern
to prevent excessive re-evaluation that caused 95-107% CPU spikes when
marking tasks as done with Focus Mode enabled.

Changes:
- Converted updateBanner$ from combineLatest([10 selectors]) to action-based
  pattern that only fires on relevant Focus Mode actions
- Added throttling (500ms) to limit banner updates to max 2/second
- Added skipWhileApplyingRemoteOps guards to setTaskBarProgress$ and
  playTickSound$ effects to prevent duplicate operations during sync
- Added distinctUntilChanged to flatDoneTodayNr$ selector to reduce
  unnecessary task list filtering

Fixes #6001
2026-01-16 22:34:49 +01:00
Johannes Millan
5b1a843196 fix(e2e): add robust overlay cleanup to prevent blocked clicks
Angular Material overlay backdrops were not being properly cleared between
tag operations, causing subsequent clicks to timeout when overlays blocked
element interactions. Added waitForOverlaysToClose() helper with multiple
fallback strategies (natural close, Escape key, retry) to ensure clean state.

Fixes 4 failing tests:
- Tag CRUD: remove tag via context menu
- Tag CRUD: delete tag and update tasks
- Tag CRUD: navigate to tag view
- Menu: toggle tags via submenu
2026-01-16 22:34:49 +01:00
Johannes Millan
7f4e5381d0 fix(plugins): wire up translation loading to i18n service (CRITICAL)
Fixes the critical issue where translations were loaded but never
passed to PluginI18nService, making the i18n system non-functional.

Changes:
- Inject PluginI18nService in PluginService
- Load translations into i18n service in 3 locations:
  - _loadPluginLazy() for lazy-loaded plugins
  - _loadPlugin() for file-based plugins
  - _loadUploadedPlugin() for cached plugins
- Improve LANGUAGE_CHANGE hook type guard
  - Use explicit LanguageCode type predicate
  - Remove non-null assertion (no longer needed)

This makes api.translate() functional for all plugin loading paths.
2026-01-16 18:48:41 +01:00
Johannes Millan
5caef4132f fix(plugins): fix i18n-related test failures
- Add global config initial state to plugin-hooks.effects tests
  - Provide localization config to prevent undefined access
  - Fixes 6 failing tests in taskUpdate$ suite

- Add PluginI18nService mock to plugin-runner tests
  - Mock translate, getCurrentLanguage, and translation loading methods
  - Fixes 2 failing tests due to missing Store provider

All 6377 tests now passing
2026-01-16 18:11:12 +01:00
Johannes Millan
1858a09429 test(plugins): add comprehensive i18n tests (Phase 8)
- Add plugin-i18n.service.spec.ts with 21 unit tests
  - Translation loading and fallback chain
  - Parameter interpolation
  - Nested key lookup
  - Language switching
  - Edge cases (empty objects, numeric values)

- Add plugin-i18n-date.util.spec.ts with 31 unit tests
  - Date formatting in multiple locales
  - All format types (short, medium, long, time, datetime)
  - Input parsing (Date, ISO string, timestamp)
  - Invalid input handling
  - Edge cases (leap years, boundaries, midnight/noon)

- Fix TypeScript error in plugin-hooks.effects.ts
  - Add filter before distinctUntilChanged to handle null/undefined
  - Use non-null assertion after filter

All 52 tests passing
2026-01-16 18:01:50 +01:00
Johannes Millan
5bb8b24c82 docs(plugins): add comprehensive i18n documentation
Phase 7: Create documentation

- Create PLUGIN_I18N.md with complete i18n guide
  - Quick start guide with file structure
  - Manifest configuration details
  - Translation file format and best practices
  - Complete API documentation (translate, formatDate, getCurrentLanguage)
  - Language change hook documentation
  - Full list of supported languages (24 languages)
  - Complete working example with multi-language support
  - Best practices and troubleshooting sections
  - Migration guide from hard-coded strings
  - Testing and performance considerations

- Update README.md with i18n section
  - Add i18n API methods to Plugin API section
  - Add languageChange hook to hooks list
  - Add i18n example to usage section
  - Add i18n files to optional files list
  - Add i18n best practice
  - Link to comprehensive PLUGIN_I18N.md guide

Documentation provides complete guide for plugin developers to add
multi-language support to their plugins with working examples.
2026-01-16 17:55:34 +01:00
Johannes Millan
f166ae1595 feat(plugins): add language support display in plugin UI
Phase 6: Update UI to display supported languages

- Add language name mapping for all supported language codes
- Add getPluginLanguages() helper to format language list
- Add supportsCurrentLanguage() to check current language support
- Display languages in plugin metadata table
- Add visual indicator (check icon) for plugins supporting current language
- Add CSS styling for language support highlighting
- Add LANGUAGES translation key to T.const and en.json

UI features:
- Shows "English only" for plugins without i18n or only English
- Shows comma-separated language names (e.g., "English, German, French")
- Highlights languages in primary color when plugin supports current app language
- Displays check_circle icon next to supported languages
2026-01-16 17:52:13 +01:00
Johannes Millan
c742295624 feat(plugins): add i18n API methods and fix LANGUAGE_CHANGE hook
Phase 4-5: Plugin API Extensions and Language Switching

- Add translate(), formatDate(), getCurrentLanguage() to PluginAPI
- Inject PluginI18nService into PluginAPI constructor
- Update PluginRunner to pass PluginI18nService to PluginAPI
- Fix LANGUAGE_CHANGE hook bug (was firing on work context changes)
- Add proper languageChange$ effect listening to global config updates
- Wire language changes to PluginI18nService.setCurrentLanguage()
- Remove incorrect workContextChange$ effect dispatch

Translation API features:
- Simple translate(key, params?) with fallback chain
- Locale-aware formatDate(date, format) with predefined formats
- getCurrentLanguage() to get current app language

Language switching:
- Listens to updateGlobalConfigSection for 'localization' section
- Uses distinctUntilChanged to fire only on actual language changes
- Updates plugin i18n service and dispatches hook to plugins
2026-01-16 17:52:13 +01:00
Johannes Millan
cde660bd0c feat(plugins): add date formatting utility for i18n (Phase 4 partial)
- Create plugin-i18n-date.util.ts with formatDateForPlugin()
- Supports 5 predefined formats: short, medium, long, time, datetime
- Uses Intl.DateTimeFormat for locale-aware formatting
- Handles Date, string, and number inputs
- Graceful fallback to English for invalid locales

Part of Phase 4: Plugin API Extensions
2026-01-16 17:52:13 +01:00
Johannes Millan
ea5fbb60af fix(e2e): wait for overlay cleanup to prevent tag operation failures
**Problem:**
E2E tests started failing after PR #6010 with timeouts when clicking the Tags
group button in the sidebar. The failure occurred in tag deletion and removal
tests that previously worked.

**Root Cause:**
PR #6010 added `<div (click)="$event.stopPropagation()">` wrapper around tag
menu items to prevent menu closure when toggling tags. However, this prevented
Material CDK from detecting clicks properly, leaving overlay backdrops in the
DOM after menu operations. These lingering backdrops blocked subsequent clicks
on the Tags sidebar button, causing Playwright to timeout waiting for the
element to become "stable".

**Solution:**
Added explicit waits for `.cdk-overlay-backdrop` to disappear after menu
operations in:
- `assignTagToTask()`: Wait after assigning tag via context menu
- `removeTagFromTask()`: Wait after removing tag via context menu
- `deleteTag()`: Wait before attempting to interact with sidebar

**Changes:**
- e2e/pages/tag.page.ts: Add overlay cleanup waits with proper error handling
- All waits use `.catch(() => {})` to gracefully handle cases where no overlay exists

**Testing:**
Verified with `npm run checkFile e2e/pages/tag.page.ts` - all checks pass.

Fixes failing tests:
- tags/tag-crud.spec.ts:47 "should remove tag from task via context menu"
- tags/tag-crud.spec.ts:80 "should delete tag and update tasks"
- tags/tag-crud.spec.ts:117 "should navigate to tag view when clicking tag in sidebar"
- menu/menu-touch-submenu.spec.ts:71 "should support toggling tags via submenu"
- sync/webdav-sync-delete-cascade.spec.ts:77 "Delete tag with archived tasks syncs"
2026-01-16 17:52:13 +01:00
Johannes Millan
2d6955011c feat(plugins): add i18n manifest validation and translation loading (Phase 2-3)
- Extend validatePluginManifest to validate i18n configuration
  - Check i18n.languages is array of valid language codes
  - Warn if English not included (fallback language)
  - Warn about unsupported language codes
  - Return both errors and warnings

- Update PluginLoaderService to load translation files
  - Load i18n/{lang}.json for each declared language
  - Store translations as Record<string, string> (lang -> JSON)
  - Support both file-based and uploaded plugins
  - Log translation loading success/failure per language

- Extend PluginCacheService for translations
  - Add translations field to CachedPlugin interface
  - Update storePlugin to accept and cache translations
  - Calculate and log translation file sizes
  - Support retrieving translations from cache

Validates against app's supported LanguageCode enum.
Translation files loaded as strings, parsed later by PluginI18nService.
2026-01-16 17:52:13 +01:00
Johannes Millan
95578ef77b feat(plugins): add plugin i18n foundation (Phase 0-1)
- Add i18n field to PluginManifest type in plugin-api package
- Create PluginI18nService for translation management
  - Load translations from file paths or cached content
  - Nested key lookup with dot notation (e.g., BUTTONS.SAVE)
  - Smart fallback: current language → English → key
  - Parameter interpolation with {{param}} syntax
  - Language switching via signals
  - Memory cleanup for unloaded plugins
- Add type validation for cached translation content
- Document language sync integration points

Part of plugin internationalization system implementation.
Tests will be added in Phase 8.
2026-01-16 17:52:13 +01:00
johannesjo
0177b4860d fix(ios): remove alpha from icon, add safe area styling 2026-01-16 17:25:49 +01:00
johannesjo
d91d5e892b fix(ios): set background color for safe area behind home indicator
- Add backgroundColor to Capacitor iOS config to fill safe area
- Add CSS :has() rules to set html background based on theme class
2026-01-16 17:24:47 +01:00
johannesjo
ae0ca10c57 fix(ios): extend viewport behind safe areas to fix bottom white area
Add viewport-fit=cover to the viewport meta tag so iOS extends the web
view behind the home indicator area, allowing the bottom navigation
background color to fill the entire screen.
2026-01-16 16:52:58 +01:00
johannesjo
5813ea6f94 fix(ios): strip pre-release suffix from version for App Store 2026-01-16 16:52:53 +01:00
johannesjo
98855bb577 fix(ios): skip code signing during archive, sign during export 2026-01-16 16:37:53 +01:00
johannesjo
28da8999f2 fix(ios): show reminder dialog and prevent duplicate notifications
- Show reminder dialog on iOS when app is opened with pending reminders
  (Android still skips dialog since native notification actions work in background)
- Skip scheduling past reminders to prevent duplicate notifications when
  the effect runs after a reminder has already fired
2026-01-16 16:09:22 +01:00
johannesjo
9d19a481c8 fix(ios): use correct bundle ID com.super-productivity.app 2026-01-16 16:07:33 +01:00
johannesjo
5b4dfa09a3 fix(ios): fix archive step for CocoaPods compatibility
- Remove PROVISIONING_PROFILE_SPECIFIER from archive (breaks Pods)
- Use CODE_SIGN_STYLE=Automatic for archive step
- Manual signing handled during export via ExportOptions.plist
- Add bundle ID verification to catch mismatches early
2026-01-16 16:03:51 +01:00
johannesjo
d58f5eb6b7 docs: add iOS App Store build and publish notes 2026-01-16 16:02:13 +01:00
johannesjo
b05add91a3 fix(tour): disable add-task-bar auto-focus when tour is active
Prevents keyboard from popping up on mobile when reading tour instructions
by binding isDisableAutoFocus to shepherdService.isActive.
2026-01-16 15:48:54 +01:00
johannesjo
04a7e0e6aa refactor(ios): reuse existing mac_certs for iOS signing
Apple Distribution certificate works for both macOS and iOS.
Only new secret needed: ios_provision_profile
2026-01-16 15:42:28 +01:00
johannesjo
2996aaa361 feat(ios): add GitHub Actions workflow for iOS App Store release
- Create build-ios.yml workflow triggered on releases
- Configure signing keychain with iOS distribution certificate
- Install provisioning profile for App Store distribution
- Sync version from package.json using agvtool
- Build, archive, and export IPA with manual code signing
- Validate and upload to App Store Connect via xcrun altool
- Add sync:ios and dist:ios:prod npm scripts
2026-01-16 15:24:03 +01:00
Johannes Millan
0345c0c11c fix(metric): fix worklog aggregation for all-tasks metrics
**Problem:**
AllTasksMetricsService used take(1) which locked worklog data to whatever
context was active at initialization. This caused incorrect metrics when
navigating to /tag/TODAY/metrics after viewing a project.

**Solution:**
- Make service reactive to activeWorkContext$ changes
- Filter to only compute when context is TODAY_TAG
- Replace DataInitStateService with WorkContextService
- Add explicit filter for TODAY_TAG context
- Leverage existing WorklogService behavior: getCompleteStateForWorkContext
  automatically returns ALL tasks when context is TODAY_TAG

**Changes:**
- AllTasksMetricsService now triggers on activeWorkContext$ instead of isAllDataLoadedInitially$
- Only computes metrics when context is TODAY_TAG (matches MetricComponent logic)
- Tests updated to reflect reactive behavior (16 tests, all passing)
- Signal retains last value when observable stops emitting (expected behavior)

**Benefits:**
- Metrics recompute correctly when switching contexts
- WorklogService already handles 'all tasks' for TODAY_TAG
- No need for custom worklog aggregation
- Tests verify context switching and recomputation

Addresses CRITICAL issue from Codex code review.
2026-01-16 14:22:00 +01:00
Johannes Millan
ca377b93a2 fix(metric): add translation support for 'all tasks' title
- Add T.PM.ALL_TASKS_TITLE translation key
- Use translation pipe in metric.component.html
- Add type safety check for TODAY_TAG context
- Update tests to expect translation keys
2026-01-16 14:21:14 +01:00
Johannes Millan
30f541f6c7 test(capacitor-reminder): add missing Store provider to fix test failures 2026-01-16 14:18:11 +01:00
Johannes Millan
395bba6430 fix: resolve post-merge build errors
- Install missing Capacitor dependencies (@capacitor/keyboard, @capacitor/status-bar)
- Add missing useAlarmStyle argument to scheduleNativeReminder in capacitor-reminder.service.ts
- Fix undeclared variable errors in project-metrics.service.spec.ts
- Access reminder config correctly via GlobalConfigService.cfg()

All TypeScript compilation errors resolved and build passes.
2026-01-16 13:49:22 +01:00