Commit graph

6 commits

Author SHA1 Message Date
felix bear
71f4ca484c
fix(plugins): return dialog result #5239 (#8106)
Co-authored-by: cocojojo5213 <cocojojo5213@users.noreply.github.com>
2026-06-08 12:14:08 +02:00
Federico Simonetta
9a7a86c8ec
Feature plugins app state (#7803)
* chore(plugin-api): normalize index exports

* feat(plugin-api): expose app state types and getAppState

* feat(plugins): expose getAppState through PluginAPI

* feat(plugins): expose getAppState in plugin-bridge

* fix(plugins): restore tag selector import

* test(plugins): cover PluginAPI.getAppState

* fix(plugins): expose getAppState for iframe PluginAPI

* docs(plugin-api): add getAppState permission + example

* docs(plugins): concise getAppState entry

* fix(plugins): redact credentials from getAppState snapshot

`getAppState` previously returned `globalConfig` and `projects[]` verbatim,
exposing per-installation secrets to every loaded plugin. Strip the three
known credential surfaces before returning:

- `globalConfig.sync` — WebDAV/Nextcloud passwords, SuperSync access tokens,
  encryption keys
- `globalConfig.misc.unsplashApiKey` — user-supplied API key
- per-project `issueIntegrationCfgs` — Jira/CalDAV passwords, GitLab/Redmine
  tokens, OpenProject/Trello/Linear keys

Add a security-regression spec that seeds sentinel credential strings into
each surface and asserts none appear in `JSON.stringify(snapshot)`.

---------

Co-authored-by: 00sapo <00sapo@noreply.codeberg.org>
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
2026-05-27 17:02:29 +02:00
Johannes Millan
3c69aa961e
feat(plugins): fire PERSISTED_DATA_CHANGED hook on persisted-data changes (#7805)
* feat(plugins): fire PERSISTED_DATA_CHANGED hook on persisted-data changes

Wires the dead PluginHooks.PERSISTED_DATA_UPDATE enum into a fired hook,
renamed PERSISTED_DATA_CHANGED. Plugins are notified when their persisted
data changes for any reason after the host's initial boot load — local
writes, remote incremental sync via bulkApplyOperations, and post-boot
wholesale loadAllData paths (SYNC_IMPORT / BACKUP_IMPORT / validation
repair / recovery).

Selector-based effect on selectPluginUserDataFeatureState, gated on
SyncTriggerService.afterInitialSyncDoneAndDataLoadedInitially$ so the
boot-time state seeds the pairwise baseline. Differ compares prev/next
by === on the encoded data blob (never decoded). Stage A composite
entityIds (pluginId:key) are normalized to owner pluginId and deduped
so a plugin with N keyed entries changing in one emission fires exactly
once. Per-pluginId dispatch via new PluginHooksService.dispatchHookToPlugin.

Effect is { dispatch: false } and creates no ops, so no sync-window
guard is needed — and adding one (skipDuringSyncWindow) would silently
suppress the very remote-sync deliveries the hook is designed to catch.

Closes #7754. Follow-up: doc-mode adoption tracked in #7752.

* test(plugins): tighten PERSISTED_DATA_CHANGED spec + docs

Multi-review pass 2 surfaced that the 5s timeout spec asserted only that
the dispatcher resolved — it would have silently passed if the timeout
race were removed entirely. Spy on PluginLog.err and assert the timeout
message actually reached the catch branch.

Also:
- Add `:` guard to PluginHooksService.registerHookHandler so the
  persistence-key grammar is enforced at both the persistence and
  hooks-registry endpoints (defense-in-depth; composeId already throws
  at the bridge).
- Add async-rejected-promise spec to cover the Promise.race branch that
  sync-throw didn't exercise.
- Carry the PERSISTED_DATA_CHANGED contract paragraph into
  docs/plugin-development.md and docs/wiki/3.01-API.md (previously only
  in packages/plugin-api/README.md).
- Clarify loadSyncedData(key?) in the README for keyed plugins.
2026-05-26 23:41:01 +02:00
Johannes Millan
4d22a64955
Feat/plugin UI kit (#6362)
* fix(e2e): stabilize undo task delete sync test

Two flakiness sources fixed:
- Click on task element could activate title inline editor, causing
  Backspace to edit text instead of triggering delete. Now clicks the
  drag handle which calls focusSelf() without entering edit mode.
- Replaced deleteTask helper with inline sequence to avoid wasting
  2s of the 5s undo snackbar window on dialog-detection timeout.

* refactor: address code review findings from 2026-02-03

- Extract getBreakCycle helper to replace error-prone `cycle - 1 || 1`
  pattern at 3 call sites
- Add clarifying comment on intentionally broad 'timed out' match
- Reduce Pomodoro E2E test from 9 to 5 sessions (sufficient coverage)
- Remove dead _isTransientNetworkError wrapper from DropboxApi
- Extract stubWindowConfirm helper in task reducer tests

* fix(sync): prevent Formly from clearing provider config on show (#6345)

resetOnHide: true caused Formly to reset field values when provider
fieldGroups transitioned from hidden to visible, discarding user input
if sync was enabled before selecting a provider.

* fix(tasks): fix huge space between emoji and text in tag/project menus

Use matMenuItemIcon attribute on emoji spans so they project into the
icon slot of mat-menu-item instead of the text slot. Update emoji icon
sizing to 24x24px to match mat-icon and add overflow: hidden.

Closes #5977

* fix(tasks): guard against undefined task entities in selectors and archive (#6359)

Prevent TypeError crashes (reading 'dueWithTime', 'dueDay', 'issueProviderId') caused
by orphaned IDs in NgRx state. Fix archive merge to deduplicate IDs, filter orphans,
and use correct entity precedence (young over old). Add defensive null guards to
selectors and archive/task service methods.

* fix(tasks): guard against undefined task in mainListTasksInProject$ (#6360)

* fix(tasks): detect and sanitize orphaned task IDs to prevent startup crashes (#6359, #6360)

Orphaned task IDs (entries in task.ids without matching entities) caused
TypeError on app startup. Fix addresses three layers: validation now
flags orphaned IDs instead of silently skipping them, loadAllData
sanitizes IDs on load as a safety net, and data repair no longer crashes
when encountering orphaned IDs it's trying to fix.

* fix(sync): prevent recurring task duplication across clients

Remove SuperSync special-case that bypassed initial sync wait, causing
repeatable task effects to fire before sync completed. Add post-sync
cleanup effect that detects and removes stale duplicate repeat instances
when multiple active instances exist for the same repeat config.

* fix(sync): restore WebDAV provider compatibility warning text

* feat(sync): mark WebDAV and LocalFile sync options as experimental

* feat(plugins): add UI Kit with inject-first CSS strategy for iframe plugins

Introduce a lightweight CSS reset (UI Kit) that auto-styles basic HTML
elements in plugin iframes to match the host app theme. Injected after
<head> so plugin styles always win by source order.

UI Kit provides: element resets (body, headings, buttons, inputs, tables,
links, code, lists, hr), .btn-primary/.btn-outline button variants, and
.card/.card-clickable components.

All bundled plugins updated to use UI Kit classes, removing redundant
custom CSS (-542 lines net). Pico CSS removed from automations plugin.
sync-md converted from hardcoded colors to host theme variables.

* feat(plugins): extract shared CSS utilities into UI Kit

Move .text-muted, .text-primary, .page-fade and @keyframes fadeIn from
plugin CSS into the UI Kit so all iframe plugins get them automatically.
Add box-shadow focus ring to input:focus for better accessibility.
Remove per-plugin focus overrides now covered by the UI Kit.
2026-02-04 18:18:22 +01:00
Johannes Millan
1a79592aca build: update links to match our new organization 2026-01-05 14:45:06 +01:00
Johannes Millan
d4d81bf511 feat(plugin-api): create foundational plugin API package
- Add @super-productivity/plugin-api package with TypeScript definitions
- Define core plugin interfaces, types, and manifest structure
- Add plugin hooks system for event-driven architecture
- Create plugin API type definitions and constants
- Add documentation and development guidelines
2025-06-27 18:13:19 +02:00