Commit graph

7634 commits

Author SHA1 Message Date
Ben Gotow
e61e511d44
Update build-macos.yaml 2026-01-07 21:17:36 -06:00
Claude
b5f7d58357
Update macOS CI to use macos-15-intel for Intel builds
macos-13 is being deprecated (retired December 2025). Update to use:
- macos-15 for ARM64 builds
- macos-15-intel for Intel builds (available until August 2027)

Also improved the workflow to properly rename both Intel and Apple Silicon
artifacts using matrix variables.
2026-01-08 03:14:47 +00:00
Ben Gotow
b272fb6c42 Bump mailsync submodule 2026-01-07 21:02:41 -06:00
Ben Gotow
0463ff63fd Bump mailsync submodule 2026-01-07 20:34:09 -06:00
Ben Gotow
38672d50cf
Migrate Windows code signing to Azure Trusted Signing (#2552)
Replace traditional P12 certificate-based signing with Azure Trusted Signing
for Windows builds. This provides better security and simplifies certificate
management by using Azure's cloud-based signing service.

Changes:
- Remove P12 certificate setup step from workflow
- Add Azure Trusted Signing action to sign application files (exe, dll)
- Add Azure Trusted Signing action to sign the installer after creation
- Update create-signed-windows-installer.js to remove certificate options

Required secrets:
- AZURE_TENANT_ID
- AZURE_CLIENT_ID
- AZURE_CLIENT_SECRET
- AZURE_TRUSTED_SIGNING_ENDPOINT
- AZURE_TRUSTED_SIGNING_ACCOUNT_NAME
- AZURE_TRUSTED_SIGNING_CERT_PROFILE_NAME

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-07 17:21:46 -06:00
Ben Gotow
f81017ee52
Adopt newer features of the Electron Notification API, Windows toasts with XML (#2549)
* Add implementation plan for enhanced notification system

Plan covers migrating from Web Notification API to Electron's main
process Notification module to enable macOS inline reply, action
buttons, and Windows rich toast notifications.

* Implement enhanced notification system with main process IPC

Migrate from Web Notification API to Electron's main process
Notification module to enable platform-specific features:

- macOS: inline reply support via hasReply, action buttons
- Windows: rich toast notifications via toastXml
- Linux: urgency levels, persistent notifications

Key changes:
- Add notification-ipc.ts: Main process handlers following
  quickpreview-ipc.ts pattern
- Update native-notifications.ts: IPC bridge with callback
  management for notification events
- Update unread-notifications: Add "Mark as Read" and "Archive"
  action buttons, implement _handleNotificationAction method
- Register IPC handlers in application.ts

The notification system now properly forwards reply, action, click,
and close events from main process back to renderer via
windowManager.sendToAllWindows().

* Improve Windows toast notifications following Microsoft best practices

- Add hint-maxLines="1" to sender name to prevent wrapping
- Add hint-style="captionSubtle" to message snippet for visual hierarchy
- Add group attribute for notification stacking per thread
- Add reply text input with Send button for Windows toast
- Add activationType="background" for action buttons
- Create dedicated buildWindowsSummaryToastXml for multi-message notifications
- Add displaySummaryNotification method for summary notifications
- Update _notifyAll to use summary notification with sender names

References:
- https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/adaptive-interactive-toasts
- https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-ux-guidance

* Address PR feedback: improve XML escaping, add types, consolidate helpers

- Replace manual XML escaping with browser DOM APIs (XMLSerializer)
  to properly handle full UTF-8 range including international text
- Add IIPCNotificationOptions interface for type safety in IPC calls
- Consolidate sender list formatting into formatSenderList helper
  with proper English formatting ("X", "X and Y", "X, Y, and Z")
- Remove implementation plan document (now complete)

* Add icon path validation to prevent arbitrary file access

Validate that notification icon paths are within allowed directories
before passing them to nativeImage.createFromPath(). This prevents
the renderer from potentially specifying arbitrary file paths.

Allowed paths:
- Application's static resources (resourcePath/static/)
- System icon directories on Linux (/usr/share/icons/, etc.)
- System temp directory (for converted PNG icons on Linux)

Uses path.resolve() to prevent directory traversal attacks, following
the same pattern as quickpreview-ipc.ts.

* Remove unused notification code

- Remove _macNotificationsByTag: legacy field from old node-mac-notifier
  implementation, never used in new IPC-based approach
- Remove closeNotificationsForThread: never called; the renderer already
  handles notification dismissal via individual NotificationHandle.close()
  calls when threads are marked as read

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-07 15:45:17 -06:00
Ben Gotow
bcef00d4b4
Upgrade Windows installer spinner to high-resolution (800x800) (#2551)
- Increased resolution from 400x400 to 800x800 for better display on high-DPI screens
- Updated spinner color to use Mailspring brand blue (#419bf9)
- Added smoother animation with 48 frames at 24fps
- Maintained original dual-arc spinner design style

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-07 12:27:38 -06:00
Ben Gotow
f56f1577eb
Fix thread list context menu not checking for empty items array (#2550)
The itemsForMouseEvent method returns an empty array [] when no item
is found at the click location, not null. The check `if (!items)` was
incorrect because an empty array is truthy in JavaScript. This caused
the context menu to be created with no threads when right-clicking
on empty space, leading to errors.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-07 12:22:55 -06:00
Ben Gotow
6e19fca2f3
Replace componentWillReceiveProps usage (#2546)
* Migrate componentWillReceiveProps to componentDidUpdate for React 17 compatibility

This commit migrates all 47 usages of componentWillReceiveProps across the
codebase to use the componentDidUpdate pattern, which is the recommended
approach for React 17+.

Migration pattern:
- Changed from comparing `nextProps` vs `this.props` to `prevProps` vs `this.props`
- Merged logic into existing componentDidUpdate methods where applicable
- Also migrated componentWillMount to componentDidMount + state initialization
  where needed (message-list-scroll-tooltip.tsx, thread-list-scroll-tooltip.tsx)

Files migrated:
- Core components: spinner, mail-important-icon, empty-list-state, fixed-popover,
  time-picker, mini-month-view, dropdown-menu, injected-component,
  injected-component-set, sheet, sheet-toolbar, scenario-editor-row,
  swipe-container, outline-view-item, menu, multiselect-list,
  key-commands-region, resizable-region, lazy-rendered-list,
  tokenizing-text-field, webview
- Decorators/HOCs: listens-to-flux-store, listens-to-observable,
  flux-container, inflates-draft-client-id
- Internal packages: composer-header, send-action-button, toolbar-category-picker,
  autoload-images-header, message-item-body, message-item-container,
  newsletter-signup, preferences-account-details, preferences-appearance,
  send-later-status, send-reminders-composer-button, thread-search-bar,
  calendar-event-popover, activity dashboard root, label-picker-popover,
  move-picker-popover, copy-button, thread-sharing-button, undo-redo-toast,
  scroll tooltips for message-list and thread-list

* Fix race conditions and edge cases in componentWillReceiveProps migration

This commit addresses critical issues found during review of the
lifecycle migration from componentWillReceiveProps to componentDidUpdate.

## inflates-draft-client-id.tsx
- Clear draft/session state immediately when headerMessageId changes
- Prevents rendering with mismatched draft content during async fetch

## listens-to-observable.tsx
- Added subscription generation counter (subscriptionId)
- Guards against stale observable emissions updating state
- Prevents race condition where old observable emits after props change

## flux-container.tsx
- Store getStateFromStores as instance property (_getStateFromStores)
- Reorder: setup listeners BEFORE setState in componentDidUpdate
- Listeners now reference instance property, not captured props
- Prevents stale closures and race conditions with store events

## multiselect-list.tsx
- Replace expensive _.isEqual(prevProps, this.props) with specific checks
- Only check dataSource and columns identity (props that affect state)
- Remove unnecessary teardown/setup (WorkspaceStore listener is static)
- Significant performance improvement + eliminates race condition

## menu.tsx
- Added getDerivedStateFromProps to clamp selectedIndex before render
- Prevents undefined access when items array shrinks
- Simplified componentDidUpdate to only handle selection preservation
- Added bounds checking before accessing prevProps.items[oldSelectedIndex]

* Use getDerivedStateFromProps to eliminate state/props desync renders

This commit addresses the remaining MEDIUM-risk issues where components
could render with state out of sync with props for one frame.

## inflates-draft-client-id.tsx
- Added getDerivedStateFromProps to detect headerMessageId changes
- Clears draft/session state BEFORE render (not after in componentDidUpdate)
- Track _lastHeaderMessageId in state to detect prop changes
- Eliminates the one-frame mismatch between draft and headerMessageId

## multiselect-list.tsx
- Added getDerivedStateFromProps to update handler/columns before render
- Store _checkmarkColumn in state (created once in constructor)
- Track _prevDataSource and _prevColumns to detect changes
- Handler is now always in sync with current dataSource
- Removed _getStateFromStores (logic moved to getDerivedStateFromProps)
- _onChange now triggers empty setState to let getDerivedStateFromProps handle updates

## menu.tsx
- Enhanced getDerivedStateFromProps to handle selection preservation
- Added selectedItemKey and prevItems to state
- When items change, finds selected item by key in new array
- Selection is preserved BEFORE render (not after in componentDidUpdate)
- Removed selection preservation logic from componentDidUpdate
- Updated all selection state changes to include selectedItemKey

All three components now have no render where state is out of sync with props.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-06 15:27:45 -06:00
Ben Gotow
f61eab48c1
Upgrade TypeScript from version 3 to 5 (#2547) 2026-01-06 12:41:35 -06:00
Ben Gotow
1a5e7db483
Upgrade React from 16.6.0 to 16.9.0 (#2545)
Enables React hooks support (introduced in 16.8) and adds the React 16.9
deprecation warnings for unsafe lifecycle methods.

Breaking changes from upgrade notes:
- React 16.7: No breaking changes (bugfix release)
- React 16.8: Introduces hooks, no breaking changes for stable releases
- React 16.9: Deprecates componentWillMount, componentWillReceiveProps,
  componentWillUpdate with console warnings (still work, but should be
  renamed to UNSAFE_ prefix eventually)

Current codebase uses deprecated methods in 53 files - these will show
console warnings but continue to function.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 23:51:54 -06:00
Ben Gotow
ea673faf0a
Implement event creation in quick popover (#2543)
* Add implementation plan for quick event creation

Document the approach for implementing SyncbackEventTask integration
in quick-event-popover.tsx, including task queueing patterns and
calendar event focus behavior.

* Implement quick event creation with calendar focus

- Add FocusedEventInfo type as Pick<EventOccurrence, 'start' | 'id'>
  for minimal event focus data
- Update focusedEvent prop type in calendar components to use the
  new minimal type instead of full EventOccurrence
- Implement createEvent in quick-event-popover.tsx:
  - Use SyncbackEventTask.forCreating() to save the event
  - Wait for task completion with TaskQueue.waitForPerformRemote()
  - Focus calendar on new event with Actions.focusCalendarEvent()

* Delete plans/quick-event-creation-implementation.md

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 23:17:55 -06:00
Ben Gotow
e2e858c93d
Fix null check in parseDateString causing Quick Event error (#2542)
The check `!(val in item)` only verified if the key existed, but
chrono-node can return results where `end` is explicitly null.
Changed to `!item[val]` to properly skip null/undefined values
before accessing `.knownValues`.

Fixes TypeError: Cannot read properties of null (reading 'knownValues')

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 22:47:11 -06:00
Ben Gotow
829c8c37a7
Implement event search for calendar feature (#2533)
* Implement event search functionality in calendar

Add a fully functional event search bar to the calendar sidebar that allows
users to search for events by title, description, location, or participants.

Features:
- Search input with debounced queries (300ms) to avoid excessive database calls
- Dropdown suggestions showing matching events with calendar color indicators
- Keyboard navigation (arrow keys, Enter to select, Escape to clear)
- Event details display including title, date/time, and location
- Results sorted by proximity to current date
- Respects disabled calendars filter
- Loading indicator during search

The implementation follows the same patterns used by the thread/email search
feature, leveraging the existing Event model's FTS5 search capability and
the Menu component for the dropdown UI.

* Move event search bar to toolbar with ThreadSearchBar styling

- Move EventSearchBar from calendar sidebar to toolbar location
- Create focusCalendarEvent action for communication between search
  bar and calendar component
- Update styling to match ThreadSearchBar design:
  - Fixed width toolbar appearance
  - Box-shadow border styling
  - Dropdown suggestions matching thread search style
  - Same clear button (searchclear.png) and search icon
- EventSearchBar now gets disabled calendars from config directly
- Calendar component listens to focusCalendarEvent action

The search bar now appears in the same toolbar area as the email
search bar, providing a consistent UX across the application.

* Fix focus ring to appear on entire search bar container

- Add 'focused' class to search bar container when input is focused
- Style the container with focus ring instead of the input element
- Use accent-primary color for consistent focus ring appearance

* Remove inner input focus styling to prevent double focus ring

* Fix stuck event selection after clicking search results

Clear focusedEvent when clicking on other events in the calendar
to prevent the search result event from remaining in a focused state
and continuously re-opening the popover.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 22:46:44 -06:00
Ben Gotow
40019cfed0
Align calendar plus icon with message button style (#2541)
* Align calendar plus icon with compose button style

Add a compose-style button to the MiniMonthView header for creating new
calendar events. The button uses the same toolbar-compose.png icon and
styling as the main compose button - positioned on the right edge with
opacity hover effects.

* Revert "Align calendar plus icon with compose button style"

This reverts commit 624fd1e0ee.

* Align calendar plus icon with compose button style

Update QuickEventButton to use the same compose icon and item-compose
class styling as the main ComposeButton, giving it consistent appearance
in the calendar toolbar.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 21:12:30 -06:00
Alexey Anufriev
f7f635421b
Fix SQL generation for multiple ORDER BY clauses (#2530) 2026-01-05 15:06:54 -06:00
Ben Gotow
594a32a5cf
Fix December month rendering blank and calendar height shifting (#2540)
The mini month view calendar was blank for December 2025 due to a bug in
week number calculation at year boundaries. When December ends in week 1
of the following year, the loop condition `week <= endWeek` (e.g., 49 <= 1)
was never true, so no weeks rendered.

Fixed by iterating through weeks sequentially using day addition instead
of week numbers. Also ensures consistent height by always rendering 6 weeks
regardless of the actual number of weeks the month spans.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 21:48:38 -06:00
Ben Gotow
c113627c03
Calendar preview: month view (#2537)
* Implement calendar month view with grid layout

- Add full MonthView component with proper data subscription
- Create MonthViewDayCell component for individual day cells
- Create MonthViewEvent component with fixed height and text truncation
- Support up to 5 visible events per day with "+N more" overflow
- Add event click/double-click handlers for selection and popover
- Style month view grid to match existing calendar design
- Navigate to week view when clicking day numbers or overflow

* Fix month view styling issues

- Remove duplicate left border on events (was duplicating existing border)
- Add proper width constraints to prevent horizontal overflow on months
  like December 2025 that have many events
- Add min-width: 0 to flex children to allow proper shrinking
- Add overflow: hidden to month-view container

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 21:44:46 -06:00
Ben Gotow
000784f2ce
Use DOM spellcheck attribute with typing-debounce - work around spellcheck issues on Windows (#2535)
* Use native browser spellcheck with typing-debounce optimization

Replace custom Slate spellcheck plugin with native browser spellcheck.
The spellCheck DOM attribute is toggled based on typing activity to avoid
performance issues - it's disabled while typing and re-enabled 800ms after
the user stops typing.

This simplification:
- Removes ~250 lines of custom spellcheck decoration code
- Uses Chromium's native spellcheck underlines and context menu
- Maintains performance by debouncing spellcheck during active typing
- Respects the existing core.composing.spellcheck user preference

* Disable spellcheck for code blocks, blockquotes, and links

Add spellCheck={false} attribute to elements that shouldn't be
spellchecked:
- blockquote: Quoted text from replies
- code blocks: Pre-formatted code
- codeInline: Inline code snippets
- links: URLs and link text

This preserves the behavior from the removed spellcheck-plugins.tsx
which excluded these element types from custom spellcheck decorations.

* Fix spellcheck context menu not showing suggestions

When right-clicking on a misspelled word without selecting it first,
the context menu was not showing spelling suggestions because the
word wasn't being extracted properly.

Now the onContextMenu handler:
- Extracts the word at cursor position when no text is selected
- Selects the word so the spelling correction replaces it properly
- Uses the same word boundary regex as the old spellcheck plugin

* Use webContents context-menu event for spellcheck suggestions

Replace custom context menu handling with Electron's webContents
'context-menu' event which provides native spellcheck integration:

- Uses params.misspelledWord and params.dictionarySuggestions from
  the browser's native spellcheck
- Uses webContents.replaceMisspelling() for corrections
- Uses session.addWordToSpellCheckerDictionary() for learning words
- Removes custom word extraction logic from composer-editor

This is the proper way to integrate with Chromium's native spellcheck
when using spellcheck="true" on contenteditable elements.

* Prevent double context menu in search tokenizing contenteditable

Add event.preventDefault() to the onContextMenu handler in
tokenizing-contenteditable.tsx to prevent the webContents context-menu
handler from also firing and showing a duplicate menu.

This component has spellCheck={false} so it doesn't benefit from native
spellcheck suggestions and continues to use the existing spelling menu.

* Remove webFrame spellcheck APIs, use native spellcheck everywhere

This completes the transition to native browser spellcheck by:

- Removing openSpellingMenuFor and openContextualMenuForInput from
  window-event-handler.ts (these used webFrame.isWordMisspelled)
- Removing isMisspelled and appendSpellingItemsToMenu from spellchecker.ts
- Updating tokenizing-contenteditable to use spellCheck={true} and
  removing its custom onContextMenu handler
- Updating webContents context-menu handler to handle all editable
  areas including INPUT/TEXTAREA elements
- Updating spellchecker tests to only test remaining functionality

The webFrame spellcheck APIs are removed because they don't work
reliably on Windows. Native browser spellcheck provides a more
consistent cross-platform experience.

* Disable cut/copy in context menu for password fields

Check params.inputFieldType to detect password inputs and disable
cut and copy menu items to prevent copying sensitive data.

* Use editFlags from context-menu params for menu item states

Replace manual detection of password fields and selection state with
Electron's built-in editFlags object (canCut, canCopy, canPaste).
This is more robust as Electron handles all edge cases including
password field restrictions automatically.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 21:32:59 -06:00
Ben Gotow
76709aa060
Add hover and active states to undo-redo toast buttons (#2539)
The action buttons in the undo-redo toast (like "Send now instead" and
"Undo") were missing visual feedback for user interaction. This adds:
- cursor: pointer to indicate clickability
- :active state with darker background for pressed feedback

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 21:23:39 -06:00
Ben Gotow
e73adfd9c0
Add plugin system architecture documentation and Claude skill (#2538)
Document the Mailspring plugin system including:
- Plugin discovery and loading from multiple directories
- Package.json schema and required fields
- Plugin lifecycle (activate/deactivate)
- Extension points: ComponentRegistry, ExtensionRegistry
- User plugin installation mechanisms
- Current limitations for future marketplace work

Add Claude skill to guide agents working on plugin-related code.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 21:00:53 -06:00
Ben Gotow
b80752fd44
Analyze and document provider-specific overrides in Python caldav (#2536)
* Add CalDAV compatibility documentation

Comprehensive documentation of 7 key CalDAV compatibility issues
discovered through analysis of the python-caldav library:

1. Search without component type - servers requiring comp-type filters
2. Recurrence expansion - broken/missing server-side expansion
3. Sync tokens - lack of RFC 6578 support and fallback strategies
4. Text search - substring/collation/case sensitivity issues
5. Time-range search - inaccurate overlap calculations
6. MKCALENDAR - calendar creation method variations
7. is-not-defined filter - property undefined filtering issues

Each document includes RFC specifications, real-world server
behaviors, python-caldav workarounds, and implementation guidance
with code examples.

* Add protocol-level CalDAV compatibility documentation

Three additional documents covering HTTP and protocol-level issues:

08-url-path-handling.md:
- Quoted vs unquoted URLs in responses
- Absolute vs relative URL handling
- Special characters (@, spaces, Unicode)
- Double-encoding edge cases

09-object-loading-recovery.md:
- event.load() failure recovery patterns
- Multiget fallback when direct GET fails
- Servers returning URLs but failing data fetch
- Partial data in search results

10-http-protocol-issues.md:
- XML with wrong Content-Type (iCloud, OX)
- WWW-Authenticate header parsing variations
- Authentication header case sensitivity
- HTTP/2 multiplexing issues (Baikal)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 15:36:41 -06:00
Ben Gotow
97c693107e More complete handling of recurring events with exceptions 2026-01-04 14:55:48 -06:00
Ben Gotow
4195f8eb96 Use CalDAV-provided calendar colors when available 2026-01-04 12:42:12 -06:00
Ben Gotow
db259f6be1 Revert "Fix Windows spellcheck not working for en-US (#2534)"
This reverts commit 306fcf21c5.
2026-01-03 20:35:16 -06:00
Ben Gotow
f92b02f34f Implement Syncback + Destroy event tasks 2026-01-03 20:34:23 -06:00
Ben Gotow
9a87b5a5ab Remove unused files 2026-01-03 20:34:23 -06:00
Ben Gotow
306fcf21c5
Fix Windows spellcheck not working for en-US (#2534) 2026-01-03 15:51:34 -06:00
Ben Gotow
6ec0bc748e
Replace AppVeyor with GitHub Actions Windows build (#2524)
* Add plan for replacing AppVeyor with GitHub Actions Windows build

Document the strategy for migrating the Windows build from AppVeyor to
GitHub Actions, including certificate handling changes and required secrets.

* Replace AppVeyor with GitHub Actions for Windows build

- Add .github/workflows/build-windows.yaml for Windows builds
- Delete .appveyor.yml (no longer needed)
- Remove temporary plan file

The new workflow:
- Uses windows-2022 runner with Node.js 20
- Handles code signing via base64-encoded certificate secret
- Runs npm ci, lint, build, and creates signed installer
- Uploads MailspringSetup.exe, *.nupkg, and RELEASES to S3

Required secrets to configure:
- WINDOWS_CODESIGN_CERT_BASE64: Base64-encoded P12 certificate
- WINDOWS_CODESIGN_CERT_PASSWORD: Certificate password

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-03 13:16:31 -06:00
Ben Gotow
27208467e1
Audit calendar preview features and gaps (#2532)
* Add calendar feature assessment and implementation roadmap

Document the current state of the calendar preview feature including:
- Features currently implemented (week view, RSVP, event display)
- Partially implemented/stubbed features
- Missing features compared to typical desktop calendar apps
- Key technical blockers (SyncbackEventTask unimplemented)
- Detailed phased to-do list for completing the calendar
- Architecture overview and code locations

* Expand calendar assessment with detailed architecture guide

Add comprehensive architectural documentation for implementers including:
- Complete directory structure with annotations
- Plugin architecture patterns and entry point examples
- Data flow diagram from UI to sync engine
- Data models with TypeScript definitions
- Reactive data patterns using RxJS observables
- Task system with working example (EventRSVPTask)
- ICS parsing utilities reference
- Feature implementation guide with file locations
- Component extension points
- Step-by-step examples for adding new views and write operations

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-03 12:53:33 -06:00
Ben Gotow
33236be6c5
Fix Download All changing filenames with hyphen-number patterns (#2531) 2026-01-02 19:50:46 -06:00
Ben Gotow
a78247af3c
Fix Linux tray icon sharing ID with other Electron apps (#2529)
Set app.setName('Mailspring') explicitly on Linux to ensure the system
tray icon gets a unique StatusNotifierItem ID. Without this, all Electron
applications share the same ID, causing KDE Plasma and other desktop
environments to synchronize tray visibility settings across different apps.

Fixes: https://community.getmailspring.com/t/tray-icon-share-id-with-different-apps-particularly-electron/14000
See: https://github.com/electron/electron/issues/40936

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 14:39:52 -06:00
Ben Gotow
0a9c8e8e7b Update dev env Node.js version from outdated Node 16 2026-01-02 14:38:51 -06:00
Ben Gotow
0563c480eb
Fix notifications respecting macOS Do Not Disturb (#2525)
* Fix DND/Focus mode not silencing new mail sounds on macOS 12+

Update macos-notification-state from v1.3.6 to v3.0.0 to add proper
support for macOS Focus mode (which replaced Do Not Disturb in macOS 12).
The old version did not detect Focus mode on macOS 12+, causing sounds
to play even when the user had DND/Focus enabled.

The v3.0.0 API change requires async handling since getDoNotDisturb()
now returns a Promise, so update doNotDisturb() and displayNotification()
to be async, along with their callers in the unread-notifications package.

Fixes: https://community.getmailspring.com/t/notifications-do-not-respect-focus-mode-on-macos/9737

* Silence all sounds when macOS Focus/DND mode is enabled

Move the Do Not Disturb check into SoundRegistry.playSound() so that
all sounds are silenced when Focus mode is active, not just the new
mail notification sound. This affects:
- New mail sound
- Send button click sound ('hit-send')
- Mail sent sound ('send')

This is more consistent with user expectations - when DND is enabled,
no sounds should play from the app.

* Patches from local testing

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 14:29:52 -06:00
Ben Gotow
5d6b98314b
Enable native Wayland support in Snap package (#2527)
Remove DISABLE_WAYLAND environment variable that was forcing XWayland
fallback. With Electron 39 (based on Chromium 142), native Wayland
support is now enabled by default and works reliably.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 13:48:49 -06:00
Ben Gotow
637d771d30
Fix encoding issue in 'Message Clipped - Show All' window (#2526)
When opening the full message in a new window via the 'Message Clipped -
Show All' link, Chinese and other non-ASCII characters would appear as
garbled text. This was because the HTML file was written without a charset
declaration, causing the browser to guess the encoding incorrectly.

The fix prepends a UTF-8 charset meta tag to the HTML content before
writing it to the temporary file, ensuring the browser correctly
interprets the character encoding.

Fixes: https://community.getmailspring.com/t/message-clipped-show-all-has-a-problem-with-encoding/9532

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 13:47:18 -06:00
Ben Gotow
5728388199
Add additional safeguards to attachment preview generation (#2523)
* Add security vulnerability assessment for quickpreview attachment chain

Document verified vulnerability chain in quick preview feature:
- XSS via unsanitized HTML from Snarkdown/Mammoth (innerHTML injection)
- Exposed privileged IPC (finishWithData/finishCapture) to renderer
- Path validation bypass via directory traversal (missing path.resolve)
- Arbitrary file write leading to persistent RCE

Includes remediation plan with prioritized fixes and testing strategy.

* Fix critical security vulnerability chain in quickpreview attachment handling

This commit addresses a critical vulnerability chain that could lead to
persistent RCE via a single malicious attachment preview.

Security fixes implemented:

1. Path Validation (CVE-like: directory traversal)
   - Use path.resolve() to normalize paths before validation
   - Add path.sep suffix to prevent prefix-matching attacks
   - Return resolved path to prevent double-resolution

2. HTML Sanitization (CVE-like: XSS)
   - Add DOMPurify library with SanitizeTransformer config
   - Sanitize Mammoth, Snarkdown, and XLSX output before innerHTML
   - Prevents script injection via malicious .md/.docx/.xlsx files

3. Opaque Tokens (defense in depth)
   - Replace direct file path exposure with cryptographic tokens
   - Token-to-path mapping stored only in main process
   - Tokens are single-use and deleted after consumption

4. Sender Validation (defense in depth)
   - Validate IPC sender URL matches quickpreview renderer
   - Reject requests from unauthorized senders

5. Content Security Policy (defense in depth)
   - Add CSP headers to preview and capture windows
   - Restrict script-src, object-src, frame-src
   - Allow external images for document preview functionality

Reported-by: Chanho Kim

* Fix memory leak in preview token system

Add cleanupPreviewToken() function and call it when preview times out
or fails. This prevents token accumulation in long-running instances.

On success, the IPC handler already deletes the token. On failure
(timeout, crash, etc.), we now explicitly clean up the unused token.

* Fix token storage in wrong process (renderer vs main)

The previewTokens Map was being stored in the renderer process (where
index.ts runs) but the IPC handlers check for tokens in the main process
(where quickpreview-ipc.ts is registered). This caused "Invalid or expired
preview token" errors for PDF and DOCX previews.

Fix: Generate and cleanup tokens via IPC handlers so they're stored in
the main process where the validation occurs.

Changes:
- Add quickpreview:generateToken and quickpreview:cleanupToken IPC handlers
- Update index.ts to call these via ipcRenderer.invoke instead of
  importing functions directly
- Make _generateNextCrossplatformPreview async to await token generation

* Remove unneeded markdown files

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 11:41:59 -06:00
Ben Gotow
b38281f318 Bump mailsync to 6778fe27 2026-01-01 23:12:04 -06:00
Ben Gotow
3205b0b784
Address npm audit issues (#2522)
* Address `npm audit` issues

* Remove “new” npm version in package.json, other unused build-time deps
2026-01-01 23:11:03 -06:00
Ben Gotow
d29831d3ca
Upgrade node-emoji to latest version (#2516)
* Upgrade node-emoji from v1.2.1 to v2.2.0

Breaking changes addressed:
- Changed import from default to namespace import (v2 uses ESM)
- Replaced `NodeEmoji.emoji` object access with `NodeEmoji.search('')`
  since the `emoji` property is no longer exposed in v2
- Added caching for getAllEmojiNames() to maintain performance
- Removed @types/node-emoji as v2 includes built-in TypeScript types

* Fix issue with emoji toolbar popover

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-01 22:58:53 -06:00
Ben Gotow
879f0b3a03
Upgrade uuid module from v3 to v13 (#2521)
- Update uuid from ^3.0.0 to ^13.0.0 in app/package.json
- Remove @types/uuid (types now built-in with v11+)
- Convert CommonJS require() to ESM imports in:
  - app/internal_packages/onboarding/lib/onboarding-constants.ts
  - app/src/flux/stores/draft-factory.ts

Breaking changes addressed:
- v7+: Removed default export, now uses named exports only
- v12+: Dropped CommonJS support (ESM only)
- v11+: Written in TypeScript with built-in types

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-01 22:49:51 -06:00
Ben Gotow
c4ee0aa86f
Upgrade ical.js and ical-expander to latest versions (#2520)
* Upgrade ical.js and ical-expander to latest versions

- Update ical.js from 1.3.0 to 2.2.1
- Update ical-expander from 2.0.0 to 3.2.0
- Fix TypeScript type imports for ical.js 2.x ES module structure
- Fix getFirstPropertyValue return type handling in event-header.tsx
- Fix occurrence item access pattern in calendar-data-source.ts

The ical.js 2.x release includes ES6 module support, improved TypeScript
definitions, and various bug fixes for recurrence handling and timezone
support.

* Fix issues

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-01 22:47:59 -06:00
Ben Gotow
2ce02e2b6b
Upgrade juice module from 7.0.0 to 11.0.3 (#2519)
* Upgrade juice module from 7.0.0 to 11.0.3

Upgrade the CSS inlining library used for email composition.
No code changes required as the basic juice(html) API remains
compatible across these versions.

* Update package-lock.json for juice upgrade

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-01 22:38:08 -06:00
Ben Gotow
f1e3b0ba27
Remove node-fetch dependency in favor of native fetch (#2518)
Node.js 18+ includes native fetch, and Electron 39 uses Node.js 22+.
The node-fetch package is no longer needed.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-01 22:35:32 -06:00
Ben Gotow
24e639ce3c
Upgrade lru-cache from v4.0.1 to v10.4.3 (#2517)
Update to modern lru-cache API for improved performance:
- Use named import { LRUCache } instead of default import
- Rename del() to delete() method
- Rename reset() to clear() method

v10.4.3 is the latest version compatible with Node ^16.17.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-01 22:30:07 -06:00
Ben Gotow
6de11a74e0
Upgrade snarkdown from 1.2.2 to 2.0.0 (#2515)
Update the snarkdown Markdown parser dependency to latest version.
v2.0.0 includes bug fixes for link parsing, fenced code blocks,
strikethrough support, TypeScript typings, and code block HTML structure.

The API remains compatible - CommonJS require() returns the function
directly, so no code changes were needed.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-01 22:24:56 -06:00
Ben Gotow
c0e5d6b0e0
Update app dependencies (Phase 1 & 2 security and minor updates) (#2513)
Security fixes:
- mammoth: 1.4.19 → 1.11.0 (fixes directory traversal vulnerability CVSS 9.3)
- npm audit fix: fixes tar-fs symlink bypass and electron ASAR integrity

Safe minor/patch updates:
- dompurify: 3.0.8 → 3.3.1
- cheerio: 1.0.0-rc.6 → 1.1.2 (now stable)
- chrono-node: 2.7.6 → 2.9.0
- moment: 2.24.0 → 2.30.1
- moment-timezone: 0.5.32 → 0.6.0
- graceful-fs: 4.1.11 → 4.2.11
- underscore: 1.13.1 → 1.13.7
- mousetrap: 1.5.3 → 1.6.5
- react-color: 2.17.0 → 2.19.3
- source-map-support: 0.3.2 → 0.5.21
- rtlcss: 4.1.1 → 4.3.0
- classnames: 1.2.1 → 2.5.1
- collapse-whitespace: 1.1.6 → 1.1.7
- enzyme: 3.8.0 → 3.11.0
- enzyme-adapter-react-16: 1.9.0 → 1.15.8

Also adds plans/dependency-upgrade-plan.md documenting remaining
upgrade phases (3 & 4) for future reference.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-01 22:15:14 -06:00
Ben Gotow
5e6847ae4e
Electron v37 to v39 migration (#2514)
* Add Electron upgrade assessment for v37 to v39 migration

Comprehensive analysis of breaking changes, deprecated APIs, and required
code changes for upgrading from Electron 37.2.2 to the latest v39.x.

Key findings:
- macOS 11 support dropped in Electron 38 (min is now macOS 12)
- new-window event deprecated, needs migration to setWindowOpenHandler
- did-get-response-details event removed, needs alternative approach
- @electron/remote still compatible but represents technical debt

* Upgrade better-sqlite3 from 11.7 to 12.5

* Upgrade dependencies, re-test on macOS

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-01 22:03:05 -06:00
Vuk Marinkovic
d543ec25ac
Fix #1960: Bug: Mailspring was unable to create or delete the LaunchAgent file (#2509) 2025-12-29 20:22:30 -06:00
Ben Gotow
e46acc763a
Add comprehensive CLAUDE.md with architecture and commands (#2512) 2025-12-25 13:21:20 -10:00