Skip to content

feat: clipboard export, persisted bearing, and poster presets#127

Open
ehabmansour1 wants to merge 3 commits into
yousifamanuel:devfrom
ehabmansour1:feat/clipboard-bearing-presets
Open

feat: clipboard export, persisted bearing, and poster presets#127
ehabmansour1 wants to merge 3 commits into
yousifamanuel:devfrom
ehabmansour1:feat/clipboard-bearing-presets

Conversation

@ehabmansour1

@ehabmansour1 ehabmansour1 commented Apr 6, 2026

Copy link
Copy Markdown

Summary

Three additive features that improve the poster authoring loop:

  • Copy poster to clipboard — new "Copy" button (teal) in both desktop flyout and mobile export modal. Uses navigator.clipboard.write with ClipboardItem to put a PNG of the composited poster on the system clipboard.
  • Persisted map bearingbearing is now a PosterForm field, so map rotation survives across interactions, syncs through MapPreview alongside center/zoom (with a new MAP_BEARING_SYNC_EPSILON), and is captured by presets. Local mapBearing state in PreviewPanel was removed.
  • Poster presets — new vertical slice under src/features/presets/ (domain / infrastructure / application / ui). Saves up to 20 named configurations (PosterForm + customColors) to localStorage under a stable key (terraink:presets), no TTL. Surfaces as a new "Presets" accordion section in SettingsPanel.

Each feature is in its own commit so they can be reviewed (or reverted) independently.

Architecture notes

  • Clipboard exporter follows the existing infrastructure pattern: new file in features/export/infrastructure/, re-exported from features/export/infrastructure/index.ts and core/services.ts. Hook handles dispatch of the new "clipboard" ExportFormat branch inside useExport.exportPoster.
  • Bearing change is the cleanest way to make rotation work with presets — without it, loading a preset would silently lose the user's chosen orientation. Export already reads bearing from the live map instance, so no changes were needed in mapExporter.ts / exportUtils.ts.
  • Presets storage deliberately avoids localStorageCache because that adapter enforces a TTL and namespaces by APP_VERSION (presets would be wiped on every release). Uses a plain stable key instead, mirroring how custom marker icons use IndexedDB for durable user data.
  • Loading a preset dispatches SET_THEME (resets customColors) → SET_FORM_FIELDS → per-color SET_COLOR, reusing existing reducer actions instead of adding a new LOAD_PRESET action.

Test plan

  • Click the new Copy button (desktop flyout / mobile modal) → poster lands on clipboard, can be pasted into an image editor
  • Verify copy works while flyout stays open during the in-flight export
  • Rotate the map, lock it, unlock again → bearing is preserved (previously reset to 0)
  • Rotate, then recenter → bearing resets to 0 (existing recenter behavior preserved)
  • Save a preset with a custom name, change theme/location/bearing/colors, click the preset → all fields restored including bearing and custom colors
  • Save more than 20 presets → oldest entries are evicted (cap = 20)
  • Delete a preset → removed from list and localStorage
  • Reload the page → presets persist
  • bun run build passes (verified locally)

Notes

  • Three pre-existing TypeScript errors in pngExporter.ts, StartupLocationModal.tsx, and typography.ts are unrelated to this PR and untouched.
  • Firefox does not yet support image writes via the Clipboard API; the exporter throws a clear error message in that case.

ehabmansour1 and others added 3 commits April 7, 2026 00:59
Use the Clipboard API to copy the composited poster as a PNG
image directly to the system clipboard. A new teal "Copy" button
appears in both the desktop flyout and the mobile export modal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move map bearing from local PreviewPanel state into PosterForm
so it survives across interactions, is included in presets, and
syncs via MapPreview alongside center and zoom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a Presets accordion section that stores up to 20 named poster
configurations (form + custom colors) in localStorage. Users can
save the current state, load a preset to restore it, or delete
presets they no longer need.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@yousifamanuel

yousifamanuel commented Apr 9, 2026

Copy link
Copy Markdown
Owner

@ehabmansour1 thank you for your contribution again!

Before proceeding please consider this:

Updated Contributing Guidelines & New CLA

The project license was recently updated to AGPL-3.0 with dual-licensing rights, but the Contributing Guidelines were not updated at the same time, and the project did not yet have a standalone Contributor License Agreement. Your PR was opened during that gap, so this step is necessary before I can proceed with review.

Both documents are now in place. Please take a moment to review them:

  • CONTRIBUTING.md — updated since your PR was opened
  • CLA.md — new, standalone Contributor License Agreement

I need your confirmation before I can proceed with reviewing your code. Please reply to this comment with the following:

If you have any questions, feel free to ask here. Thank you for understanding!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants