Commit Graph
35 Commits
Author SHA1 Message Date
Tobias Genannt d6d3683b65 feat(plugins): add Combobox component to UI kit with host floating dropdown portal
- Add Combobox component supporting single and multiselect modes, search filtering, and clear button
- Implement floating dropdown bridge and host slot portal so dropdown renders cleanly outside plugin iframe
- Fix clear button and chip removals to not unexpectedly open the dropdown
- Add comprehensive unit tests covering Combobox options, clear behavior, multiselect, and bridge communication
2026-10-16 10:08:27 +02:00
Linus Rath db7a4408a1 fix: list plugin composer buttons under Options when the composer is too narrow for them 2026-10-14 14:59:29 +02:00
Linus Rath 9d7dc4fc4e fix: stop plugin frames from painting a white backdrop in dark themes 2026-10-14 14:02:40 +02:00
Linus Rath 9d8f762b1d feat: let a plugin title its sidebar section 2026-10-14 11:47:25 +02:00
Linus Rath 1bd4f743a1 feat: give plugins the app's own controls and draw their dialogs like the app's dialogs 2026-10-14 11:10:14 +02:00
Linus Rath c87ef9c44a fix: move plugin buttons in a narrow selection toolbar into a more menu so the toolbar no longer wraps 2026-10-14 09:28:56 +02:00
Linus Rath ae2130f5c8 feat: let a plugin ask a question on top of its own dialog 2026-10-14 06:47:53 +02:00
Linus Rath d841785cbd feat: let composer plugins read the selected text and write over it 2026-10-14 06:45:08 +02:00
Linus Rath 14c5b10c6a feat: tell a plugin page which of its pages is open 2026-10-14 05:30:34 +02:00
Linus Rath 7a6a0fc67f Merge branch 'slots/nav' into feat/plugin-system-audit
# Conflicts:
#	lib/__tests__/plugin-slot-catalog.test.ts
2026-10-14 05:30:34 +02:00
Linus Rath 425eae2227 Merge branch 'slots/mail' into feat/plugin-system-audit
# Conflicts:
#	lib/__tests__/plugin-slot-catalog.test.ts
2026-10-14 05:07:18 +02:00
Linus Rath b8b7a4ed15 feat: let plugins add pages of their own with an entry in the navigation 2026-10-14 04:57:00 +02:00
Linus Rath 37846bec44 feat: let plugins add collapsible sections to the mail sidebar 2026-10-14 04:45:24 +02:00
Linus Rath d144a6e85c feat: let plugins add sections to event details and the event form, and label calendar days 2026-10-14 04:43:40 +02:00
Linus Rath 8fbc8a1c1d feat: let plugins add frames above and in an empty message list and buttons to message rows and the selection toolbar 2026-10-14 04:43:22 +02:00
Linus Rath 5d9268c923 feat: let plugins add buttons, calendar day labels and pages, and use connected accounts from the sandbox 2026-10-14 04:31:43 +02:00
Linus Rath 60257bb732 feat: let users connect accounts for plugins in a dialog the plugin can't read, and disconnect them in settings 2026-10-14 04:14:13 +02:00
Linus Rath 2adb1e2b12 fix: size toolbar plugin slots to their content and give composer plugin buttons their own row 2026-10-14 02:53:46 +02:00
Linus Rath f048db7b52 fix: count each plugin hook failure once and keep a plugin that kept failing turned off 2026-10-14 02:33:45 +02:00
Linus Rath ff1d1f399d fix: limit plugin dialogs to one at a time, name the plugin in each and offer to turn it off 2026-10-14 02:15:45 +02:00
Linus Rath e720be7e17 feat: let users install their own plugins, with admin review of the actual code
The admin page offered "User Plugin Uploads" and "Require Admin
Approval", but nothing in Settings used them: there was no upload, no
uninstall and no way to ask for approval, and the admin had no list of
requests. The approval code that existed pinned a hash of the code only
and showed the admin a manifest the browser sent, never the code.

Now:
- Settings > Plugins has an install button (when uploads are allowed),
  a remove button for the user's own plugins, and lists in plain
  language what each plugin may do, high-risk items first
- an install sends the ZIP to the server, which reads the manifest and
  code itself and pins the request to a hash of both
- the admin Plugins tab lists requests with the code and approves or
  denies them; a user may have 10 open requests
- a user plugin is checked again on every start, so a revoked approval
  or turning user plugins off takes effect
- the consent dialog and the plugin settings are translated, use the
  theme colours, and show the sites a plugin may send data to
2026-10-14 02:03:26 +02:00
Linus Rath c45533646e fix: give slot plugins the message, event or contact only with the matching read permission 2026-10-14 01:31:30 +02:00
Linus Rath 4d733aa598 chore: remove unused exports and the dead plugin error boundary 2026-10-13 13:36:51 +02:00
Linus Rath 3c59e811a5 chore: remove unused files and dependencies 2026-09-22 19:36:33 +02:00
bartfaizoli76 2adc281a9d feat: add ui.openDialog + 'plugin-dialog' slot for large, clickable custom UI
Every existing plugin slot renders wherever it's placed in the page (a
toolbar row, an attachment's hover-action bar, etc.) and is constrained
by that spot's own CSS - fine for small controls, but there's no way
for a plugin to show a large custom UI (a graphical file browser, a
multi-step wizard) since even a position:fixed overlay rendered from
inside such a slot is clipped to the surrounding row's own layout, not
the real page (confirmed live building the webdav-storage plugin: a
folder browser rendered but was squeezed into an unusable sliver).

