docs: update contributing guidelines and enhance README formatting
This commit is contained in:
+9
-2
@@ -7,17 +7,20 @@ Thank you for your interest in contributing to Bulwark Webmail! This document pr
|
|||||||
### Development Setup
|
### Development Setup
|
||||||
|
|
||||||
1. **Fork and clone** the repository:
|
1. **Fork and clone** the repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/bulwarkmail/webmail.git
|
git clone https://github.com/bulwarkmail/webmail.git
|
||||||
cd webmail
|
cd webmail
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Install dependencies**:
|
2. **Install dependencies**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Set up environment**:
|
3. **Set up environment**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env.local
|
cp .env.example .env.local
|
||||||
# Edit .env.local with your JMAP server URL
|
# Edit .env.local with your JMAP server URL
|
||||||
@@ -75,9 +78,10 @@ This project uses **next-intl** for internationalization. Please follow these gu
|
|||||||
### Key Rules
|
### Key Rules
|
||||||
|
|
||||||
1. **Never hardcode user-facing text** - Always use translations:
|
1. **Never hardcode user-facing text** - Always use translations:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
const t = useTranslations('namespace');
|
const t = useTranslations("namespace");
|
||||||
return <div>{t('key')}</div>;
|
return <div>{t("key")}</div>;
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Translation file locations**:
|
2. **Translation file locations**:
|
||||||
@@ -109,6 +113,7 @@ This project uses **next-intl** for internationalization. Please follow these gu
|
|||||||
### Before Submitting
|
### Before Submitting
|
||||||
|
|
||||||
1. **Create a feature branch**:
|
1. **Create a feature branch**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git checkout -b feature/your-feature-name
|
git checkout -b feature/your-feature-name
|
||||||
```
|
```
|
||||||
@@ -147,6 +152,7 @@ Follow the conventional commits format:
|
|||||||
- `chore:` - Maintenance tasks
|
- `chore:` - Maintenance tasks
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```
|
```
|
||||||
feat: add email threading support
|
feat: add email threading support
|
||||||
fix: resolve attachment download issue
|
fix: resolve attachment download issue
|
||||||
@@ -184,6 +190,7 @@ webmail/
|
|||||||
## Questions?
|
## Questions?
|
||||||
|
|
||||||
If you have questions about contributing, feel free to:
|
If you have questions about contributing, feel free to:
|
||||||
|
|
||||||
- Open an issue for discussion
|
- Open an issue for discussion
|
||||||
- Check existing issues and pull requests
|
- Check existing issues and pull requests
|
||||||
|
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ OAUTH_ISSUER_URL= # optional, for external IdPs (Keycloak, Authe
|
|||||||
```
|
```
|
||||||
|
|
||||||
Endpoints are auto-discovered via `.well-known/oauth-authorization-server` or `.well-known/openid-configuration`.
|
Endpoints are auto-discovered via `.well-known/oauth-authorization-server` or `.well-known/openid-configuration`.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -210,12 +211,13 @@ SESSION_SECRET=your-secret-key # Generate with: openssl rand -base64 32
|
|||||||
```
|
```
|
||||||
|
|
||||||
Credentials encrypted with AES-256-GCM, stored in an httpOnly cookie (30-day expiry).
|
Credentials encrypted with AES-256-GCM, stored in an httpOnly cookie (30-day expiry).
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Keyboard Shortcuts
|
## Keyboard Shortcuts
|
||||||
|
|
||||||
| Key | Action |
|
| Key | Action |
|
||||||
|-----|--------|
|
| ------------- | ----------------------- |
|
||||||
| `j` / `k` | Navigate between emails |
|
| `j` / `k` | Navigate between emails |
|
||||||
| `Enter` / `o` | Open email |
|
| `Enter` / `o` | Open email |
|
||||||
| `Esc` | Close / deselect |
|
| `Esc` | Close / deselect |
|
||||||
@@ -231,7 +233,7 @@ Credentials encrypted with AES-256-GCM, stored in an httpOnly cookie (30-day exp
|
|||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
| ------------- | ------------------------------------------------- |
|
||||||
| **Framework** | [Next.js 16](https://nextjs.org/) with App Router |
|
| **Framework** | [Next.js 16](https://nextjs.org/) with App Router |
|
||||||
| **Language** | TypeScript |
|
| **Language** | TypeScript |
|
||||||
| **Styling** | [Tailwind CSS v4](https://tailwindcss.com/) |
|
| **Styling** | [Tailwind CSS v4](https://tailwindcss.com/) |
|
||||||
|
|||||||
+22
@@ -5,12 +5,14 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
## Completed Features
|
## Completed Features
|
||||||
|
|
||||||
### Core Infrastructure
|
### Core Infrastructure
|
||||||
|
|
||||||
- [x] Next.js 16 with TypeScript and App Router
|
- [x] Next.js 16 with TypeScript and App Router
|
||||||
- [x] Tailwind CSS v4 with Oxide engine
|
- [x] Tailwind CSS v4 with Oxide engine
|
||||||
- [x] Zustand state management
|
- [x] Zustand state management
|
||||||
- [x] Custom JMAP client implementation (RFC 8620)
|
- [x] Custom JMAP client implementation (RFC 8620)
|
||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
- [x] Login with JMAP server authentication
|
- [x] Login with JMAP server authentication
|
||||||
- [x] Session management (no password storage for security)
|
- [x] Session management (no password storage for security)
|
||||||
- [x] Username autocomplete with history
|
- [x] Username autocomplete with history
|
||||||
@@ -23,6 +25,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] "Remember me" session persistence for Basic Auth (AES-256-GCM encrypted httpOnly cookie)
|
- [x] "Remember me" session persistence for Basic Auth (AES-256-GCM encrypted httpOnly cookie)
|
||||||
|
|
||||||
### JMAP Server Connection
|
### JMAP Server Connection
|
||||||
|
|
||||||
- [x] Session establishment and keep-alive
|
- [x] Session establishment and keep-alive
|
||||||
- [x] Connection error handling and retries
|
- [x] Connection error handling and retries
|
||||||
- [x] CORS error detection with actionable user guidance
|
- [x] CORS error detection with actionable user guidance
|
||||||
@@ -32,6 +35,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] Shared folders support (multi-account access)
|
- [x] Shared folders support (multi-account access)
|
||||||
|
|
||||||
### Email Operations
|
### Email Operations
|
||||||
|
|
||||||
- [x] Email fetching and display
|
- [x] Email fetching and display
|
||||||
- [x] Full HTML email rendering
|
- [x] Full HTML email rendering
|
||||||
- [x] Compose, reply, reply-all, forward
|
- [x] Compose, reply, reply-all, forward
|
||||||
@@ -48,6 +52,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] Email threading (Gmail-style inline expansion)
|
- [x] Email threading (Gmail-style inline expansion)
|
||||||
|
|
||||||
### Real-time Updates
|
### Real-time Updates
|
||||||
|
|
||||||
- [x] EventSource for JMAP push notifications
|
- [x] EventSource for JMAP push notifications
|
||||||
- [x] State synchronization
|
- [x] State synchronization
|
||||||
- [x] Email arrival notifications
|
- [x] Email arrival notifications
|
||||||
@@ -55,6 +60,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] Mailbox change handling
|
- [x] Mailbox change handling
|
||||||
|
|
||||||
### User Interface
|
### User Interface
|
||||||
|
|
||||||
- [x] Three-pane layout (sidebar, list, viewer)
|
- [x] Three-pane layout (sidebar, list, viewer)
|
||||||
- [x] Minimalist design system
|
- [x] Minimalist design system
|
||||||
- [x] Dark and light theme support
|
- [x] Dark and light theme support
|
||||||
@@ -86,6 +92,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] Screen reader live region announcements (sr-only)
|
- [x] Screen reader live region announcements (sr-only)
|
||||||
|
|
||||||
### Internationalization
|
### Internationalization
|
||||||
|
|
||||||
- [x] English language support
|
- [x] English language support
|
||||||
- [x] French language support
|
- [x] French language support
|
||||||
- [x] Japanese language support
|
- [x] Japanese language support
|
||||||
@@ -98,6 +105,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] Language preference persistence
|
- [x] Language preference persistence
|
||||||
|
|
||||||
### Security & Accessibility
|
### Security & Accessibility
|
||||||
|
|
||||||
- [x] External content blocked by default
|
- [x] External content blocked by default
|
||||||
- [x] HTML sanitization with DOMPurify
|
- [x] HTML sanitization with DOMPurify
|
||||||
- [x] User control for loading external content
|
- [x] User control for loading external content
|
||||||
@@ -114,6 +122,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] Screen reader sr-only live region for dynamic announcements
|
- [x] Screen reader sr-only live region for dynamic announcements
|
||||||
|
|
||||||
### Identity Management
|
### Identity Management
|
||||||
|
|
||||||
- [x] Multiple sender identities (name, email, signature)
|
- [x] Multiple sender identities (name, email, signature)
|
||||||
- [x] Sub-addressing support (user+tag@domain.com)
|
- [x] Sub-addressing support (user+tag@domain.com)
|
||||||
- [x] Per-identity signatures
|
- [x] Per-identity signatures
|
||||||
@@ -123,6 +132,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] Primary identity (matching login) selected by default in composer
|
- [x] Primary identity (matching login) selected by default in composer
|
||||||
|
|
||||||
### Address Book & Contacts
|
### Address Book & Contacts
|
||||||
|
|
||||||
- [x] Contact store with JMAP sync and local fallback
|
- [x] Contact store with JMAP sync and local fallback
|
||||||
- [x] Contact CRUD operations (create, read, update, delete)
|
- [x] Contact CRUD operations (create, read, update, delete)
|
||||||
- [x] Contacts list view with search/filter
|
- [x] Contacts list view with search/filter
|
||||||
@@ -136,12 +146,14 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] i18n support for contacts (all 8 languages)
|
- [x] i18n support for contacts (all 8 languages)
|
||||||
|
|
||||||
### Vacation Responder
|
### Vacation Responder
|
||||||
|
|
||||||
- [x] JMAP VacationResponse singleton management
|
- [x] JMAP VacationResponse singleton management
|
||||||
- [x] Settings tab with date range and message configuration
|
- [x] Settings tab with date range and message configuration
|
||||||
- [x] Sidebar indicator when vacation auto-reply is active
|
- [x] Sidebar indicator when vacation auto-reply is active
|
||||||
- [x] i18n support (all 8 languages)
|
- [x] i18n support (all 8 languages)
|
||||||
|
|
||||||
### Calendar Integration
|
### Calendar Integration
|
||||||
|
|
||||||
- [x] JMAP Calendar types (RFC 8984) and client methods
|
- [x] JMAP Calendar types (RFC 8984) and client methods
|
||||||
- [x] Calendar capability detection (urn:ietf:params:jmap:calendars)
|
- [x] Calendar capability detection (urn:ietf:params:jmap:calendars)
|
||||||
- [x] Calendar store with Zustand (persist middleware)
|
- [x] Calendar store with Zustand (persist middleware)
|
||||||
@@ -177,6 +189,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] Event duplication button in modal (clones event +1 day, opens for editing)
|
- [x] Event duplication button in modal (clones event +1 day, opens for editing)
|
||||||
|
|
||||||
### Email Filters
|
### Email Filters
|
||||||
|
|
||||||
- [x] JMAP Sieve Scripts (RFC 9661) with capability detection
|
- [x] JMAP Sieve Scripts (RFC 9661) with capability detection
|
||||||
- [x] Visual rule builder (conditions: From/To/Cc/Subject/Header/Size/Body, actions: Move/Copy/Forward/Mark read/Star/Label/Discard/Reject/Keep/Stop)
|
- [x] Visual rule builder (conditions: From/To/Cc/Subject/Header/Size/Body, actions: Move/Copy/Forward/Mark read/Star/Label/Discard/Reject/Keep/Stop)
|
||||||
- [x] Raw Sieve script editor with syntax validation
|
- [x] Raw Sieve script editor with syntax validation
|
||||||
@@ -189,6 +202,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] i18n support (all 8 languages)
|
- [x] i18n support (all 8 languages)
|
||||||
|
|
||||||
### Email Templates
|
### Email Templates
|
||||||
|
|
||||||
- [x] Reusable email templates with local storage persistence
|
- [x] Reusable email templates with local storage persistence
|
||||||
- [x] Category organization (General, Business, Personal, Support, Follow-up, custom)
|
- [x] Category organization (General, Business, Personal, Support, Follow-up, custom)
|
||||||
- [x] Dynamic placeholder variables with auto-fill from composer context
|
- [x] Dynamic placeholder variables with auto-fill from composer context
|
||||||
@@ -200,10 +214,12 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] i18n support (all 8 languages)
|
- [x] i18n support (all 8 languages)
|
||||||
|
|
||||||
### Email Display
|
### Email Display
|
||||||
|
|
||||||
- [x] Proper email layout without horizontal scroll or clipping
|
- [x] Proper email layout without horizontal scroll or clipping
|
||||||
- [x] Blocked image container collapsing (no empty spaces in newsletters)
|
- [x] Blocked image container collapsing (no empty spaces in newsletters)
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
- [x] Unit tests for validation utilities (57 tests)
|
- [x] Unit tests for validation utilities (57 tests)
|
||||||
- [x] Unit tests for email sanitization (27 tests)
|
- [x] Unit tests for email sanitization (27 tests)
|
||||||
- [x] Unit tests for color transformation (40 tests)
|
- [x] Unit tests for color transformation (40 tests)
|
||||||
@@ -226,6 +242,7 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
- [x] Playwright E2E framework setup
|
- [x] Playwright E2E framework setup
|
||||||
|
|
||||||
### Deployment
|
### Deployment
|
||||||
|
|
||||||
- [x] Runtime environment variables (Docker-friendly configuration)
|
- [x] Runtime environment variables (Docker-friendly configuration)
|
||||||
- [x] Health check endpoint
|
- [x] Health check endpoint
|
||||||
- [x] Docker support (multi-stage build, docker-compose, standalone output)
|
- [x] Docker support (multi-stage build, docker-compose, standalone output)
|
||||||
@@ -238,26 +255,31 @@ This document tracks the development status and planned features for Bulwark Web
|
|||||||
## Planned Features
|
## Planned Features
|
||||||
|
|
||||||
### Advanced Features
|
### Advanced Features
|
||||||
|
|
||||||
- [ ] Free/busy queries (Principal/getAvailability)
|
- [ ] Free/busy queries (Principal/getAvailability)
|
||||||
- [ ] Calendar sharing UI (JMAP Sharing RFC 9670)
|
- [ ] Calendar sharing UI (JMAP Sharing RFC 9670)
|
||||||
- [ ] Email encryption (PGP/GPG)
|
- [ ] Email encryption (PGP/GPG)
|
||||||
|
|
||||||
### Performance Optimizations
|
### Performance Optimizations
|
||||||
|
|
||||||
- [ ] Email content caching
|
- [ ] Email content caching
|
||||||
- [ ] Bundle size optimization
|
- [ ] Bundle size optimization
|
||||||
- [ ] Service worker for offline support
|
- [ ] Service worker for offline support
|
||||||
- [ ] Lazy loading for attachments
|
- [ ] Lazy loading for attachments
|
||||||
|
|
||||||
### Testing (Remaining)
|
### Testing (Remaining)
|
||||||
|
|
||||||
- [ ] E2E tests with real JMAP server
|
- [ ] E2E tests with real JMAP server
|
||||||
- [ ] Accessibility testing
|
- [ ] Accessibility testing
|
||||||
- [ ] Performance testing
|
- [ ] Performance testing
|
||||||
|
|
||||||
### Deployment
|
### Deployment
|
||||||
|
|
||||||
- [ ] Production build optimizations
|
- [ ] Production build optimizations
|
||||||
- [ ] Monitoring and logging
|
- [ ] Monitoring and logging
|
||||||
|
|
||||||
### Security Enhancements
|
### Security Enhancements
|
||||||
|
|
||||||
- [ ] Rate limiting
|
- [ ] Rate limiting
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|||||||
+9
-1
@@ -6,7 +6,15 @@ services:
|
|||||||
env_file:
|
env_file:
|
||||||
- .env.local
|
- .env.local
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/api/health"]
|
test:
|
||||||
|
[
|
||||||
|
"CMD",
|
||||||
|
"wget",
|
||||||
|
"--no-verbose",
|
||||||
|
"--tries=1",
|
||||||
|
"--spider",
|
||||||
|
"http://127.0.0.1:3000/api/health",
|
||||||
|
]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ This document describes a system that allows the Bulwark Webmail application to
|
|||||||
### Rebuild Requirements
|
### Rebuild Requirements
|
||||||
|
|
||||||
| Addon Type | Source | Rebuild Needed? |
|
| Addon Type | Source | Rebuild Needed? |
|
||||||
|-----------|--------|----------------|
|
| ---------- | ---------------------------- | ------------------------------------------- |
|
||||||
| Theme | Bundled (`/addons/themes/`) | **Yes** — included at build time |
|
| Theme | Bundled (`/addons/themes/`) | **Yes** — included at build time |
|
||||||
| Theme | URL (remote) | **No** — CSS loaded via `<link>` at runtime |
|
| Theme | URL (remote) | **No** — CSS loaded via `<link>` at runtime |
|
||||||
| Plugin | Bundled (`/addons/plugins/`) | **Yes** — included at build time |
|
| Plugin | Bundled (`/addons/plugins/`) | **Yes** — included at build time |
|
||||||
@@ -57,7 +57,7 @@ Bundled addons are compiled into the app's static assets during `next build`. Ad
|
|||||||
## 2. Terminology
|
## 2. Terminology
|
||||||
|
|
||||||
| Term | Definition |
|
| Term | Definition |
|
||||||
|------|-----------|
|
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| **Addon** | Any installable extension — umbrella term for themes and plugins. |
|
| **Addon** | Any installable extension — umbrella term for themes and plugins. |
|
||||||
| **Theme** | An addon that only customizes visual appearance (colors, fonts, spacing, density). Ships as CSS + a manifest. Contains no executable code. |
|
| **Theme** | An addon that only customizes visual appearance (colors, fonts, spacing, density). Ships as CSS + a manifest. Contains no executable code. |
|
||||||
| **Plugin** | An addon that adds or modifies functionality. Ships as a JS/TS module + a manifest. May include a theme. |
|
| **Plugin** | An addon that adds or modifies functionality. Ships as a JS/TS module + a manifest. May include a theme. |
|
||||||
@@ -80,14 +80,14 @@ Every addon has an `addon.json` at its root:
|
|||||||
"description": "A brief description.",
|
"description": "A brief description.",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Jane Doe",
|
"name": "Jane Doe",
|
||||||
"url": "https://example.com"
|
"url": "https://example.com",
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://example.com/my-addon",
|
"homepage": "https://example.com/my-addon",
|
||||||
|
|
||||||
// ── Compatibility ──
|
// ── Compatibility ──
|
||||||
"engine": {
|
"engine": {
|
||||||
"webmail": ">=1.0.0" // Required host app version range
|
"webmail": ">=1.0.0", // Required host app version range
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Type ──
|
// ── Type ──
|
||||||
@@ -101,7 +101,7 @@ Every addon has an `addon.json` at its root:
|
|||||||
"theme": {
|
"theme": {
|
||||||
"variables": "theme.css", // CSS file with variable overrides
|
"variables": "theme.css", // CSS file with variable overrides
|
||||||
"presets": ["light", "dark"], // Which base modes it provides
|
"presets": ["light", "dark"], // Which base modes it provides
|
||||||
"preview": "preview.png" // Screenshot for settings UI
|
"preview": "preview.png", // Screenshot for settings UI
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Permissions (plugins only) ──
|
// ── Permissions (plugins only) ──
|
||||||
@@ -119,20 +119,14 @@ Every addon has an `addon.json` at its root:
|
|||||||
"navigation:tab", // Add a top-level navigation tab
|
"navigation:tab", // Add a top-level navigation tab
|
||||||
"context-menu:email", // Extend email context menu
|
"context-menu:email", // Extend email context menu
|
||||||
"keyboard-shortcuts", // Register keyboard shortcuts
|
"keyboard-shortcuts", // Register keyboard shortcuts
|
||||||
"external-fetch" // Fetch external URLs (declared origins)
|
"external-fetch", // Fetch external URLs (declared origins)
|
||||||
],
|
],
|
||||||
|
|
||||||
// ── External Origins (if external-fetch permission is declared) ──
|
// ── External Origins (if external-fetch permission is declared) ──
|
||||||
"allowedOrigins": [
|
"allowedOrigins": ["https://api.example.com"],
|
||||||
"https://api.example.com"
|
|
||||||
],
|
|
||||||
|
|
||||||
// ── Slots (declares which UI slots the plugin uses) ──
|
// ── Slots (declares which UI slots the plugin uses) ──
|
||||||
"slots": [
|
"slots": ["sidebar.bottom", "compose.toolbar", "viewer.actions"],
|
||||||
"sidebar.bottom",
|
|
||||||
"compose.toolbar",
|
|
||||||
"viewer.actions"
|
|
||||||
],
|
|
||||||
|
|
||||||
// ── Settings Schema (plugin-specific preferences) ──
|
// ── Settings Schema (plugin-specific preferences) ──
|
||||||
"settings": {
|
"settings": {
|
||||||
@@ -140,17 +134,17 @@ Every addon has an `addon.json` at its root:
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"label": "API Key",
|
"label": "API Key",
|
||||||
"description": "Your API key for the service.",
|
"description": "Your API key for the service.",
|
||||||
"secret": true
|
"secret": true,
|
||||||
},
|
},
|
||||||
"enabled": {
|
"enabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Enable integration",
|
"label": "Enable integration",
|
||||||
"default": true
|
"default": true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── i18n ──
|
// ── i18n ──
|
||||||
"locales": "locales/" // Directory with {locale}.json files
|
"locales": "locales/", // Directory with {locale}.json files
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -210,7 +204,7 @@ User selects theme in Settings → Appearance
|
|||||||
### 4.3 Theme Capabilities
|
### 4.3 Theme Capabilities
|
||||||
|
|
||||||
| Capability | Mechanism |
|
| Capability | Mechanism |
|
||||||
|-----------|-----------|
|
| ----------------- | -------------------------------------------------- |
|
||||||
| Colors | Override `--color-*` CSS variables |
|
| Colors | Override `--color-*` CSS variables |
|
||||||
| Typography | Override `--font-family-*` variables |
|
| Typography | Override `--font-family-*` variables |
|
||||||
| Spacing/density | Override `--spacing-density` multiplier |
|
| Spacing/density | Override `--spacing-density` multiplier |
|
||||||
@@ -271,7 +265,9 @@ export function activate(ctx: PluginContext) {
|
|||||||
ctx.contextMenu.register("email", {
|
ctx.contextMenu.register("email", {
|
||||||
label: ctx.i18n.t("translateEmail"),
|
label: ctx.i18n.t("translateEmail"),
|
||||||
icon: "Languages",
|
icon: "Languages",
|
||||||
action: (emailId) => { /* ... */ },
|
action: (emailId) => {
|
||||||
|
/* ... */
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,7 +407,8 @@ Next.js `import()` only resolves modules known at build time. To load plugins fr
|
|||||||
async function loadRemotePlugin(url: string): Promise<PluginModule> {
|
async function loadRemotePlugin(url: string): Promise<PluginModule> {
|
||||||
// 1. Fetch the plugin's JS bundle as text
|
// 1. Fetch the plugin's JS bundle as text
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
if (!response.ok) throw new Error(`Failed to fetch plugin: ${response.status}`);
|
if (!response.ok)
|
||||||
|
throw new Error(`Failed to fetch plugin: ${response.status}`);
|
||||||
const code = await response.text();
|
const code = await response.text();
|
||||||
|
|
||||||
// 2. Validate size limit (500 KB default)
|
// 2. Validate size limit (500 KB default)
|
||||||
@@ -437,11 +434,11 @@ async function loadRemotePlugin(url: string): Promise<PluginModule> {
|
|||||||
|
|
||||||
// Shared dependencies exposed to plugins — avoids bundling duplicates
|
// Shared dependencies exposed to plugins — avoids bundling duplicates
|
||||||
const SHARED_DEPS: Record<string, unknown> = {
|
const SHARED_DEPS: Record<string, unknown> = {
|
||||||
"react": React,
|
react: React,
|
||||||
"react/jsx-runtime": jsxRuntime,
|
"react/jsx-runtime": jsxRuntime,
|
||||||
"lucide-react": lucideIcons,
|
"lucide-react": lucideIcons,
|
||||||
"date-fns": dateFns,
|
"date-fns": dateFns,
|
||||||
"sonner": sonner,
|
sonner: sonner,
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -531,7 +528,7 @@ function Slot({ name }: { name: string }) {
|
|||||||
#### Available Slots
|
#### Available Slots
|
||||||
|
|
||||||
| Slot Name | Location | Use Case |
|
| Slot Name | Location | Use Case |
|
||||||
|-----------|----------|----------|
|
| ------------------- | -------------------------------------- | ------------------------------------- |
|
||||||
| `sidebar.top` | Top of sidebar, below compose button | Quick-access widgets |
|
| `sidebar.top` | Top of sidebar, below compose button | Quick-access widgets |
|
||||||
| `sidebar.bottom` | Bottom of sidebar, above storage quota | Extra navigation, widgets |
|
| `sidebar.bottom` | Bottom of sidebar, above storage quota | Extra navigation, widgets |
|
||||||
| `navigation.tabs` | Navigation rail, below contacts icon | New top-level views |
|
| `navigation.tabs` | Navigation rail, below contacts icon | New top-level views |
|
||||||
@@ -552,7 +549,7 @@ Plugins can listen to app events and state transitions:
|
|||||||
#### Email Hooks
|
#### Email Hooks
|
||||||
|
|
||||||
| Event | Payload | Description |
|
| Event | Payload | Description |
|
||||||
|-------|---------|-------------|
|
| --------------------------- | ----------------------- | --------------------------------- |
|
||||||
| `email:selected` | `{ emailId, email }` | User selected an email |
|
| `email:selected` | `{ emailId, email }` | User selected an email |
|
||||||
| `email:opened` | `{ emailId, email }` | Email viewer rendered |
|
| `email:opened` | `{ emailId, email }` | Email viewer rendered |
|
||||||
| `email:compose:open` | `{ mode, replyTo? }` | Composer opened |
|
| `email:compose:open` | `{ mode, replyTo? }` | Composer opened |
|
||||||
@@ -565,7 +562,7 @@ Plugins can listen to app events and state transitions:
|
|||||||
#### Calendar Hooks
|
#### Calendar Hooks
|
||||||
|
|
||||||
| Event | Payload | Description |
|
| Event | Payload | Description |
|
||||||
|-------|---------|-------------|
|
| ------------------------ | -------------------- | ---------------------- |
|
||||||
| `calendar:event:created` | `{ event }` | New event created |
|
| `calendar:event:created` | `{ event }` | New event created |
|
||||||
| `calendar:event:updated` | `{ event, changes }` | Event modified |
|
| `calendar:event:updated` | `{ event, changes }` | Event modified |
|
||||||
| `calendar:event:deleted` | `{ eventId }` | Event deleted |
|
| `calendar:event:deleted` | `{ eventId }` | Event deleted |
|
||||||
@@ -574,7 +571,7 @@ Plugins can listen to app events and state transitions:
|
|||||||
#### Contact Hooks
|
#### Contact Hooks
|
||||||
|
|
||||||
| Event | Payload | Description |
|
| Event | Payload | Description |
|
||||||
|-------|---------|-------------|
|
| ------------------ | --------------- | ------------------- |
|
||||||
| `contact:selected` | `{ contactId }` | Contact selected |
|
| `contact:selected` | `{ contactId }` | Contact selected |
|
||||||
| `contact:created` | `{ contact }` | New contact created |
|
| `contact:created` | `{ contact }` | New contact created |
|
||||||
| `contact:updated` | `{ contact }` | Contact modified |
|
| `contact:updated` | `{ contact }` | Contact modified |
|
||||||
@@ -582,7 +579,7 @@ Plugins can listen to app events and state transitions:
|
|||||||
#### App Hooks
|
#### App Hooks
|
||||||
|
|
||||||
| Event | Payload | Description |
|
| Event | Payload | Description |
|
||||||
|-------|---------|-------------|
|
| -------------------- | -------------- | ---------------------------- |
|
||||||
| `app:ready` | `{}` | App fully loaded |
|
| `app:ready` | `{}` | App fully loaded |
|
||||||
| `app:theme:changed` | `{ theme }` | Theme switched |
|
| `app:theme:changed` | `{ theme }` | Theme switched |
|
||||||
| `app:locale:changed` | `{ locale }` | Language changed |
|
| `app:locale:changed` | `{ locale }` | Language changed |
|
||||||
@@ -610,7 +607,7 @@ Permissions are enforced at the `PluginContext` level — if a plugin didn't dec
|
|||||||
### 8.2 Sandboxing Strategy
|
### 8.2 Sandboxing Strategy
|
||||||
|
|
||||||
| Layer | Mechanism |
|
| Layer | Mechanism |
|
||||||
|-------|-----------|
|
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| **Store access** | `PluginContext` exposes only permitted store slices. Write access returns proxied objects — mutations are validated before applying. |
|
| **Store access** | `PluginContext` exposes only permitted store slices. Write access returns proxied objects — mutations are validated before applying. |
|
||||||
| **DOM access** | Plugin components render inside an `<AddonErrorBoundary>`. They receive a scoped React tree — no direct `document` manipulation. |
|
| **DOM access** | Plugin components render inside an `<AddonErrorBoundary>`. They receive a scoped React tree — no direct `document` manipulation. |
|
||||||
| **Network** | `ctx.fetch()` is a controlled wrapper. Requests are only allowed to origins listed in `allowedOrigins`. All other `fetch` / `XMLHttpRequest` calls from plugin code are blocked via CSP headers. |
|
| **Network** | `ctx.fetch()` is a controlled wrapper. Requests are only allowed to origins listed in `allowedOrigins`. All other `fetch` / `XMLHttpRequest` calls from plugin code are blocked via CSP headers. |
|
||||||
@@ -621,6 +618,7 @@ Permissions are enforced at the `PluginContext` level — if a plugin didn't dec
|
|||||||
### 8.3 Content Security Policy
|
### 8.3 Content Security Policy
|
||||||
|
|
||||||
Theme CSS is sanitized to disallow:
|
Theme CSS is sanitized to disallow:
|
||||||
|
|
||||||
- `url()` references to external domains (only data URIs and same-origin).
|
- `url()` references to external domains (only data URIs and same-origin).
|
||||||
- `@import` statements.
|
- `@import` statements.
|
||||||
- `expression()` or `behavior:` (legacy IE attack vectors).
|
- `expression()` or `behavior:` (legacy IE attack vectors).
|
||||||
@@ -651,7 +649,7 @@ function AddonErrorBoundary({ addonId, children }) {
|
|||||||
### 9.1 Addon Formats
|
### 9.1 Addon Formats
|
||||||
|
|
||||||
| Format | Description | Use Case |
|
| Format | Description | Use Case |
|
||||||
|--------|-------------|----------|
|
| -------------- | --------------------------------------------- | ----------------------------------- |
|
||||||
| **Bundled** | Shipped inside the app's `/addons/` directory | Default themes, first-party plugins |
|
| **Bundled** | Shipped inside the app's `/addons/` directory | Default themes, first-party plugins |
|
||||||
| **URL** | Loaded from a remote URL at runtime | Self-hosted or third-party addons |
|
| **URL** | Loaded from a remote URL at runtime | Self-hosted or third-party addons |
|
||||||
| **Local file** | Loaded from a local path (dev mode only) | Plugin development |
|
| **Local file** | Loaded from a local path (dev mode only) | Plugin development |
|
||||||
@@ -692,12 +690,14 @@ addons/
|
|||||||
### 9.4 Installation Flow
|
### 9.4 Installation Flow
|
||||||
|
|
||||||
**From URL:**
|
**From URL:**
|
||||||
|
|
||||||
1. User pastes addon URL into Settings → Addons → "Install from URL".
|
1. User pastes addon URL into Settings → Addons → "Install from URL".
|
||||||
2. App fetches `{url}/addon.json`, validates schema and compatibility.
|
2. App fetches `{url}/addon.json`, validates schema and compatibility.
|
||||||
3. Manifest is stored in addon registry (`localStorage`).
|
3. Manifest is stored in addon registry (`localStorage`).
|
||||||
4. On next activation, the addon's assets are fetched and cached.
|
4. On next activation, the addon's assets are fetched and cached.
|
||||||
|
|
||||||
**Bundled:**
|
**Bundled:**
|
||||||
|
|
||||||
1. Addons in `/addons/` are auto-discovered at build time.
|
1. Addons in `/addons/` are auto-discovered at build time.
|
||||||
2. A generated `addon-registry.json` maps addon IDs to their local paths.
|
2. A generated `addon-registry.json` maps addon IDs to their local paths.
|
||||||
3. Bundled addons appear pre-installed (but can be disabled).
|
3. Bundled addons appear pre-installed (but can be disabled).
|
||||||
@@ -779,6 +779,7 @@ Appearance
|
|||||||
```
|
```
|
||||||
|
|
||||||
When a theme is selected, the app:
|
When a theme is selected, the app:
|
||||||
|
|
||||||
1. Sets `data-theme` attribute on `<html>`.
|
1. Sets `data-theme` attribute on `<html>`.
|
||||||
2. Loads the theme's CSS file.
|
2. Loads the theme's CSS file.
|
||||||
3. Persists the choice in `ThemeStore`.
|
3. Persists the choice in `ThemeStore`.
|
||||||
@@ -894,6 +895,7 @@ nord-theme/
|
|||||||
```
|
```
|
||||||
|
|
||||||
`addon.json`:
|
`addon.json`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "org.nordtheme.bulwark-webmail",
|
"id": "org.nordtheme.bulwark-webmail",
|
||||||
@@ -925,6 +927,7 @@ email-translator/
|
|||||||
```
|
```
|
||||||
|
|
||||||
`addon.json`:
|
`addon.json`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "com.example.email-translator",
|
"id": "com.example.email-translator",
|
||||||
|
|||||||
Reference in New Issue
Block a user