Skip to content

Copy and paste items between strategies on the same map - #401

Merged
SunkenInTime merged 2 commits into
mainfrom
t3/canvas-copy-paste
Oct 28, 2026
Merged

SunkenInTime merged 2 commits into
mainfrom
t3/canvas-copy-paste

Conversation

@SunkenInTime

@SunkenInTime SunkenInTime commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Ctrl+A, Ctrl+C in one strategy, Ctrl+V in another on the same map; a paste on Bind is refused

Windows release build: Ctrl+A and Ctrl+C in "Source" (agents, a smoke, text, a stroke, an image and a lineup with a screenshot); Ctrl+V in "Playbook" puts it all down where it was; Ctrl+V in "Bind plan" is refused with a toast.

This is the last PR in the selection stack, on #400; the three merge together. It is the request itself: a player wanted to bring replay-captured pages into the playbooks they already have, and later move single items like one lineup between strategies.

What changes

  • Copying: Ctrl+C copies the selection and Ctrl+X cuts it.
  • Pasting: Ctrl+V pastes it where it was, as one undo step, into any strategy on the same map, and the pasted items stay selected so they can be dragged right away. A paste on another map is refused: "What you copied is from Ascent. Paste it into a strategy on Ascent." Position is what a tactical item means, so pasting at the cursor would put a smoke somewhere else in the game.
  • The clipboard lives in the app, never on the system clipboard. Nothing copied can end up in a Discord message, so no future version ever has to read it back.
  • Copying a page is Ctrl+A, Ctrl+C, add a page in the other strategy, Ctrl+V. A pages-bar page copy (carrying the page name and side) waits until people say this isn't enough (your call).

Pictures

Copy reads every picture's bytes when it's made, so the source doesn't need to stay open. A picture can come from a local file, from bytes still uploading, or as a cloud download.

  • Into the strategy it came from: an image shows the same picture while that picture is still there, as copies already do (assetId). If its last use was cut and the file cleaned up, or the cloud copy swept, it's put back from the copied bytes.

  • Into another strategy, local or cloud: each picture is saved anew and, on cloud, uploaded the way a dropped image is:

    • save the bytes;
    • stage the upload (enqueuePlacedImageUpload, enqueueLineupMediaJobs);
    • add the items in one transaction;
    • commitStagedMediaReferences.

    That path is durable, survives restarts and works offline, and a failed upload already shows in the sync menu. There is no new server code.

  • Picture ids: the first pasted image of a picture takes the picture's id and later ones point at it. That's the original-and-copies shape #276 set up, so builds that drop assetId still show the right picture.

  • Refusals: a paste refuses rather than put down items whose pictures it can't save:

    • a picture couldn't be read when copied;
    • a picture is over the 15 MB cloud limit;
    • the page changed while pictures were saved.

Ids

Pasted items get copy ids (<root>~cp1~<uuid>), so a page transition still pairs a pasted agent with the one it came from. Pages pasted one after another (consecutive replay captures, say) keep their agents gliding. An item imported with a very long id gets a plain uuid instead, under the same rule offline page copy already used. That rule moved to newStorableCopyId so both share it.

Ground truth

  • Windows release build, local (driven in-app; real keys were proven in #399):
    • Source to Playbook: all 7 items landed.
    • The image and the lineup screenshot got new picture files in Playbook's own folder.
    • Everything was still there after leaving and reopening.
    • The Bind paste was refused.
  • Against the dev Convex deployment, with a test account:
    • Cloud to cloud: 5 items reached the server, and the picture was re-uploaded and became active.
    • Local to cloud: items, the lineup and both pictures reached the server and uploaded as active.
    • Sync status settled to "synced" with nothing queued, and the playbook read the same after leaving and reopening.

Cloud playbook after pasting from a cloud and a local strategy, reopened

  • Found on the real build: a pasted freehand stroke threw, and the paste silently did nothing. The drawing's saved form holds its bounding box as an object, so the copy now goes through JSON text. A paste that fails now says so on screen.
  • Review: Astra's second round found five problems, all fixed with tests where one can catch them:
    • Cut deleted whatever was selected once pictures finished reading, not what was cut.
    • A same-strategy paste could point at a cleaned-up picture.
    • Long imported ids made unsavable copies.
    • A slow paste could land on another page.
    • Touch and pen drags were blocked by a multi-selection (fixed in #400).

What you copied is from Ascent

Not covered

  • The web build wasn't run. There, pictures live as pending bytes rather than files; saveSecureImage handles that, but I didn't see it.
  • Paste while offline into a cloud strategy wasn't run; it rides the same durable queue as an offline image drop.
  • An account switch clears the clipboard. That was read in code, not run.

ci: analyze test web

@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5ff85a32-752d-4065-8022-8b28872e3342
Commits

Reviewing files that changed from the base of the PR and between 3425462 and 3545276.

Files selected for processing: 10
  • lib/collab/page_made_here.dart
  • lib/const/line_provider.dart
  • lib/const/page_copy_id.dart
  • lib/providers/canvas_clipboard_provider.dart
  • lib/providers/canvas_selection_provider.dart
  • lib/providers/drawing_provider.dart
  • lib/providers/image_provider.dart
  • lib/providers/strategy_image_source.dart
  • lib/widgets/canvas_selection_layer.dart
  • test/canvas_clipboard_test.dart
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Ctrl+C copies the selection, Ctrl+X cuts it, and Ctrl+V pastes it where it
was, as one undo step, into any strategy on the same map (a paste on another
map says so). The clipboard lives in the app, never on the system clipboard.

Pasted into the strategy it came from, an image shows the same picture.
Pasted into another strategy, local or cloud, each picture is saved there
anew and uploads as a dropped image does. Copy reads the pictures' bytes
when it is made, so the source need not be open to paste. Pasted items keep
their copy root, so pasted pages still animate between each other.
Cutting an image and pasting it in the same cloud strategy refused with
"hadn't loaded" when the copy could not download the picture (offline, or a
browser origin the storage does not allow): the paste looked for the picture
on the page on screen, where a cut one is no longer listed. In the cloud the
picture is still there (a cut image's tombstone keeps it for 30 days), so the
strategy it came from always keeps its picture; only a local strategy, which
cleans up unused files when it reopens, checks for the file. The refusal now
says the picture could not be read, not that it hadn't loaded.
@SunkenInTime
SunkenInTime changed the base branch from t3/canvas-group-move to main October 28, 2026 21:03
@SunkenInTime
SunkenInTime merged commit 0bfaa6c into main Oct 28, 2026
14 of 26 checks passed
@SunkenInTime
SunkenInTime deleted the t3/canvas-copy-paste branch October 28, 2026 21:03
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.

1 participant