Skip to content

issue/3857/feat/copying-distributions#4538

Open
lsabor wants to merge 3 commits into
mainfrom
issue/3857/feat/copying-distributions
Open

issue/3857/feat/copying-distributions#4538
lsabor wants to merge 3 commits into
mainfrom
issue/3857/feat/copying-distributions

Conversation

@lsabor

@lsabor lsabor commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

closes #3857

implements copy and paste from arbitrary distributions
support ctrl+c and ctrl+v

Summary by CodeRabbit

Summary of changes

  • New Features

    • Added continuous forecast “copy/paste distribution” using the browser clipboard, including validation and clear success/failure notifications.
    • Introduced Ctrl/Cmd+C and Ctrl/Cmd+V shortcuts with appropriate focus/disabled-state handling.
    • Pasting updates the relevant distribution data, switches the input mode, marks the form as edited, and suppresses unintended mode-sync behavior.
  • Localization

    • Added new clipboard action labels and notification messages for Czech, English, Spanish, Portuguese, Simplified Chinese, and Traditional Chinese.

closes #3859
implements copy and paste from arbitrary distributions
support ctrl+c and ctrl+v
@coderabbitai

coderabbitai Bot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds localized distribution clipboard messages and implements copy/paste support for continuous forecast distributions, including validation, keyboard shortcuts, toast feedback, and parent-state integration.

Changes

Distribution clipboard flow

Layer / File(s) Summary
Clipboard menu and validation
front_end/src/components/forecast_maker/continuous_input/continuous_clipboard_menu.tsx
Adds clipboard serialization, JSON validation, copy/paste buttons, keyboard shortcuts, and success or failure toasts.
Input wiring and state updates
front_end/src/components/forecast_maker/continuous_input/..., front_end/src/components/forecast_maker/forecast_maker_*/...
Passes clipboard data through continuous input components and applies pasted slider or quantile distributions to parent state.
Localized clipboard messages
front_end/messages/{cs,en,es,pt,zh,zh-TW}.json
Adds six translated keys for copy/paste actions and success, failure, or invalid-input notifications.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Menu as ContinuousClipboardMenu
    participant Clipboard as Browser Clipboard
    participant State as Forecast State

    User->>Menu: Copy distribution or press Ctrl/Cmd+C
    Menu->>Clipboard: Write serialized distribution
    Clipboard-->>Menu: Success or error
    Menu->>User: Show copy result toast

    User->>Menu: Paste distribution or press Ctrl/Cmd+V
    Menu->>Clipboard: Read clipboard text
    Clipboard-->>Menu: Return clipboard text
    Menu->>Menu: Parse and validate distribution
    Menu->>State: onPaste(type, components)
    State->>State: Update components and input mode
    Menu->>User: Show paste result toast
Loading

Suggested reviewers: cemreinanc, elisescu, ncarazon

Poem

