Skip to content

.2869047221311500:9c5759a5394aa1b51101e64d032d75de_69e614bbf51efcfb0ecaa3bf.69e62868f51efcfb0ecaa543.69e62867d90ca3ecca53d17c:Trae CN.T(2026/4/20 21:21:44)Trae 4#374

Open
Juebandoctor wants to merge 3 commits into
1j01:masterfrom
Juebandoctor:Trae_4

Conversation

@Juebandoctor
Copy link
Copy Markdown

No description provided.

实现参考图片功能,包括加载、显示/隐藏、调整透明度和移除参考图片
添加相关CSS样式和菜单选项
创建ReferenceImage类处理参考图片的显示和交互
修改ReferenceImage的显示/隐藏逻辑,使用CSS类.hidden替代直接调用jQuery的hide/show方法
Copilot AI review requested due to automatic review settings April 20, 2026 13:37
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a “Reference Image” overlay feature to the JS Paint canvas so users can load an image/PDF to trace over, toggle visibility, adjust opacity, and remove it.

Changes:

  • Introduces a new ReferenceImage on-canvas object with resize handles and a move handle.
  • Adds View menu entries to load/show/adjust opacity/remove the reference image.
  • Adds CSS styling for reference image layering and RTL support.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
styles/layout.css Adds .reference-image styling and stacking behavior.
styles/layout.rtl.css Mirrors .reference-image styling for RTL layout.
src/menus.js Adds “Reference Image” submenu under View and wires menu actions.
src/functions.js Implements reference image load/toggle/remove/opacity UI and exports.
src/app-state.js Adds global singleton reference_image state.
src/ReferenceImage.js New on-canvas object implementation for displaying/moving/resizing the reference image.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/menus.js
Comment on lines +610 to +662
MENU_DIVIDER,
{
emoji_icon: "📷",
label: localize("&Reference Image"),
submenu: [
{
label: localize("&Load Reference Image") + "...",
speech_recognition: [
"load reference image", "load reference",
"import reference image", "import reference",
"open reference image", "open reference",
],
action: () => { load_reference_image(); },
description: localize("Loads a reference image to trace over."),
},
{
label: localize("&Show Reference Image"),
speech_recognition: [
"toggle reference image", "toggle reference",
"show reference image", "show reference",
"hide reference image", "hide reference",
],
enabled: () => has_reference_image(),
checkbox: {
toggle: () => { toggle_reference_image(); },
check: () => is_reference_image_visible(),
},
description: localize("Shows or hides the reference image."),
},
{
label: localize("Reference Image &Opacity") + "...",
speech_recognition: [
"change reference image opacity", "change reference opacity",
"adjust reference image opacity", "adjust reference opacity",
"reference image transparency", "reference transparency",
],
enabled: () => has_reference_image(),
action: () => { show_reference_image_opacity_window(); },
description: localize("Adjusts the opacity of the reference image."),
},
{
label: localize("&Remove Reference Image"),
speech_recognition: [
"remove reference image", "remove reference",
"delete reference image", "delete reference",
"clear reference image", "clear reference",
],
enabled: () => has_reference_image(),
action: () => { remove_reference_image(); },
description: localize("Removes the reference image."),
},
],
},
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

New View > Reference Image menu actions (load/toggle/opacity/remove) aren’t covered by Cypress tests, while the repo already has menu interaction helpers in cypress/integration/visual-tests.spec.js. Add an e2e test that loads a reference image fixture, verifies it appears, toggles visibility, adjusts opacity, and removes it (and verifies menu items enable/disable based on has_reference_image()).

