Commit graph

21 commits

Author SHA1 Message Date
felix bear
fe65d4b9a6
fix(plugin): refresh procrastination buster i18n #5102 (#8145)
Co-authored-by: cocojojo5213 <cocojojo5213@users.noreply.github.com>
2026-06-08 20:44:43 +02:00
Johannes Millan
2580ea6eb1
perf(plugins): skip no-op doc-mode saves + rename to doc-mode (#7825)
* perf(plugins): skip no-op doc-mode saves via lastSeenDocBytes equality

Closes #7815.

flushSave and flushSaveSync now compute the would-be-written raw bytes
first and short-circuit when they equal lastSeenDocBytes — a typed-then-
reverted cycle inside the 30s throttle window no longer produces a
postMessage round-trip, an IDB transaction, or an op-log entry.

The baseline is intentionally NOT updated on a skip: it already equals
these bytes, so the self-echo invariant for PERSISTED_DATA_CHANGED
(#7752) is preserved.

Drive-by: added the isDocCorrupt guard to flushSave for symmetry with
flushSaveSync / scheduleSave. The schedule gate normally prevents
flushSave from firing on a corrupt doc, but corruption can be set
between schedule and fire (e.g. a remote-update reload turning up an
unparseable blob), so the explicit guard is cheaper than reasoning
about that invariant.

Persistence rename: saveContextDoc → persistContextDocRaw. The caller
now owns serialisation (via serializeContextDoc) so the byte-compare
can run before the persist dispatch. Three persistence.spec.ts tests
replace the dropped saveContextDoc test: exact-raw write, encoder
determinism (the premise the byte-compare relies on), and
write-idempotence (which proves the skip loses no information).

* refactor(plugins): apply doc-mode no-op-save review findings (#7815)

Multi-review follow-ups for #7815:

- Tighten serializeContextDoc determinism test: previously asserted
  equality between a doc and its `{...doc}` spread, which preserves V8
  insertion order — the test would pass even against a future encoder
  that randomised iteration over Map-backed nodes (the real failure
  mode for the byte-compare). Now asserts repeated-call determinism on
  the same input directly, plus type+non-empty shape so a future return-
  type change (e.g. Uint8Array) breaks here, not in the editor.

- Document the sync/async stamp asymmetry in flushSaveSync: it stamps
  lastSeenDocBytes BEFORE the void dispatch, whereas flushSave stamps
  AFTER `await`. Both are correct (the sync path is fire-and-forget and
  the iframe can be torn down mid-call, so a post-dispatch stamp would
  silently drop on teardown) but the divergence wasn't called out in
  the inline comments.

* refactor(plugins): rename document-mode to doc-mode

Renames the bundled plugin's id (`document-mode` → `doc-mode`), display
name (`Document Mode (Alpha)` → `Doc Mode (Alpha)`), package name,
directory, and asset path. No migration: the plugin has never been
published, so there is no on-disk user data to preserve.

Touched everywhere the id was hardcoded:
- Plugin manifest, package.json, build/deploy/test scripts, log prefixes
- Host BUNDLED_PLUGIN_PATHS entry (src/app/plugins/plugin.service.ts)
- Host comments and test fixtures (plugin-hooks, plugin-persistence-key,
  plugin-persistence.model, conflict-resolution.service.spec)
- E2E spec filenames + PLUGIN_ID constant + label assertions
- build-all.js plugin orchestrator
- build/release-notes.md user-facing entry

Test pass: 88/88 plugin specs, 8/8 plugin-hooks, 132/132
conflict-resolution, 15/15 plugin-persistence-key.util.
Bundle redeployed to src/assets/bundled-plugins/doc-mode (gitignored).

The old `src/assets/bundled-plugins/document-mode/` dir is gitignored and
left on disk after this commit; the host loader no longer references it,
so it's inert. Remove with `rm -rf src/assets/bundled-plugins/document-mode`.

* refactor(plugins): apply doc-mode rename review findings

Multi-review follow-ups for the rename in 3443bcacd0:

- editor.ts:1920: missed runtime log string ('Document mode:' →
  'doc-mode:') — leaked into the dev console with inconsistent brand
  because the rename sweep matched on the kebab `document-mode` or the
  PascalCase `Document Mode`, not the bare-space `Document mode`.
- editor.ts + background.ts header JSDoc: `Document-Mode editor` /
  `Document-Mode background script` → `Doc-Mode …`. Cosmetic but the
  only remaining branded references in the source.
- scripts/build.js: esbuild iife `globalName: 'DocumentModePlugin'` →
  `'DocModePlugin'`. Internal global, no consumers, but worth keeping
  consistent with the new id.
- src/app/plugins/plugin.service.ts: add a header comment to
  BUNDLED_PLUGIN_PATHS noting that pluginIds become entityId prefixes in
  IDB / op-log / sync wire and must be treated as permanent for any
  plugin that has ever shipped. Records the rule we relied on being
  absent for this rename.

DOM-document references (`installDocumentDragHandlers`,
`Document-status banner`, `Document-level pointer handlers`) left
unchanged — those refer to the browser document object, not the brand.
Historical plan docs in `docs/plans/2026-05-2[123]-*.md` also left
unchanged per the rename commit's intent.
2026-05-27 17:30:38 +02:00
Johannes Millan
1c10ff67dd feat(document-mode): add TipTap-based document-mode plugin
A document-mode plugin under packages/plugin-dev/document-mode/ that
renders the active work context's tasks as an editable TipTap document.
Per-context doc state is persisted as a last-writer-wins JSON blob via
PluginAPI.persistDataSynced; task identity (title, done state, hierarchy)
stays in NgRx and is reached through PluginAPI.updateTask and the
ANY_TASK_UPDATE hook.

It registers a work-context header button and embeds itself into the
work-view embed slot added in the preceding commit. See
docs/plans/2026-05-21-document-mode-tiptap-plugin.md for the full design.

Squashed from the feat/doc-mode-v4 work; full per-step history is
preserved in the archive/doc-mode-v4-full-history branch and the
doc-mode-v4-pre-squash tag.
2026-05-22 17:33:22 +02:00
Johannes Millan
020fd56504 feat(calendar): add CalDAV Calendar plugin with time-blocking support
Add a CalDAV Calendar plugin that syncs tasks with calendar events via
the CalDAV/WebDAV protocol, supporting self-hosted servers like Nextcloud.

Plugin features:
- CalDAV PROPFIND/REPORT for calendar discovery and event fetching
- iCal parsing and serialization with RFC 5545 compliance
- Two-way sync with field mappings (title, notes, dates, duration)
- Time-block integration for auto-creating calendar events
- Multi-calendar support with compound IDs

Host-side changes:
- Add generic request() method to PluginHttp for WebDAV methods
- Add allowPrivateNetwork manifest flag (bundled plugins only)
- Add dynamic loadOptions support for config field dropdowns
- Add backfill effect for existing scheduled tasks
- Generify translation keys (LOAD_OPTIONS instead of LOAD_CALENDARS)

Security:
- SSRF protection via origin validation in resolveHref
- allowPrivateNetwork gated by _isPluginBundled check
- XML parse error detection in CalDAV response parsing
- Description rendered as plain text (not markdown)

Correctness:
- TZID conversion uses formatToParts (no local timezone contamination)
- modifyICalEvent scoped to VEVENT block (preserves VTIMEZONE)
- responseType: 'text' on all CalDAV PUT/DELETE calls
- CR characters handled in iCal text escaping
- Trailing CRLF added to modifyICalEvent output per RFC 5545
2026-03-28 23:10:08 +01:00
Johannes Millan
f885336aa3 feat(plugins): enable google-calendar-provider bundled plugin 2026-03-26 17:43:03 +01:00
Johannes Millan
02bc3e88e3 feat(two-way-sync): add plugin OAuth, two-way field sync, and remote issue deletion
Add OAuth support for plugins with PKCE, token persistence in local-only
IndexedDB, and Electron/web redirect handling. Extend two-way sync with
dueDay, dueWithTime, and timeEstimate field mappings including mutually
exclusive field clearing. Support remote issue deletion on task delete
and remote deletion detection during polling.

Add Google Calendar plugin (disabled from bundled builds pending legal
review) as a development reference for the plugin OAuth and two-way
sync APIs.

Additional fixes:
- Restore accidentally deleted focus-mode translation keys
- Remove full Task[] from deleteTasks action to prevent op-log bloat
- Add dueDay/deadlineDay format validation guards (#6908)
- Fix Android reminder alarm cancel intent matching
- Validate dueDay format to prevent false overdue from corrupted data
- Fix PKCE test expectation after utility consolidation
2026-03-22 13:02:41 +01:00
Johannes Millan
7fcf2fa213 refactor(voice-reminder): extract voice reminder to standalone plugin
Move the voice reminder (domina mode) feature from a built-in Angular
feature into a self-contained plugin with its own manifest, i18n, and
config dialog. Add registerConfigHandler plugin API for settings button
on plugin cards. Include migration logic to auto-enable the plugin and
transfer config for users who had domina mode enabled.

- Fix currentTaskChange hook to dispatch full Task object instead of ID
- Extract shared BUNDLED_PLUGIN_PATHS constant to eliminate duplication
- Use firstValueFrom instead of deprecated .toPromise()
- Add voice name fallback matching for migrated configs
- Mark DominaModeConfig and selector as @deprecated
- Add unit tests for onCurrentTaskChange and config handler
2026-03-20 21:36:30 +01:00
Johannes Millan
76239d3411 refactor(issue): migrate ClickUp issue provider from built-in to plugin
Migrate the ClickUp issue provider from a tightly-coupled Angular
service to the external plugin architecture, following the pattern
established by the GitHub provider migration.

- Delete all built-in ClickUp files (api service, models, components,
  tests, constants) from src/app/features/issue/providers/clickup/
- Create new plugin in packages/plugin-dev/clickup-issue-provider/
  with pure-function API layer, plugin registration, i18n, and tests
- Add migration in issue-provider.reducer.ts to convert old ClickUp
  provider config to plugin shape (preserves legacy fields for rollback)
- Move CLICKUP from BuiltInIssueProviderKey to MigratedIssueProviderKey
- Register clickup-issue-provider as bundled plugin
- Fix isDone detection: use status.type instead of status.status so
  custom closed statuses like "Shipped" are correctly detected
- Show disabled plugin issue providers in setup overview for
  discoverability — clicking auto-enables and opens config dialog
2026-03-19 15:03:45 +01:00
Johannes Millan
30cbb4d7a6
fix(build): ensure plugin-api is built before plugin builds (#6720)
* fix(build): ensure plugin-api is built before plugin builds

The github-issue-provider plugin build was failing in CI because
plugin-api/dist/ didn't exist when tsc --noEmit ran. The dist/ directory
is gitignored and only built during the prepare lifecycle, which may not
run reliably in all CI contexts. Adding plugin-api:build to the
plugins:build script ensures the type declarations are always available.

Also improved error logging in build-all.js to show stderr on failure.

https://claude.ai/code/session_01StB1pMk1g2k79AywyksoyZ

* fix(build): remove tsc --noEmit from github-issue-provider build

The github-issue-provider was the only plugin running tsc --noEmit as
part of its build command. In CI, TypeScript module resolution fails for
the file: dependency on plugin-api due to npm workspace hoisting
interference. All other plugins (ai-productivity-prompts,
procrastination-buster, sync-md) skip type-checking during build and
rely on esbuild to strip type-only imports.

Align with existing plugin pattern by separating build from typecheck.
Also improve error logging in build-all.js to capture stdout on failure.

https://claude.ai/code/session_01StB1pMk1g2k79AywyksoyZ

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-04 11:25:21 +01:00
Johannes Millan
6a78913177 feat(plugins): add plugin issue provider system with GitHub migration
Add a plugin-based issue provider architecture that allows plugins to
register as issue providers alongside the existing built-in providers.

- Plugin API: types for search, display, comments, two-way sync, and
  field mappings in @super-productivity/plugin-api
- Registry service to manage plugin provider lifecycle
- HTTP proxy with SSRF protection for plugin network requests
- Adapter service implementing IssueServiceInterface for plugins
- Sync adapter for plugin-based two-way sync
- Plugin config UI in the issue provider edit dialog
- Plugin providers shown in setup overview and issue panel

Migrate GitHub from built-in to plugin as first proof:
- Bundled github-issue-provider plugin with full feature parity
- Reducer migration converts old GitHub data to plugin format while
  preserving legacy fields for cross-version compatibility
- Auto-enable plugin when existing GitHub providers are detected
- Plugin registers under 'GITHUB' key via MigratedIssueProviderKey
- GitHub translations moved to plugin i18n bundles

Mark Two-Way Sync as experimental in the UI.
2026-03-03 20:14:54 +01:00
Johannes Millan
28e4335c99 fix(plugins): add missing automations plugin to build script
The automations plugin was missing from the build-all.js script, causing
its manifest.json to not be copied to src/assets/bundled-plugins/.

This resulted in 404 errors during E2E tests (6 per test × ~199 tests =
~1,194 404s total) because plugin.service.ts tries to load all 6
bundled plugins but only 5 were being built.

Added automations plugin configuration to build-all.js with the same
pattern as other bundled plugins (build + copy dist files to assets).

Fixes: 1,146 console 404 errors in E2E test runs
Related: plugin.service.ts lines 93-156 (_discoverBuiltInPlugins)
2026-01-23 20:07:52 +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
e268076332 test(e2e): try to fix e2e tests 2025-12-10 21:26:48 +01:00
Johannes Millan
cb41b600cf fix(e2e): include missing assets for api-test-plugin build
Copies config-schema.json and icon.svg to bundled assets, ensuring the plugin loads correctly in e2e tests. Also updates package-lock.json to resolve missing dependencies.
2025-12-04 11:50:25 +01:00
Johannes Millan
56d54b593b build: fix failing 2025-10-28 13:17:08 +01:00
Johannes Millan
0d3789a75f build: add new plugin to build all 2025-08-30 13:06:43 +02:00
Johannes Millan
88814795c7 feat(sync-md): new approach and make it work better 2025-07-07 18:39:14 +02:00
Johannes Millan
caa64775e5 fix(plugin): better handle bundled plugins 2025-06-29 15:32:51 +02:00
Johannes Millan
11911b2ba2 fix: procrastination-buster plugin build process with asset inlining
- Add inline-assets script to embed JS/CSS into HTML for data URL compatibility
- Update build process to automatically inline assets during build:all
- Fix PluginAPI integration and wait for API availability
- Update App.tsx to use PluginAPI.dispatchAction instead of postMessage
2025-06-29 11:49:37 +02:00
Johannes Millan
0145f28d50 feat: cleanup plugins 2025-06-29 06:19:39 +02: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