Skip to content

Preserve referenced SVG definitions when pasting across documents - #1104

Merged
jfhenon merged 1 commit into
SVG-Edit:masterfrom
veselin-kutsarov:dev
Aug 5, 2026
Merged

Preserve referenced SVG definitions when pasting across documents#1104
jfhenon merged 1 commit into
SVG-Edit:masterfrom
veselin-kutsarov:dev

Conversation

@veselin-kutsarov

@veselin-kutsarov veselin-kutsarov commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

PR description

Summary

Cross-document paste previously copied only the selected SVG elements. References to external definitions such as symbol, gradient, filter, mask, clipPath, and pattern could therefore become unresolved in the target document.

This PR introduces a versioned clipboard payload that includes the recursively referenced definitions required by the copied elements.

Behavior

  • Preserves the existing same-document paste behavior.
  • Maintains compatibility with the legacy clipboard array format.
  • Recursively collects required definitions from defs.
  • Imports missing dependencies into the target document.
  • Reuses definitions with matching IDs and equivalent content.
  • Automatically renames conflicting visual dependencies and updates references in the pasted content.
  • Prompts the user when a directly referenced use target has the same ID but different content:
    • Use existing
    • Replace existing
    • Keep both
  • Records dependency imports, replacements, and pasted elements as one undoable operation.
  • Refreshes use references correctly during paste, undo, and redo.

Tests

  • Added unit coverage for recursive dependency collection and conflict handling.
  • Added coverage for legacy and same-document clipboard behavior.
  • Added undo/redo tests for replaced definitions.
  • Added a Playwright cross-tab test using a use element whose symbol references a gradient.

Verification completed:

  • 617 unit tests passed
  • Cross-tab Playwright clipboard tests passed
  • JavaScript Standard Style checks passed
  • Production build passed

Checklist

Note that we require UI tests to ensure that the added feature will not be
nixed by some future fix and that there is at least some test-as-documentation
to indicate how the fix or enhancement is expected to behave.

  • [x ] - Added Playwright UI tests
  • [ X] - Ran npm test, ensuring linting passes and that Cypress UI tests keep
    coverage to at least the same percent (reflected in the coverage badge
    that should be updated after the tests run)
  • - Added any user documentation. Though not required, this can be a big
    help both for future users and for the PR reviewer.

Summary by Sourcery

Add versioned SVG clipboard payloads that include required defs and support cross-document paste with dependency resolution and conflict handling.

New Features:

  • Introduce a structured, versioned clipboard format that captures selected elements, their external SVG definitions, and source document identity.
  • Support cross-document paste that imports the minimal set of referenced defs and prompts users to resolve conflicting direct use targets.

Bug Fixes:

  • Ensure pasted elements retain valid references to defs by importing or reusing their referenced symbols, gradients, and other resources across documents.
  • Prevent invalid or duplicate IDs during paste by remapping element IDs and internal references and handling conflicts consistently.

Enhancements:

  • Refactor paste logic into modular helpers for ID management, dependency indexing, equivalence checks, and paste finalization.
  • Extend undo/redo handling to refresh use references when batch commands replace defs, ensuring consistent rendering after history operations.

Documentation:

  • Add localized UI strings explaining how clipboard definition conflicts are resolved and the available user choices.

Tests:

  • Add unit tests for recursive dependency collection, equivalence-based reuse, conflict policies, and legacy clipboard compatibility.
  • Add end-to-end Playwright tests to verify referenced defs are copied and resolved correctly across browser tabs.

@sourcery-ai

sourcery-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements a versioned, cross-document-aware SVG clipboard format that collects and pastes recursively referenced defs while preserving existing in-document behavior and extension hooks.

Sequence diagram for cross-document SVG paste with defs and conflict resolution

