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.
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
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
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
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.
- 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
- 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
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.
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
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
- 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
**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"
- 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.
- 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.
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.
- 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
- 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
- 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
**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.
Resolves merge conflicts in:
- src/app/core/startup/startup.service.ts: Keep both _store and _platformService injections
- src/app/features/android/store/android.effects.ts: Refactor to use platform-agnostic CapacitorReminderService
The feat/ios branch adds iOS support via Capacitor, introducing platform-agnostic
services for notifications, reminders, and platform detection that work across
web, Android, and iOS.
Remove unused imports (SimpleMetrics, TODAY_STR, EMPTY, worklogService, workContextService)
and add eslint-disable comment for naming-convention to allow date string object keys in tests.
Extract ensureGlobalAddTaskBarOpen() to e2e/utils/element-helpers.ts to avoid
duplicating the logic for opening the global add task bar across multiple tests.
This helper properly waits for the add button and input to be visible,
preventing race conditions in tests.
Implements aggregated metrics across all projects and tags when viewing the Today page (/tag/TODAY/metrics).
Key Changes:
- Add AllTasksMetricsService to aggregate metrics from all contexts
* Sums break numbers and times across all projects and tags
* Uses TaskService.getAllTasksEverywhere() for task counts
* Leverages existing worklog behavior for time tracking
- Update MetricComponent with dynamic service switching
* Detects TODAY_TAG context via computed signal
* Switches between AllTasksMetricsService and ProjectMetricsService
* Dynamic title: "Metrics (all tasks)" for TODAY_TAG
- Comprehensive test coverage (73 new tests)
* metric.util.spec.ts: 17 tests for core calculation logic
* project-metrics.service.spec.ts: 18 tests for service behavior
* all-tasks-metrics.service.spec.ts: 19 tests for aggregation
* metric.component.spec.ts: 19 tests for component integration
All tests passing (6,264+ tests across timezones).
Replace inline `.toISOString().split('T')[0]` pattern with getDbDateStr()
utility across production code, tests, and plugin examples.
Changes:
- Production: 6 files (plugin-bridge, metrics, counters, task-repeat-cfg)
- Tests: 4 spec files (helper functions and direct uses)
- Plugin example: Added formatDateStr() helper matching main app pattern
This ensures consistent local timezone handling for all user-facing date
operations and provides a single source of truth for date string formatting.
Explicitly close the icon autocomplete panel before closing the create tag
dialog to prevent Material CDK overlay backdrop from remaining in DOM and
intercepting pointer events. Fixes E2E test failures where rapid dialog
closure left the autocomplete overlay active.
Replace UTC date strings (toISOString().split('T')[0]) with getDbDateStr()
to match implementation behavior. Fixes 3 CalendarIntegrationService tests
and 1 StartupService test that failed in non-UTC timezones.
Fixes#6021
When users manually added subtasks to markdown files that referenced
non-existent parent tasks, the plugin would crash due to unsafe null
assertions and missing validation. This made the plugin permanently
disabled and required creating a new markdown file to recover.
Changes:
- Added parent ID validation before creating operations
- Orphaned subtasks are now converted to root tasks with warnings
- Added comprehensive error handling with user notifications
- Plugin stays enabled even after sync errors
- Added 15 new tests (689 lines) for orphaned subtask scenarios
- Fixed all existing tests to support new error notifications
The fix implements defense-in-depth:
1. Validation layer: Check parent IDs exist before operations
2. Error handling: Catch and report errors without crashing
3. User feedback: Clear notifications about issues
4. Data preservation: No data loss, orphans become root tasks
The Plan tab in Daily Summary was not displaying tasks because tomorrow$
observable lacked shareReplay(1), causing async pipe subscriptions to miss
cached values on cold start. This fix matches the pattern already used by
days$ observable.
Fixes#6022
**Banner Action Refactoring:**
- Merged _getTextButtonActions() and _getIconButtonActions() into unified _getBannerActions()
- Extracted business logic into 6 testable helper methods:
- _handleStartAfterBreak() - Start session after break completion
- _handleStartAfterSessionComplete() - Start break or session after completion
- _handlePlayPauseToggle() - Toggle play/pause
- _handleSkipBreak() - Skip current break
- _handleEndSession() - End session manually
- _handleOpenOverlay() - Open focus overlay
- Eliminated 150+ lines of duplicated code
- Improved testability and separation of concerns
- Updated all test calls to use new unified method with useIcons parameter
**Performance Regression Tests:**
- Added 4 performance tests for block merging algorithm (create-sorted-blocker-blocks.spec.ts)
- Tests validate O(n log n) performance improvements from commit 7ec5bfba9
- Covers 1000 and 10,000 block scenarios with timing assertions
- Tests overlapping and non-overlapping merge cases
- Each test verifies both correctness (sorted output) and execution time
**Benefits:**
- Better maintainability (changes in one place)
- Easier to test business logic independently
- Guards against performance regressions
- Preserves all original behavior
**Testing:**
- All 132 focus-mode tests pass ✓
- All 33 block merging tests pass ✓
**Code Review:**
- Codex Review: APPROVED (no critical issues)
- Warnings addressed with eslint-disable for performance test arithmetic