Copilot uses AI. Check for mistakes.
Comment thread styles/layout.css
Comment on lines +135 to +140
.reference-image {
z-index: 2.5;
display: block !important;
box-sizing: border-box;
-moz-box-sizing: border-box;
pointer-events: none;
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

z-index: 2.5 is not valid per the CSS spec (z-index takes an integer). Some browsers will ignore the declaration, which can break stacking of the reference image relative to .main-canvas (z-index: 2) and .selection/.textbox/.helper-layer (z-index: 3). Use an integer z-index and adjust stacking via DOM order or by shifting the surrounding z-index values (e.g., keep reference image at 2 and ensure it’s appended after .main-canvas, or move .main-canvas to 1 and reference image to 2).

Copilot uses AI. Check for mistakes.
Comment thread styles/layout.rtl.css
Comment on lines +135 to +141
.reference-image {
z-index: 2.5;
display: block !important;
box-sizing: border-box;
-moz-box-sizing: border-box;
pointer-events: none;
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

z-index: 2.5 is not valid per the CSS spec (z-index takes an integer). If the browser drops this rule, the reference image may end up behind the main canvas or above selections inconsistently. Use an integer z-index and adjust stacking using DOM order or integer z-index steps.

Copilot uses AI. Check for mistakes.
Comment thread src/functions.js
Comment on lines +1817 to +1819
async function load_reference_image() {
const { file } = await systemHooks.showOpenFileDialog({ formats: image_formats });
if (file.type.match(/^image|application\/pdf/)) {
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

systemHooks.showOpenFileDialog can reject/throw when the user cancels (Electron implementation explicitly throws; showOpenFilePicker also rejects). This function doesn’t handle that, so canceling can cause an unhandled rejection. Wrap the dialog call in a try/catch and return early on cancel.

Copilot uses AI. Check for mistakes.
Comment thread src/functions.js
Comment on lines +1818 to +1821
const { file } = await systemHooks.showOpenFileDialog({ formats: image_formats });
if (file.type.match(/^image|application\/pdf/)) {
read_image_file(file, (error, info) => {
if (error) {
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

Don’t gate reference image loading on file.type. In Electron, readBlobFromHandle constructs new File([...], fileName) without a MIME type, so file.type is an empty string and this will incorrectly show “not a valid bitmap” for valid images/PDFs. Prefer calling read_image_file(file, ...) directly and rely on its content-based detection/error handling (optionally fall back to checking file.name extension when needed).

Copilot uses AI. Check for mistakes.
Comment thread src/ReferenceImage.js
Comment on lines +68 to +72
const rect = this.canvas.getBoundingClientRect();
const cx = e.clientX - rect.left;
const cy = e.clientY - rect.top;
mox = ~~(cx / rect.width * this.canvas.width);
moy = ~~(cy / rect.height * this.canvas.height);
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

The move-handle drag offset is calculated using this.canvas.getBoundingClientRect(), but the pointerdown occurs on .reference-move-handle (positioned outside the canvas). This can produce negative/incorrect offsets and cause the reference image to jump when dragging. Compute the offset from the reference image’s current position instead (e.g., on pointerdown: const m = to_canvas_coords(e); mox = m.x - this.x; moy = m.y - this.y;).

Suggested change
const rect = this.canvas.getBoundingClientRect();
const cx = e.clientX - rect.left;
const cy = e.clientY - rect.top;
mox = ~~(cx / rect.width * this.canvas.width);
moy = ~~(cy / rect.height * this.canvas.height);
const m = to_canvas_coords(e);
mox = m.x - this.x;
moy = m.y - this.y;

Copilot uses AI. Check for mistakes.
Comment thread src/ReferenceImage.js
Comment on lines +42 to +55
this.handles = new Handles({
$handles_container: this.$el,
$object_container: $canvas_area,
outset: 2,
get_rect: () => ({ x: this.x, y: this.y, width: this.width, height: this.height }),
set_rect: ({ x, y, width, height }) => {
this.x = x;
this.y = y;
this._resize(width, height);
this.position();
},
get_ghost_offset_left: () => parseFloat($canvas_area.css("padding-left")) + 1,
get_ghost_offset_top: () => parseFloat($canvas_area.css("padding-top")) + 1,
});
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

Handles registers global $G.on("resize theme-load", update_handle) listeners but doesn’t provide a way to unregister them. Creating/destroying reference images repeatedly will accumulate window-level handlers (and this class also adds its own resize handler in addition to OnCanvasObject’s). Consider adding a destroy() method to Handles that removes its $G listeners and calling it from ReferenceImage.destroy(). Also consider avoiding the extra resize listener by overriding position() to also update the move-handle position, relying on OnCanvasObject’s resize handler.

Copilot uses AI. Check for mistakes.
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