sequenceDiagram
  actor User
  participant Editor
  participant SvgCanvas
  participant pasteElementsMethod
  participant clipboard_js as clipboard_js

  User->>Editor: trigger paste
  Editor->>SvgCanvas: pasteElements(type, x, y)
  SvgCanvas->>pasteElementsMethod: pasteElementsMethod(type, x, y)
  pasteElementsMethod->>sessionStorage: getItem(CLIPBOARD_ID)
  pasteElementsMethod->>clipboard_js: normalizeClipboardData(parsed)
  clipboard_js-->>pasteElementsMethod: clipboard
  pasteElementsMethod->>clipboard_js: getClipboardDocumentId(svgContent)
  clipboard_js-->>pasteElementsMethod: sourceDocumentId
  pasteElementsMethod->>pasteElementsMethod: [decide same vs cross document]
  alt cross-document with dependencies
    pasteElementsMethod->>pasteElementsMethod: pasteAcrossDocuments(clipboard, type, x, y)
    pasteElementsMethod->>clipboard_js: indexDependencies(clipboard.dependencies)
    pasteElementsMethod->>clipboard_js: getTargetElementsById()
    pasteElementsMethod->>clipboard_js: createEquivalenceChecker(...)
    pasteElementsMethod->>clipboard_js: collectReachableRoots(...)
    pasteElementsMethod->>clipboard_js: resolveUseConflicts(conflicts)
    clipboard_js->>SvgCanvas: call('resolveClipboardConflicts', { conflicts })
    SvgCanvas->>Editor: resolveClipboardConflicts
    Editor->>Editor: seConfirm(message, options)
    Editor-->>SvgCanvas: 'use-existing'|'replace-existing'|'keep-both'|'cancel'
    SvgCanvas-->>clipboard_js: policy
    clipboard_js-->>pasteElementsMethod: policy
    opt policy !== 'cancel'
      pasteElementsMethod->>clipboard_js: remapJsonReferences(...)
      pasteElementsMethod->>clipboard_js: remapJsonElementIds(...)
      pasteElementsMethod->>SvgCanvas: addSVGElementsFromJson(dependency)
      SvgCanvas-->>pasteElementsMethod: insertedDependencies
      pasteElementsMethod->>SvgCanvas: addSVGElementsFromJson(element)
      SvgCanvas-->>pasteElementsMethod: pasted
      pasteElementsMethod->>SvgCanvas: setUseData(svgContent)
      pasteElementsMethod->>SvgCanvas: addCommandToHistory(BatchCommand)
      pasteElementsMethod->>SvgCanvas: call('changed', pasted)
    end
  else same-document or legacy clipboard
    pasteElementsMethod->>pasteElementsMethod: pasteInSameDocument(elements,...)
  end
Loading

File-Level Changes

Change Details Files
Refactor paste pipeline to support async, document-aware pasting while preserving legacy same-document behavior.
  • Split paste logic into helpers for ID generation, extension notifications, visible element insertion, and final positioning/history handling.
  • Introduced pasteInSameDocument to preserve legacy ID remapping and extension behavior.
  • Added pasteAcrossDocuments to import defs, resolve conflicts, and paste both dependencies and visible elements.
  • Changed pasteElementsMethod to async, normalize clipboard payloads, and dispatch to same-document vs cross-document paths.
  • Updated type definition of pasteElements to return a Promise and ensured undo uses BatchCommand.refreshUseData for certain operations.
packages/svgcanvas/core/paste-elem.js
packages/svgcanvas/svgcanvas.d.ts
Introduce a structured clipboard subsystem for versioned payloads and dependency tracking.
  • Added clipboard.js with helpers for document IDs, payload normalization, JSON walking, ID/reference remapping, and structural equivalence checks.
  • Implemented createClipboardPayload to serialize selected elements plus recursive defs closure and track direct use targets.
  • Updated copySelectedElements to write a versioned clipboard payload instead of a raw array.
  • Adjusted hasClipboardData to honor both legacy and versioned clipboard formats via hasClipboardElements.
  • Reset per-document clipboard identity when clearing the SVG content to distinguish documents.
packages/svgcanvas/core/clipboard.js
packages/svgcanvas/core/selected-elem.js
packages/svgcanvas/svgcanvas.js
Add conflict-resolution UX and wiring for direct use-target ID collisions.
  • Implemented Editor.resolveClipboardConflicts which prompts the user on conflicting defs IDs with translated labels and returns a normalized policy string.
  • Bound resolveClipboardConflicts into the SvgCanvas event system so core paste logic can call into the host editor.
  • Defined localized strings for clipboard defs conflict messages and options.
  • Hooked pasteAcrossDocuments to call resolveClipboardConflicts and apply a consistent policy (use existing, replace, keep both, or cancel) to all conflicts.
src/editor/Editor.js
src/editor/EditorStartup.js
src/editor/locale/lang.en.js
packages/svgcanvas/core/paste-elem.js
Extend undo/redo and use-data refresh behavior for defs replacements during paste.
  • Allow BatchCommand to carry a refreshUseData flag when defs replacements occur.
  • Teach undo manager to refresh use-data for batch commands with this flag after apply/unapply.
  • Ensure pasteAcrossDocuments marks batch commands that replace existing defs so use references remain consistent across undo/redo.
packages/svgcanvas/core/undo.js
packages/svgcanvas/core/paste-elem.js
Add unit and end-to-end tests for versioned clipboard, dependency collection, conflict handling, and legacy compatibility.
  • Expanded paste-elem tests to cover same-document behavior, recursive dependency import, equivalence-based reuse, renaming of conflicting defs, conflict policies, and legacy clipboard arrays.
  • Added selected-elem tests for versioned clipboard structure and recursive dependency collection (including useTargetIds).
  • Introduced a Playwright cross-tab test that copies a use whose symbol references a gradient and verifies defs are imported exactly once.
  • Updated coverage summary metadata to reflect the new tests.
tests/unit/paste-elem.test.js
tests/unit/selected-elem.test.js
tests/e2e/clipboard.spec.js
coverage/coverage-summary.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@jfhenon
jfhenon merged commit c44f061 into SVG-Edit:master Aug 5, 2026
8 checks passed
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