* feat(plainspace): add Collaborate action to project context menu
Surface Plainspace sharing from the project context menu (active, non-Inbox,
not-yet-shared projects) so it can be discovered at the moment of collaboration
intent, reusing PlainspaceShareService. A new selectIsProjectSharedOnPlainspace
selector hides the action once a project is shared to avoid provisioning a
duplicate space on a repeat click.
* docs(plainspace): document collaboration in wiki
Add Plainspace to the issue-integration comparison (matrix + per-provider
section) and a how-to for sharing a project via the project menu, including the
network/privacy caveat.
* feat(plainspace): place Collaborate action below the share-list item
Group the two share/export actions and lift Collaborate higher for
discoverability. Gated to active, non-inbox, not-already-shared projects.
* fix(plainspace): smoother first-run connect and value-first dialog
Pre-check connectivity and revalidate a stored token before the space
picker, so a stale/foreign token routes to the connect dialog and an
offline state shows a calm message instead of the raw 'check your token'
picker error. Make the connect dialog value-first: lead with what you
get, drop the 4-step how-to and email hint in favor of one short pointer
(token-creation guidance moves to plainspace.org).
* docs(plans): dedicated from-Super-Productivity flow on plainspace.org
Open plan for a guided token/connect flow on plainspace.org when a user
arrives from SP (Model A manual token, Model B OAuth-style handoff).
* refactor(plainspace): drop redundant connect pre-check (multi-review)
The space picker already detects a stale token and offers a reconnect
(#8616), so the revalidate() pre-check duplicated that path, cost an
extra GET /me, and forced re-auth on a valid token during a transient
server blip. Keep only the one-line offline guard; drop revalidate(),
the discriminated union, and the unnecessary _isOnline() seam (navigator
.onLine is spyable in the runner). Fix stale doc comments and document
the disabled-provider trade-off in the selector.
* feat(plainspace): show brand icon in the connect dialog title
* feat(plainspace): deep-link connect dialog to the from-SP onboarding flow
Point the dialog's 'Open Plainspace' link at the dedicated
/connect/super-productivity entrypoint (which guides token creation)
instead of the bare marketing host, closing the connect-flow funnel leak.
* feat(plainspace): bounce back to the app after connecting (desktop)
Append a validated `?return=superproductivity://plainspace-connect` deep
link to the connect URL, gated on IS_ELECTRON (only desktop registers the
scheme — web/mobile would get dead buttons). Handle that action in the
Electron protocol handler by surfacing the window, so the connect page's
"Open Super Productivity" button re-focuses the app.
Desktop-only; needs an on-device check.
* feat(azure-devops): add optional WIQL override for auto backlog import
Adds an optional autoImportWiql config field to the Azure DevOps issue
provider plugin. When set, it fully replaces the generated auto-import
backlog query, so the user controls scope, state filtering and ordering
(e.g. to filter by iteration path, area path or work item type, or to
match custom done-state names). When empty, behavior is unchanged from
today's scope-based query, so it is fully backward compatible.
This mirrors the Jira provider's autoAddBacklogJqlQuery (a full query the
user owns) rather than appending a fragment, which can only narrow the
hard-coded English done-state exclusion and cannot accept a real exported
WIQL Select..From..Where statement.
Adds a vitest suite covering the default scopes, quote escaping, the
verbatim override and the blank-fallback, wires the plugin into the
plugin-tests CI matrix, and updates the issue integration comparison wiki.
Closes#7674
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DY1ESWymmU9x9ykLNaRHGH
* test(azure-devops): use node test env, drop unneeded jsdom dependency
The plugin tests exercise no DOM API, so vitest's default node
environment is sufficient (matching the clickup and google-calendar
provider plugins). Removing jsdom prunes ~530 lines of transitive
devDependencies from the lockfile.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
* feat(calendar): add per-provider include/exclude regex filters for iCal events
* fix(calendar): mask filterIncludeRegex and filterExcludeRegex in privacy export
Regex filter patterns can contain user-specific title fragments (e.g.
ClientName|Project Alpha) and must be treated as PII.
* fix(calendar): apply regex filters to initial cached calendar emission
Cached events emitted via defer() bypassed regex filters because
provider config was not yet available. The initial emission now reads
selectCalendarProviders first so filterIncludeRegex/filterExcludeRegex
are applied consistently from the start.
* fix(calendar): reconcile banner queue when provider regex filters change
Events queued in _currentlyShownBanners$ were not re-evaluated when a
provider's filterIncludeRegex or filterExcludeRegex changed, so excluded
events could linger until dismissed. The new reconcileBannersOnProviderChange
effect filters the queue against the updated config on every provider change.
* fix(calendar): store raw fetch data in cache before applying regex filters
Regex-excluded events were permanently lost from the cache because
saveToRealLs was called after the filter pass. If a user loosens or
removes a regex while offline, the fallback cache could not restore
those events until the next successful fetch.
The cache now stores unfiltered source data; regex and task/skip/hidden
filters are applied only for the downstream emission.
* fix(calendar): resolve regex filter config per-event in initial cached emission
The defer() path derived one provider config from entry.items[0]?.calProviderId
and applied it to every item in the cache entry. Mixed-provider entries
would apply provider A's regex to provider B's events.
Config is now resolved inside the filter callback via calEv.calProviderId,
matching how _groupCachedEventsByProvider() already handles per-item grouping.
Regression test added with a mixed-provider cached entry verifying that
each event is filtered against its own provider's config.
* fix(calendar): apply full view-level filters to initial cached emission
The defer() path only applied regex filters, so task-imported, skipped,
and hidden events could briefly reappear on startup until the first fetch
completed. Now reads all four filter sources in a single combineLatest
snapshot and applies the same ID-based checks as the live fetch path.
Regression test added for all three ID-based filter types.
* fix(calendar): reapply regex filters from cache
* fix(calendar): hide stale cached provider events
* fix(calendar): filter manual iCal search
* fix(calendar): refresh agenda filters
---------
Co-authored-by: agncr <a.gencer@thalia.de>
Co-authored-by: agncr <agncr@users.noreply.github.com>
Co-authored-by: johannesjo <johannes.millan@gmail.com>
* fix(electron): disable webSecurity in dev mode to suppress CORS preflights
* feat(CalDAV): Option to import sub-tasks along with parent
* test(CalDAV): Adds specs for option to import subtasks along with parent
* doc(CalDAV): Adds notion on automatic sub-task import
* feat(CalDAV): Handles grandchildren and ignores subtasks of archived tasks.
* fix(setup): removes policy violating code
* fix(caldav):_resolve N+1 issue
* test(caldav):_adds more tests
* fix(electron):_fix task-widget mock in electron test
* feat(gitea): add label-based include and exclude filters
Adds two optional config fields to the Gitea issue provider. filterLabels
is a comma-separated allowlist forwarded to the issues endpoint as the
`labels` query param (AND-matched server-side). excludeLabels is a
comma-separated denylist applied client-side, since Gitea/Forgejo's
issues endpoint has no negation syntax. Scoped labels (scope/name) work
with both.
This lets users point multiple Gitea integrations at the same repository
and route disjoint subsets of issues into different Super Productivity
projects, e.g. one project filtering `project/foo` and another excluding
it.
Filters apply only to ingestion (search suggestions and auto-import of
open issues). Already-imported tasks keep refreshing regardless of label
changes, matching how the existing `scope` filter behaves.
* docs(gitea): document label include/exclude filtering
Update the issue-integration comparison page with the new label
allowlist and denylist filters, including scoped-label support.
* feat(gitea): enforce filterLabels AND match client-side
Gitea's two issue endpoints (`/repos/{o}/{r}/issues` and
`/repos/issues/search`) parse the `labels=` query param into different
internal fields and produce different semantics (AND vs OR), so the
same `filterLabels` config behaved differently on auto-import vs
manual search. The AND on the auto-import path is also a Gitea bug
(go-gitea/gitea#33509) that may flip to OR upstream at any time.
Apply AND-matching on the client (mirroring how `excludeLabels`
already filters client-side) so behavior is consistent across both
endpoints and independent of server version. The `labels=` query is
still sent as a coarse pre-filter (always a superset of the right
answer regardless of server semantics). Forgejo inherits the same
endpoints from Gitea, so the same fix covers it.