🐰 Copy hops out, paste hops in,
Distributions dance with a grin.
Six tongues cheer, “Success is near!”
Ctrl+C and Ctrl+V bring forecasts here! ✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clearly related to the main feature, though it reads like a branch name rather than a sentence.
Linked Issues check ✅ Passed The PR adds copy/paste buttons, clipboard handling, graceful invalid-data failure, and Ctrl/Cmd shortcuts for continuous distributions, matching #3857.
Out of Scope Changes check ✅ Passed The changes stay focused on distribution copy/paste behavior and the needed localization strings, with no obvious unrelated additions.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue/3857/feat/copying-distributions

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@front_end/messages/zh-TW.json`:
- Around line 2081-2086: Update the translation values for the keys
"distributionCopied", "distributionCopyFailed", "distributionPasted",
"distributionPasteInvalid", "copyDistribution", and "pasteDistribution" to use
「分佈」 instead of 「分配」 so the terminology matches the forecasting context; locate
these string values in front_end/messages/zh-TW.json (the keys listed above) and
replace each occurrence of 「分配」 with 「分佈」 while preserving the rest of the
messages.

In
`@front_end/src/components/forecast_maker/continuous_input/continuous_clipboard_menu.tsx`:
- Around line 24-36: The isClipboardDistribution type guard currently checks
marker and type but must also validate that (data as
ClipboardDistribution).components is an array and that each component has the
required shape: ensure components is Array.isArray(...) and for each element
verify a discriminant (e.g., component.type or similar) and then for slider
components confirm presence and correct types for weight, left, center, right,
and for quantile components confirm presence and correct type for quantile; keep
referencing CLIPBOARD_MARKER, VALID_INPUT_TYPES and the ClipboardDistribution
shape and update isClipboardDistribution to return false for any malformed
component to prevent downstream runtime errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2628ab1d-d8ec-4edf-b54a-c454fc13fba0

📥 Commits

Reviewing files that changed from the base of the PR and between f1c7c45 and 52662cd.

📒 Files selected for processing (11)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
  • front_end/src/components/forecast_maker/continuous_input/continuous_clipboard_menu.tsx
  • front_end/src/components/forecast_maker/continuous_input/continuous_input_container.tsx
  • front_end/src/components/forecast_maker/continuous_input/index.tsx
  • front_end/src/components/forecast_maker/forecast_maker_group/continuous_input_wrapper.tsx
  • front_end/src/components/forecast_maker/forecast_maker_question/forecast_maker_continuous.tsx

Comment on lines +2081 to +2086
"distributionCopied": "分配已複製到剪貼簿",
"distributionCopyFailed": "複製分配失敗",
"distributionPasted": "從剪貼簿貼上了分配",
"distributionPasteInvalid": "剪貼簿中未找到有效的分配",
"copyDistribution": "將分配複製到剪貼簿",
"pasteDistribution": "從剪貼簿貼上分配",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use 「分佈」 instead of 「分配」 for consistency and correctness.

In this locale file, distribution in forecasting context is already translated as 「分佈」. Using 「分配」 here introduces inconsistent terminology and reads as “allocation.”

Suggested wording update
-  "distributionCopied": "分配已複製到剪貼簿",
-  "distributionCopyFailed": "複製分配失敗",
-  "distributionPasted": "從剪貼簿貼上了分配",
-  "distributionPasteInvalid": "剪貼簿中未找到有效的分配",
-  "copyDistribution": "將分配複製到剪貼簿",
-  "pasteDistribution": "從剪貼簿貼上分配",
+  "distributionCopied": "分佈已複製到剪貼簿",
+  "distributionCopyFailed": "複製分佈失敗",
+  "distributionPasted": "已從剪貼簿貼上分佈",
+  "distributionPasteInvalid": "剪貼簿中未找到有效的分佈",
+  "copyDistribution": "將分佈複製到剪貼簿",
+  "pasteDistribution": "從剪貼簿貼上分佈",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"distributionCopied": "分配已複製到剪貼簿",
"distributionCopyFailed": "複製分配失敗",
"distributionPasted": "從剪貼簿貼上了分配",
"distributionPasteInvalid": "剪貼簿中未找到有效的分配",
"copyDistribution": "將分配複製到剪貼簿",
"pasteDistribution": "從剪貼簿貼上分配",
"distributionCopied": "分佈已複製到剪貼簿",
"distributionCopyFailed": "複製分佈失敗",
"distributionPasted": "已從剪貼簿貼上分佈",
"distributionPasteInvalid": "剪貼簿中未找到有效的分佈",
"copyDistribution": "將分佈複製到剪貼簿",
"pasteDistribution": "從剪貼簿貼上分佈",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/messages/zh-TW.json` around lines 2081 - 2086, Update the
translation values for the keys "distributionCopied", "distributionCopyFailed",
"distributionPasted", "distributionPasteInvalid", "copyDistribution", and
"pasteDistribution" to use 「分佈」 instead of 「分配」 so the terminology matches the
forecasting context; locate these string values in front_end/messages/zh-TW.json
(the keys listed above) and replace each occurrence of 「分配」 with 「分佈」 while
preserving the rest of the messages.

@github-actions

github-actions Bot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview Environment

Your preview environment is ready!

Resource Details
🌐 Preview URL https://metaculus-pr-4538-issue-3857-feat-copying-distri-preview.mtcl.cc
📦 Docker Image ghcr.io/metaculus/metaculus:issue-3857-feat-copying-distributions-b143f4d
🗄️ PostgreSQL NeonDB branch preview/pr-4538-issue-3857-feat-copying-distri
Redis Fly Redis mtc-redis-pr-4538-issue-3857-feat-copying-distri