Adds:
- host-dialog.ts: new 'custom' DialogKind + awaitCustomDialog(), reusing
  the existing confirm/alert/prompt queue plumbing.
- host-api.ts: 'ui.openDialog' API method.
- runtime.tsx: sandbox-side api.ui.openDialog(opts) proxy.
- plugin-types.ts: new 'plugin-dialog' SlotName - a plugin implements
  this slot with its actual custom UI.
- plugin-dialog-host.tsx: renders the requested slot via the existing
  PluginIframeSlot INSIDE the dialog's real app-root fixed overlay
  (position: fixed; inset: 0, above everything) instead of the
  confirm/prompt form, for kind: 'custom'. Resolves via a generic
  onResult callback in extraProps (marshalled the same way
  composer-attachment-source's onAttach already is - no new plumbing
  needed there).

A plugin calls api.ui.openDialog({ slot: 'plugin-dialog', extraProps,
title, width }) and awaits the Promise, which resolves to whatever its
own slot component passes to props.onResult(), or null if the user
closes the dialog without doing so.
2026-09-05 18:57:49 +02:00
Linus Rath 9ab7339320 feat: add plugin ui.prompt dialog and first-class settings-section tabs 2026-07-09 14:51:17 +02:00
Linus Rath 512adab7e3 feat: add privileged same-origin plugin tier + crypto API surface 2026-06-28 16:51:42 +02:00
Linus Rath 6ee3849463 fix: theme plugin slot iframes with host font + color tokens 2026-05-31 16:28:22 +02:00
dealerweb 2ba0003e16 Feature: localizable sandboxed plugins (manifest locales + api.i18n.t)
The plugin runtime received the active locale (init payload + 'locale-change')
and plugins could declare a `locales` map, but none of it was usable: the
locales never reached the runtime, and buildPluginApi exposed no i18n. So
plugin code calling pluginApi.i18n.t(...) (as the External Link Warning plugin
does) always got undefined and fell back to English.

Thread plugin locales end to end and surface an i18n API:
- ServerPlugin gains `locales`; the upload route persists manifest.locales
  (alongside configSchema/settingsSchema), and /api/plugins surfaces it to the
  client so it flows registry -> client -> sandbox host-bridge -> runtime.
- runtime sets __PLUGIN_LOCALE__ at init (not only on later 'locale-change')
  and buildPluginApi exposes `i18n.locale` + `i18n.t(key, vars)` resolving
  against the plugin's declared locales (manifest.locales) with English/key
  fallback and {placeholder} interpolation.

Lets any sandboxed plugin localize its strings from its manifest.
2026-05-30 15:56:55 +02:00
Linus Rath fc5f6f43d6 feat: expose PWA, app identity, and extension directory keys in JSON config #312 2026-05-21 23:35:58 +02:00
Linus Rath 088810bd20 feat: harden plugin sandbox and migrate in-tree plugins 2026-05-18 12:17:54 +02:00
Linus Rath e16f572252 fix: use plugin slot offer snapshots for useSyncExternalStore 2026-05-18 11:00:05 +02:00
Linus Rath 9f312aa556 feat: sandbox plugins in null-origin iframes with postMessage RPC 2026-05-18 10:50:49 +02:00
Linus Rath 8c93941d8d feat: render app-top-banner slot on every authenticated page 2026-05-16 19:39:45 +02:00
Linus Rath 76b21147e4 feat: add plugin/theme harness and admin dashboard
Plugin & Theme System:
- Add plugin type definitions, permissions (30+), and validation constants
- Add IndexedDB storage layer for plugin code, theme CSS, and previews
- Add theme CSS sanitization, injection, and safety validation
- Add HookBus event system with 130+ hooks across 20 domains
- Add plugin ZIP extraction and manifest validation with JS security checks
- Add sandboxed PluginAPI factory with scoped storage, logging, and permission gating
- Add plugin loader with blob URL dynamic import and auto-disable circuit breaker
- Add 3 built-in themes (Nord, Catppuccin, Solarized)
- Add Zustand plugin store with install/uninstall/enable/disable lifecycle
- Add PluginSlot, PluginSlotRenderer, and PluginErrorBoundary components
- Add plugins and themes settings UI panels
- Integrate plugin slots into email viewer, composer, navigation rail, sidebar, and context menu
- Extend theme store with custom theme installation and activation

Admin Dashboard:
- Add admin authentication with scrypt password hashing and AES-256-GCM sessions
- Add rate-limited login (5 attempts/15min per IP)
- Add config manager with admin override > env var > default priority
- Add settings policy system with feature gates and per-setting restrictions
- Add audit logging with rotation
- Add admin API routes (login, logout, config, policy, audit, password change)
- Add admin UI pages (login, dashboard, config, policy, audit)
- Add policy store for client-side feature gate enforcement
- Wire admin password initialization into server instrumentation

Tests:
- Add 139 tests across 10 test files covering all plugin/theme modules
2026-03-25 00:44:03 +01:00