Details

  • Commit: 610133430c2bd5552846cd31a06ad9b9e6f74e68
  • Branch: issue/3857/feat/copying-distributions
  • Fly App: metaculus-pr-4538-issue-3857-feat-copying-distri

ℹ️ Preview Environment Info

Isolation:

  • PostgreSQL and Redis are fully isolated from production
  • Each PR gets its own database branch and Redis instance
  • Changes pushed to this PR will trigger a new deployment

Limitations:

  • Background workers and cron jobs are not deployed in preview environments
  • If you need to test background jobs, use Heroku staging environments

Cleanup:

  • This preview will be automatically destroyed when the PR is closed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@front_end/messages/zh-TW.json`:
- Line 1953: Update the tournamentTimelineClosed translation in the zh-TW locale
to use Traditional Chinese characters, changing the value from “待处理的决议” to
“待處理的決議” while leaving the key and surrounding translations unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c16e339-edb4-4bc5-80d5-cdafa2961d1b

📥 Commits

Reviewing files that changed from the base of the PR and between 52662cd and 5e16b37.

📒 Files selected for processing (6)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/cs.json
  • front_end/messages/zh.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@front_end/messages/zh-TW.json`:
- Line 1953: Update the tournamentTimelineClosed translation in the zh-TW locale
to use Traditional Chinese characters, changing the value from “待处理的决议” to
“待處理的決議” while leaving the key and surrounding translations unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c16e339-edb4-4bc5-80d5-cdafa2961d1b

📥 Commits

Reviewing files that changed from the base of the PR and between 52662cd and 5e16b37.

📒 Files selected for processing (6)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/cs.json
  • front_end/messages/zh.json
🛑 Comments failed to post (1)
front_end/messages/zh-TW.json (1)

1953-1953: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use Traditional Chinese characters.

The string "待处理的决议" uses Simplified Chinese characters (处理, 决议). Since this is the Traditional Chinese (zh-TW) locale file, it should use Traditional characters (待處理的決議).

📝 Proposed fix
-  "tournamentTimelineClosed": "待处理的决议",
+  "tournamentTimelineClosed": "待處理的決議",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  "tournamentTimelineClosed": "待處理的決議",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@front_end/messages/zh-TW.json` at line 1953, Update the
tournamentTimelineClosed translation in the zh-TW locale to use Traditional
Chinese characters, changing the value from “待处理的决议” to “待處理的決議” while leaving
the key and surrounding translations unchanged.

- Skip the Ctrl+C/Ctrl+V shortcut handling when focus is on a form field
  (e.g. a percentile input), so native copy/paste isn't hijacked there.
- Skip the slider<->table mode-sync effect when a mode change comes from
  a paste, since the pasted components are already correct and shouldn't
  be overwritten by deriving them from the other (unrelated) input mode.
- Store copied quantile values in a scale-independent internal [0,1]
  location (like slider components already do) instead of the source
  question's real-world units, and rescale on paste so distributions
  transfer correctly between questions with different ranges.
- Reconcile pasted quantile values with the target question's own
  open/closed bounds: clamp bound-probability values to 0 on a closed
  bound (matching validation, since there's no UI to fix it otherwise)
  or to [0.1, 99.9] on an open bound, and nudge q1/q2/q3 strictly inside
  a closed bound.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
front_end/src/components/forecast_maker/continuous_input/continuous_clipboard_menu.tsx (2)

208-217: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle uppercase keys for shortcuts.

When Caps Lock is enabled, e.key will be "C" or "V", causing the exact shortcut check to fail. Convert e.key to lowercase to ensure the shortcut works regardless of the user's Caps Lock state.

💡 Proposed fix
-      if (e.key === "c") {
+      const key = e.key.toLowerCase();
+      if (key === "c") {
         // Only intercept if no text is selected
         const selection = window.getSelection()?.toString();
         if (selection) return;
         e.preventDefault();
         handleCopy();
-      } else if (e.key === "v" && !disabled) {
+      } else if (key === "v" && !disabled) {
         e.preventDefault();
         handlePaste();
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@front_end/src/components/forecast_maker/continuous_input/continuous_clipboard_menu.tsx`
around lines 208 - 217, Update the keyboard shortcut checks in the continuous
clipboard menu handler to compare a lowercase-normalized e.key, ensuring copy
and paste work with Caps Lock enabled while preserving the existing selection
and disabled checks.

113-123: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Strengthen validation to include component structure checks.

The isClipboardDistribution type guard validates the marker and type but does not verify the structure of components. Malformed clipboard data could cause runtime errors downstream when:

  • components is not an array (.map() will fail)
  • Slider components are missing required fields (weight, left, center, right)
  • Quantile components are missing the quantile field
🛡️ Proposed fix to add component structure validation
+function isValidSliderComponent(c: unknown): c is DistributionSliderComponent {
+  return (
+    typeof c === "object" &&
+    c !== null &&
+    typeof (c as DistributionSliderComponent).weight === "number" &&
+    typeof (c as DistributionSliderComponent).left === "number" &&
+    typeof (c as DistributionSliderComponent).center === "number" &&
+    typeof (c as DistributionSliderComponent).right === "number"
+  );
+}
+
+function isValidQuantileComponent(c: unknown): c is DistributionQuantileComponent[number] {
+  return (
+    typeof c === "object" &&
+    c !== null &&
+    "quantile" in c
+  );
+}
+
 function isClipboardDistribution(data: unknown): data is ClipboardDistribution {
-  return (
+  if (!(
     typeof data === "object" &&
     data !== null &&
     CLIPBOARD_MARKER in data &&
     (data as ClipboardDistribution)[CLIPBOARD_MARKER] === true &&
     "type" in data &&
     "components" in data &&
     VALID_INPUT_TYPES.includes((data as ClipboardDistribution).type)
-  );
+  )) {
+    return false;
+  }
+
+  const { type, components } = data as ClipboardDistribution;
+  if (!Array.isArray(components) || components.length === 0) {
+    return false;
+  }
+
+  if (type === ContinuousForecastInputType.Slider) {
+    return components.every(isValidSliderComponent);
+  }
+  return components.every(isValidQuantileComponent);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@front_end/src/components/forecast_maker/continuous_input/continuous_clipboard_menu.tsx`
around lines 113 - 123, Strengthen the isClipboardDistribution type guard by
validating components is an array and checking each component according to its
type: slider components must contain weight, left, center, and right, while
quantile components must contain quantile. Preserve the existing marker and
valid input type checks, and reject malformed clipboard data before it reaches
downstream processing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@front_end/src/components/forecast_maker/continuous_input/continuous_clipboard_menu.tsx`:
- Around line 208-217: Update the keyboard shortcut checks in the continuous
clipboard menu handler to compare a lowercase-normalized e.key, ensuring copy
and paste work with Caps Lock enabled while preserving the existing selection
and disabled checks.
- Around line 113-123: Strengthen the isClipboardDistribution type guard by
validating components is an array and checking each component according to its
type: slider components must contain weight, left, center, and right, while
quantile components must contain quantile. Preserve the existing marker and
valid input type checks, and reject malformed clipboard data before it reaches
downstream processing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 892c1724-c6aa-42f1-8b52-e6f36d12a11c

📥 Commits

Reviewing files that changed from the base of the PR and between 5e16b37 and b143f4d.

📒 Files selected for processing (5)
  • front_end/src/components/forecast_maker/continuous_input/continuous_clipboard_menu.tsx
  • front_end/src/components/forecast_maker/continuous_input/continuous_input_container.tsx
  • front_end/src/components/forecast_maker/continuous_input/index.tsx
  • front_end/src/components/forecast_maker/forecast_maker_group/continuous_input_wrapper.tsx
  • front_end/src/components/forecast_maker/forecast_maker_question/forecast_maker_continuous.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • front_end/src/components/forecast_maker/forecast_maker_group/continuous_input_wrapper.tsx
  • front_end/src/components/forecast_maker/forecast_maker_question/forecast_maker_continuous.tsx
  • front_end/src/components/forecast_maker/continuous_input/continuous_input_container.tsx

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.

Add support for copying distributions across (non-group) continuous questions

1 participant