Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enhance canvas foundation capabilities #1055

Merged
merged 9 commits into from
Feb 19, 2025

Conversation

SonyLeo
Copy link
Contributor

@SonyLeo SonyLeo commented Jan 21, 2025

English | 简体中文

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
  • Built its own designer, fully self-validated

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

Background and solution

What is the current behavior?

Issue Number: N/A

What is the new behavior?

  1. 右键添加父级容器

    在画布中右键页面元素 -> 添加父级 -> 容器:

    image

    选中指定 容器 点击 鼠标左键,

    image

    点击 画布元素 或点击 大纲树,即可查看新增的父容器

    image

  2. 节点多选能力

点击 Ctrl + 鼠标左键 进行 多选节点 ,支持快捷键操作复制粘贴删除

image

  1. 保存撤销 基础快捷键

焦点在 画布 时,快捷键生效

功能 Win 快捷键 支持多选
保存 Ctrl + S
复制 Ctrl + C
粘贴 Ctrl + V
剪切 Ctrl + X
删除 Del
前进 Ctrl + Y
后退/撤销 Ctrl + Z
多选 Ctrl + 🖱

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

Summary by CodeRabbit

Release Notes

  • New Features

    • Added multi-selection functionality for canvas elements.
    • Introduced container insertion option in context menu.
    • Enhanced keyboard interaction for multi-node operations.
    • Added the ability to filter components based on their group classification.
  • Improvements

    • Updated component initialization logic for materials.
    • Refined node insertion and selection handling, including unique ID generation and parent node validation.
    • Improved error handling for node operations, including reference node checks.
    • Enhanced visibility and control flow in canvas action components.
  • Bug Fixes

    • Corrected style calculations for canvas actions.
    • Fixed clipboard and deletion operations for multiple nodes.

Copy link
Contributor

coderabbitai bot commented Jan 21, 2025

Walkthrough

This pull request introduces comprehensive changes to the canvas and materials management system, focusing on multi-selection functionality, node insertion, and component management. The modifications span multiple files across the canvas and materials packages, enhancing node interaction, keyboard handling, and component filtering. Key improvements include robust multi-node selection, expanded insertion logic for nodes, and more flexible component management through new utility functions.

Changes

File Change Summary
packages/canvas/DesignCanvas/src/api/useCanvas.js Enhanced node insertion logic with parent node validation, unique ID generation, improved reference node handling, and refined return structure.
packages/canvas/container/src/CanvasContainer.vue Added multi-selection support, new reactive references, and updated event handling for node selection, including modifications to the setCurrentNode function.
packages/canvas/container/src/components/CanvasAction.vue Refined visibility logic for resize handles and quick actions with multi-state support, including new computed properties.
packages/canvas/container/src/components/CanvasMenu.vue Added new menu item for container insertion under the "Add Parent" section.
packages/canvas/container/src/container.js Introduced multi-selection state management, new insertion functions, and corrected function naming inconsistencies.
packages/canvas/container/src/keyboard.js Enhanced keyboard event handling for multi-node operations, including new functions for handling multiple selections and clipboard actions.
packages/plugins/materials/src/composable/useMaterial.js Added getComponentsByGroup function for filtering components by group and updated exports.
packages/plugins/materials/src/meta/component/src/Main.vue Updated component initialization with group-based filtering using initComponents.
packages/plugins/materials/src/meta/layout/src/Main.vue Added groupName prop for material group management, enhancing internal state management.

Possibly related PRs

  • feat: outline tree support dragging #1050: The changes in the main PR, specifically the modifications to the insertNode function in container.js, are related to the new insertContainer function introduced in the retrieved PR, as both involve enhancements to node insertion logic within the same file.
  • fix: TinyGrid slot children can not operate #1100: The changes in the main PR are related to the modifications in the operationTypeMap.insert function, which also involves unique ID generation for nodes, similar to the handling of unique IDs in the handleTinyGridColumnsSlots function from the retrieved PR.
  • fix: resolve some schema update issue #956: The changes in the main PR are related to the modifications in the importSchema function within the packages/canvas/DesignCanvas/src/api/useCanvas.js file, which is also referenced in the retrieved PR. Both PRs involve enhancements to the handling of schema imports and the management of canvas state.

Suggested labels

bug

Suggested reviewers

  • hexqi
  • chilingling

Poem

🐰 Nodes dance in multi-select glee,
Canvas expands with new energy!
Ctrl-click brings friends together,
Insertion magic, light as a feather.
Code rabbits hop with pure delight! 🎉

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/canvas/container/src/CanvasContainer.vue

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "@typescript-eslint/eslint-plugin".

(The package "@typescript-eslint/eslint-plugin" was not found when loaded as a Node module from the directory "/packages/canvas".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install @typescript-eslint/eslint-plugin@latest --save-dev

The plugin "@typescript-eslint/eslint-plugin" was referenced from the config file in "packages/canvas/.eslintrc.cjs » @vue/eslint-config-typescript".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added enhancement New feature or request refactor-main refactor/develop branch feature labels Jan 21, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (11)
packages/canvas/container/src/components/CanvasAction.vue (3)

184-187: Add validation for multiStateLength prop.

While the prop is correctly typed, adding validation would prevent potential issues with negative values.

 multiStateLength: {
   type: Number,
   default: () => 0,
+  validator: (value) => value >= 0
 },

243-256: Consider renaming showQuickAction for clarity.

The computed property showQuickAction could be more descriptive, such as showQuickActionForSingleNode, to better reflect its purpose.


Line range hint 1-735: Consider splitting component responsibilities.

The component currently handles multiple concerns (selection, resize, quick actions, style calculations). Consider:

  1. Extracting the style calculation logic into a composable for better reusability
  2. Splitting the component into smaller, focused components (e.g., CanvasResize, CanvasQuickActions)

This would improve maintainability and make the code more testable.

packages/plugins/materials/src/composable/useMaterial.js (1)

486-495: Consider enhancing error handling and type validation.

While the function implementation is clean and efficient, consider adding:

  1. Type validation for groupName
  2. Explicit error handling for invalid group names
 const getComponents = (components, groupName) => {
   if (!Array.isArray(components)) return []
+  if (typeof groupName !== 'string') return []
+  if (!groupName.trim()) return components
   return components.filter((item) => item.group === groupName)
 }
packages/plugins/materials/src/meta/component/src/Main.vue (1)

91-98: Consider memoizing the initialization result.

The initComponents function is well-implemented but could benefit from memoization to prevent unnecessary recalculations.

+import { computed } from 'vue'

-const initComponents = () => {
+const initComponents = computed(() => {
   const groupName = panelState.materialGroup
   if (groupName) {
     return getComponents(components, groupName)
   }
   return components
-}
+})

Then update the state initialization:

-components: initComponents(),
+components: initComponents.value,
packages/canvas/DesignCanvas/src/api/useCanvas.js (3)

264-271: Improve error handling for missing parent node.

Instead of silently returning an empty object when the parent node is missing, consider throwing an error to help with debugging.

-    if (!parentNode) {
-      return {}
-    }
+    if (!parentNode) {
+      throw new Error('Parent node not found')
+    }

291-308: Consider using a Map for position handling.

The switch statement could be replaced with a Map for better maintainability and performance.

-    switch (position) {
-      case 'before':
-        parentNode.children.unshift(newNodeData)
-        break
-      case 'out':
-        if (childrenNode) {
-          newNodeData.children = Array.isArray(childrenNode) ? [...childrenNode] : [childrenNode]
-          parentNode.children.splice(index, 1, newNodeData)
-        }
-        break
-      case 'bottom':
-        parentNode.children.splice(index + 1, 0, newNodeData)
-        break
-      default:
-        parentNode.children.push(newNodeData)
-        break
-    }
+    const positionHandlers = new Map([
+      ['before', () => parentNode.children.unshift(newNodeData)],
+      ['out', () => {
+        if (childrenNode) {
+          newNodeData.children = Array.isArray(childrenNode) ? [...childrenNode] : [childrenNode]
+          parentNode.children.splice(index, 1, newNodeData)
+        }
+      }],
+      ['bottom', () => parentNode.children.splice(index + 1, 0, newNodeData)],
+      ['default', () => parentNode.children.push(newNodeData)]
+    ])
+    const handler = positionHandlers.get(position) || positionHandlers.get('default')
+    handler()

351-352: Simplify array check using optional chaining.

The array check can be simplified while maintaining safety.

-        if (Array.isArray(nodeItem?.children) && nodeItem?.children.length) {
+        if (nodeItem?.children?.length) {
packages/canvas/container/src/CanvasContainer.vue (3)

35-42: Ensure proper cleanup of the insertContainer panel

The insertion panel for adding a parent container is conditionally rendered based on insertContainer. Ensure that this state is correctly managed to prevent the panel from remaining open unintentionally.

Consider adding a watcher or method to handle the closure of the panel under specific conditions, such as when the user clicks outside the panel or completes the insertion.


213-214: Avoid unnecessary reassignment of insertContainer

Setting insertContainer.value = false on every mousedown event may interfere with the intended functionality.

Review whether this reassignment is necessary here. If the goal is to close the insert container panel when clicking elsewhere, consider adding condition checks or handling this logic within a dedicated method.


229-229: Consistent handling of insertContainer in the mouseup event

Similar to the mousedown event, ensure that setting insertContainer.value = false aligns with the intended user experience.

Confirm that this does not prematurely close the insert container panel during user interactions.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ceb8bae and 283aee0.

📒 Files selected for processing (9)
  • packages/canvas/DesignCanvas/src/api/useCanvas.js (2 hunks)
  • packages/canvas/container/src/CanvasContainer.vue (11 hunks)
  • packages/canvas/container/src/components/CanvasAction.vue (6 hunks)
  • packages/canvas/container/src/components/CanvasMenu.vue (1 hunks)
  • packages/canvas/container/src/container.js (6 hunks)
  • packages/canvas/container/src/keyboard.js (6 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (2 hunks)
  • packages/plugins/materials/src/meta/component/src/Main.vue (2 hunks)
  • packages/plugins/materials/src/meta/layout/src/Main.vue (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (34)
packages/canvas/container/src/components/CanvasAction.vue (3)

12-12: LGTM! Improved condition for quick action visibility.

The change from resize to showQuickAction provides better semantics and correctly handles multi-selection scenarios.


31-66: LGTM! Enhanced resize handle visibility logic.

The resize handles are now correctly shown only for single node selection through the showAction computed property.


420-423: LGTM! Added defensive null check.

The null check for doc prevents potential runtime errors and follows defensive programming practices.

packages/plugins/materials/src/meta/layout/src/Main.vue (2)

44-46: LGTM! Clean prop definition.

The groupName prop is well-defined with appropriate type and default value.


55-55: LGTM! Proper state initialization.

The materialGroup is correctly initialized with the prop value in the panel state.

packages/plugins/materials/src/composable/useMaterial.js (1)

525-526: LGTM! Clean export addition.

The getComponents function is properly exported alongside other utility functions.

packages/plugins/materials/src/meta/component/src/Main.vue (2)

57-57: LGTM! Clean function import.

The getComponents function is properly destructured from useMaterial.


101-101: LGTM! Proper state initialization.

The state is correctly initialized using the initComponents function.

packages/canvas/DesignCanvas/src/api/useCanvas.js (1)

276-283: LGTM! Good error handling for reference node.

The error handling for missing reference nodes is well implemented with a descriptive error message.

packages/canvas/container/src/container.js (6)

33-34: LGTM! Position constants updated correctly.

The addition of the OUT position constant aligns with the new parent container functionality.


126-147: LGTM! Well-implemented state extraction.

The getMultiState function properly extracts and validates node state with appropriate null checks.


170-181: LGTM! Efficient multi-selection handling.

The handleMultiState function efficiently manages selection toggling using array methods.


380-393: LGTM! Clean implementation of container insertion.

The insertContainer function follows the same pattern as other insertion functions and properly handles ID generation.


816-818: LGTM! Proper integration of container insertion.

The insertNode function correctly handles the new 'out' position case.


149-168: Consider adding type validation for multiSelectedStates.

The function assumes multiSelectedStates is a ref object with a value property.

packages/canvas/container/src/components/CanvasMenu.vue (1)

109-110: LGTM! Menu item properly integrated.

The new container menu item is correctly added with appropriate code and value properties, maintaining consistency with existing menu items.

packages/canvas/container/src/CanvasContainer.vue (8)

99-101: Initialize containerPanel and insertContainer correctly

The references containerPanel and insertContainer are added to manage the parent container insertion panel. Verify that these are properly initialized and that insertContainer is reactive.

The initialization looks correct. Ensure that any components or methods relying on these references handle them appropriately.


102-103: Confirm computed property multiStateLength

The computed property multiStateLength calculates the length of multiSelectedStates.

This is a good approach to reactively track the number of selected states.


Line range hint 104-116: Verify the modified setCurrentNode function for multi-selection

The setCurrentNode function now accepts a doc parameter and includes logic for handling multi-selection with getMultiState and setMultiState.

Ensure that getMultiState and setMultiState functions are correctly implemented and that they effectively manage the multi-selection state.

If these functions are defined in another file or context, confirm their integration here.


256-256: Register hotkey events consistently

The registerHotkeyEvent(doc) call registers keyboard events within the iframe's document.

Good implementation for capturing keyboard events in the iframe context.


279-279: Include insertContainer in outside click handling

In the mousedown event listener on the window, the check now includes containerPanel.

This ensures that clicks outside the insertContainer panel are handled correctly.


292-295: Implement the insertion logic for the 'out' position

When position === 'out', the insertContainer state is updated accordingly.

This enables the right-click functionality to add a parent container, aligning with the PR objectives.


303-311: Watch multiStateLength to manage the properties panel

The watcher on multiStateLength clears the properties panel when more than one state is selected.

This is a suitable approach to prevent conflicting property displays during multi-selection.


2-14: Review the usage of selectState within the v-for loop

The selectState prop in <canvas-action> is conditionally set using multiStateLength > 1 ? multiState : selectState. This logic could lead to confusion or unintended behavior when multiStateLength is exactly 1.

Consider revising the condition to accurately reflect when multiple selections are involved. For instance:

-    :selectState="multiStateLength > 1 ? multiState : selectState"
+    :selectState="multiStateLength >= 1 ? multiState : selectState"

Alternatively, confirm that when multiStateLength is 1, selectState should indeed be multiState.

packages/canvas/container/src/keyboard.js (10)

13-17: Import and define new reactive states for multi-selection

The imports and reactive references for multiSelectedStates and isCtrlPressed are added.

This sets up the necessary state management for multi-selection and control key detection.


19-28: Define key codes for KEY_S and KEY_CTRL

The constants KEY_S and KEY_CTRL are introduced for handling save shortcuts and control key presses.

Defining key codes as constants improves code readability and maintainability.


48-53: Refactor handlerDelete to support multi-node deletion

The handlerDelete function now iterates over multiSelectedStates to delete multiple nodes.

This enhancement aligns with the multi-selection functionality, allowing users to delete multiple selected nodes simultaneously.


78-83: Implement save functionality with handleSaveEvent

The handleSaveEvent function is added to trigger the save action when Ctrl+S is pressed.

This provides a convenient shortcut for users to save their work, enhancing the user experience.


84-96: Update handlerCtrl to include KEY_S for saving

The control key handler now includes a case for KEY_S to invoke the save functionality.

This integration ensures that pressing Ctrl+S will trigger the save action as expected.


116-130: Handle pasting multiple nodes from the clipboard

The new handleMultiNodesPaste function manages the pasting of multiple nodes when multiple nodes are selected.

This addition enhances the clipboard functionality to work seamlessly with multi-selection.


157-162: Manage control key state with handleKeyupEvent

The handleKeyupEvent function updates the isCtrlPressed state upon key release.

This ensures accurate detection of the control key's state, which is important for multi-selection handling.


163-165: Remove hotkey events appropriately

The removeHotkeyEvent function now also removes the keyup event listener.

Cleaning up event listeners helps prevent memory leaks and unintended behavior.


171-175: Register hotkey events including keyup listener

The registerHotkeyEvent function adds the keyup event listener to manage control key state.

This ensures all necessary events are captured for the enhanced keyboard functionality.


181-181: Export new state variables

The export statement now includes multiSelectedStates and isCtrlPressed.

Exporting these variables allows other components to access and reactively use the multi-selection state and control key status.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (8)
packages/canvas/container/src/keyboard.js (5)

28-30: Initialize the multiSelectedStates ref with an empty array.

For better code clarity and to prevent potential undefined errors, initialize the ref:

-const multiSelectedStates = ref([])
+const multiSelectedStates = ref([])  // Initialize with empty array for multi-node selection

46-51: Add error handling for node removal operations.

The current implementation might fail silently if node removal encounters an error. Consider adding try-catch blocks:

 function handlerDelete() {
-  multiSelectedStates.value.forEach(({ id: schemaId }) => {
-    removeNodeById(schemaId)
-  })
+  multiSelectedStates.value.forEach(({ id: schemaId }) => {
+    try {
+      removeNodeById(schemaId)
+    } catch (error) {
+      console.error(`Failed to remove node ${schemaId}:`, error)
+    }
+  })
   multiSelectedStates.value.length = 0
 }

76-93: Add user feedback for save operations.

Consider adding visual feedback to inform users when the save operation succeeds or fails:

 const handleSaveEvent = (event) => {
   const { openCommon } = getMetaApi(META_APP.Save)
   event.preventDefault()
-  openCommon()
+  try {
+    openCommon()
+    // Add toast/notification for successful save
+  } catch (error) {
+    console.error('Save failed:', error)
+    // Add toast/notification for failed save
+  }
 }

113-125: Optimize memory usage for large multi-node operations.

The current implementation creates deep copies of all selected nodes, which could be memory-intensive for large selections. Consider:

  1. Adding a size limit for multi-selection
  2. Using a more memory-efficient copying mechanism
 const handleMultiNodesPaste = (node, schema, parent) => {
+  const MAX_SELECTION = 50 // Prevent excessive memory usage
+  if (multiSelectedStates.value.length > MAX_SELECTION) {
+    console.warn(`Selection limit (${MAX_SELECTION}) exceeded. Some nodes will be skipped.`)
+    return
+  }
   if (multiSelectedStates.value.length === 1) {
     handleClipboardPaste(node, schema, parent)
     return
   }

   const selectedStates = multiSelectedStates.value.map(({ schema, parent }) => {
     return { node: copyObject(schema), schema: toRaw(schema), parent: toRaw(parent) }
   })

   selectedStates.forEach(({ node, schema, parent }) => {
     handleClipboardPaste(node, schema, parent)
   })
 }

154-168: Add JSDoc comments for the event registration functions.

The rename from 'hostkey' to 'hotkey' is good, but the functions would benefit from clear documentation:

+/**
+ * Removes keyboard and clipboard event listeners from the specified DOM element.
+ * @param {HTMLElement} dom - The DOM element to remove events from
+ */
 const removeHotkeyEvent = (dom) => {
   dom.removeEventListener('keydown', keyboardHandler)
   dom.removeEventListener('copy', handlerClipboardEvent)
   dom.removeEventListener('cut', handlerClipboardEvent)
   dom.removeEventListener('paste', handlerClipboardEvent)
 }

+/**
+ * Registers keyboard and clipboard event listeners on the specified DOM element.
+ * @param {HTMLElement} dom - The DOM element to register events on
+ */
 const registerHotkeyEvent = (dom) => {
   removeHotkeyEvent(dom)
   // ... rest of the function
 }
packages/canvas/container/src/CanvasContainer.vue (3)

2-14: Optimize rendering performance and key uniqueness.

The current implementation might have performance issues with large selections and potential key conflicts:

  1. Consider adding virtual scrolling for large selections
  2. Use a more unique key combining id with parent id:
-  <div v-for="multiState in multiSelectedStates" :key="multiState.id">
+  <div v-for="multiState in multiSelectedStates" :key="`${multiState.id}-${multiState.parent?.id}`">

303-311: Consider combining related watchers for better maintainability.

The current implementation adds a separate watcher for multi-selection. Consider combining related watchers:

-watch(
-  () => multiStateLength.value,
-  (newVal) => {
-    if (newVal > 1) {
-      // 清空属性面板
-      selectNode(null)
-    }
-  }
-)
+watch(
+  [() => multiStateLength.value, () => selectState.value],
+  ([length, state]) => {
+    if (length > 1) {
+      // Clear property panel for multi-selection
+      selectNode(null)
+    } else if (state) {
+      // Handle single selection
+      // Add your single selection logic here
+    }
+  }
+)

292-295: Add type safety for position values.

The current implementation uses string literals without type safety. Consider using an enum or constants:

+const INSERT_POSITIONS = {
+  OUT: 'out',
+  IN: 'in',
+  // Add other valid positions
+} as const
+
 const insertComponent = (position) => {
-  if (position === 'out') {
+  if (position === INSERT_POSITIONS.OUT) {
     insertContainer.value = position
     return
   }
   insertPosition.value = position
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 283aee0 and e267d5e.

📒 Files selected for processing (2)
  • packages/canvas/container/src/CanvasContainer.vue (11 hunks)
  • packages/canvas/container/src/keyboard.js (7 hunks)
🔇 Additional comments (2)
packages/canvas/container/src/keyboard.js (1)

13-17: LGTM! Import statements are well-organized.

The imports are appropriate for the multi-selection and keyboard shortcut functionality being added.

packages/canvas/container/src/CanvasContainer.vue (1)

203-211: LGTM! Multi-selection trigger implementation is correct.

The implementation properly uses event.ctrlKey for multi-selection, addressing the concerns from the previous review.

@SonyLeo SonyLeo changed the title feat: right click to add parent container、node multi-selection and capability basic shortcut keys feat: enhance canvas foundation capabilities Jan 21, 2025
@hexqi hexqi added this to the v2.3.0 milestone Feb 13, 2025
@chilingling
Copy link
Member

  1. 需要重新 rebase refactor/develop。(同步部分修复的 bug)
  2. 实测发现 多选之后,ctrl + c,ctrl + v 无效。
  3. 插入父级的弹窗,需要屏蔽区块Tab。

@Issues-translate-bot
Copy link

Bot detected the issue body's language is not English, translate it automatically.


  1. Rebase refactor/develop needs to be rebase. (Synchronous partially fixed bug)
  2. Actual tests found that after multiple selections, ctrl + c, ctrl + v is invalid.
  3. Insert the pop-up window of the parent and block Tab needs to be blocked.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/canvas/container/src/keyboard.js (1)

76-93: ⚠️ Potential issue

Add platform-specific keyboard shortcut support.

The save shortcut needs to support both Windows (Ctrl+S) and macOS (Command+S).

Apply this diff to add cross-platform support:

 const handleSaveEvent = (event) => {
   const { openCommon } = getMetaApi(META_APP.Save)
   event.preventDefault()
   openCommon()
 }

 const handlerCtrl = (event) => {
+  const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform)
+  if (!(isMac ? event.metaKey : event.ctrlKey)) {
+    return
+  }
   const keyCode = event.keyCode
   switch (keyCode) {
     case KEY_Y:
       useHistory().forward()
       break
     case KEY_Z:
       useHistory().back()
       break
     case KEY_S:
       handleSaveEvent(event)
       break
     default:
       break
   }
 }
🧹 Nitpick comments (12)
packages/canvas/container/src/keyboard.js (2)

46-52: Add error handling for node deletion.

While the implementation works, it could benefit from error handling to gracefully handle cases where node deletion fails.

Consider this improved implementation:

 function handlerDelete() {
+  try {
     multiSelectedStates.value.forEach(({ id: schemaId }) => {
+      if (!schemaId) {
+        console.warn('Skipping deletion of node with undefined id')
+        return
+      }
       removeNodeById(schemaId)
     })
     multiSelectedStates.value = []
+  } catch (error) {
+    console.error('Error deleting nodes:', error)
+  }
 }

113-126: Enhance multi-node paste handler with validation and error handling.

The implementation works but could be more robust with additional validation and error handling.

Consider this improved implementation:

 const handleMultiNodesPaste = (node, schema, parent) => {
+  if (!node || !schema || !parent) {
+    console.warn('Invalid arguments for paste operation')
+    return
+  }
+
   if (multiSelectedStates.value.length === 1) {
     handleClipboardPaste(node, schema, parent)
     return
   }

+  try {
     const selectedStates = multiSelectedStates.value.map(({ schema, parent }) => {
+      if (!schema || !parent) {
+        console.warn('Skipping invalid state in multi-paste')
+        return null
+      }
       return { node: copyObject(schema), schema: toRaw(schema), parent: toRaw(parent) }
-    })
+    }).filter(Boolean)

     selectedStates.forEach(({ node, schema, parent }) => {
       handleClipboardPaste(node, schema, parent)
     })
+  } catch (error) {
+    console.error('Error during multi-node paste:', error)
+  }
 }
packages/canvas/container/src/CanvasContainer.vue (3)

214-225: Enhance multi-selection with platform-specific support.

While the implementation works, it could be more robust with platform-specific checks and better error handling.

Consider this improved implementation:

 if (element) {
+  const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform)
   const selectedState = getMultiState(element, doc)
-  if (event.ctrlKey && event.button === 0) {
+  if ((isMac ? event.metaKey : event.ctrlKey) && event.button === 0) {
+    try {
       handleMultiState(multiSelectedStates, selectedState)
+    } catch (error) {
+      console.error('Error handling multi-selection:', error)
+    }
     return
   }
 }

313-321: Add debouncing to multi-selection watcher.

The watcher could benefit from debouncing to prevent rapid successive updates when selecting multiple nodes quickly.

Consider this improved implementation:

+import { debounce } from 'lodash-es'
+
+const debouncedSelectNode = debounce((value) => {
+  if (value === null) return
+  selectNode(value)
+}, 150)
+
 watch(
   () => multiStateLength.value,
   (newVal) => {
     if (newVal > 1) {
-      selectNode(null)
+      debouncedSelectNode(null)
     }
   }
 )

35-43: Enhance accessibility for the container panel.

The container panel should include ARIA attributes and keyboard navigation support.

Consider these accessibility improvements:

 <div
   v-if="insertContainer"
   ref="containerPanel"
-  class="insert-panel">
+  class="insert-panel"
+  role="dialog"
+  aria-label="Add Parent Container"
+  @keydown.esc="insertContainer = false">
   <component
     :is="materialsPanel"
     :shortcut="insertContainer"
     groupName="layout"
+    tabindex="0"
+    aria-expanded="true"
     @close="insertContainer = false"
   ></component>
 </div>
packages/canvas/DesignCanvas/src/api/useCanvas.js (1)

294-357: Enhance node insertion with additional validation and documentation.

While the implementation is well-structured, it could benefit from more thorough validation and documentation.

Consider these improvements:

 insert: (operation) => {
   const { parentId, newNodeData, position, referTargetNodeId } = operation
+  // Validate operation parameters
+  if (!newNodeData || typeof newNodeData !== 'object') {
+    throw new Error('Invalid newNodeData: must be a valid object')
+  }
+
   const parentNode = getNode(parentId) || pageState.pageSchema
   // 1. 确认是否存在 ParentNode
   if (!parentNode) {
-    return {}
+    throw new Error(`Parent node with ID ${parentId} not found`)
   }

   parentNode.children = parentNode.children || []

   // 2. 确保 newNodeData 有唯一 ID, 如果没有,则生成新 ID
   if (!newNodeData.id) {
     newNodeData.id = utils.guid()
   }

   // 3. 查找参考节点
   let referenceNode = null
   if (referTargetNodeId) {
     referenceNode = getNode(referTargetNodeId)
     if (!referenceNode) {
       throw new Error(`Reference node with ID ${referTargetNodeId} not found`)
     }
   }

   // 4. 根据position参数选择插入位置
   let index = parentNode.children.indexOf(referenceNode)
   if (index === -1 && referTargetNodeId) {
     index = parentNode.children.length
   }

   // 5. 插入节点的逻辑
   const childrenNode = toRaw(referenceNode)
+  // Validate position parameter
+  const validPositions = ['before', 'out', 'bottom']
+  if (position && !validPositions.includes(position)) {
+    throw new Error(`Invalid position: ${position}. Must be one of: ${validPositions.join(', ')}`)
+  }
+
   switch (position) {
     case 'before':
+      // Insert at the beginning of parent's children
       parentNode.children.unshift(newNodeData)
       break
     case 'out':
+      // Replace reference node with new node and make reference node a child
       if (childrenNode) {
         newNodeData.children = Array.isArray(childrenNode) ? [...childrenNode] : [childrenNode]
         parentNode.children.splice(index, 1, newNodeData)
       }
       break
     case 'bottom':
+      // Insert after the reference node
       parentNode.children.splice(index + 1, 0, newNodeData)
       break
     default:
+      // Append to the end of parent's children
       parentNode.children.push(newNodeData)
       break
   }

   setNode(newNodeData, parentNode)

   // 6. 如果新节点有子节点,递归构建 nodeMap
   if (Array.isArray(newNodeData?.children) && newNodeData.children.length > 0) {
     const newNode = getNode(newNodeData.id)
+    // Validate newNode before recursion
+    if (!newNode) {
+      throw new Error(`Failed to retrieve newly created node with ID ${newNodeData.id}`)
+    }
     generateNodesMap(newNodeData.children, newNode)
   }

   // 7. 返回插入结果
   return {
     current: newNodeData,
     previous: undefined
   }
 }
packages/canvas/container/src/components/CanvasAction.vue (3)

184-187: Add prop validation for multiStateLength.

While the prop is correctly typed, it could benefit from additional validation.

Consider adding validation:

 multiStateLength: {
   type: Number,
-  default: () => 0
+  default: 0,
+  validator: (value) => {
+    return value >= 0 && Number.isInteger(value)
+  }
 }

243-257: Optimize computed properties with memoization.

The computed properties could benefit from memoization to prevent unnecessary recalculations.

Consider this optimization:

+import { computed, unref } from 'vue'

 const isSingleNode = computed(() => {
-  return props.multiStateLength < 2
+  return unref(props.multiStateLength) < 2
 })

 const showAction = computed(() => {
   const { schema, parent } = getCurrent()
-  if (schema?.props?.['data-id'] === 'root-container') {
+  // Memoize the result of getCurrent()
+  const currentSchema = unref(schema)
+  if (currentSchema?.props?.['data-id'] === 'root-container') {
     return false
   }
-  return !props.resize && parent && parent?.type !== 'JSSlot' && isSingleNode.value
+  return !unref(props.resize) && parent && parent?.type !== 'JSSlot' && unref(isSingleNode)
 })

 const showQuickAction = computed(() => {
-  return !props.resize && isSingleNode.value
+  return !unref(props.resize) && unref(isSingleNode)
 })

420-422: Enhance error handling for missing document.

The null check is good, but could be more explicit about the reason for returning an empty object.

Consider this improvement:

-if (!doc) {
+// Ensure document is available before calculating styles
+if (!doc || !(doc instanceof Document)) {
+  console.warn('Document is not available or invalid for style calculation')
   return {}
 }
packages/canvas/container/src/container.js (3)

126-147: Add error handling and JSDoc documentation.

The getMultiState function would benefit from:

  1. Error handling for invalid elements or missing attributes
  2. JSDoc documentation describing parameters and return type
+/**
+ * Retrieves the state of a selected node including its dimensions and attributes
+ * @param {HTMLElement} element - The DOM element to get state from
+ * @param {Document} doc - The document context
+ * @returns {Object|undefined} The node state or undefined if invalid
+ */
 export const getMultiState = (element, doc) => {
+  if (!element?.nodeType || !doc?.nodeType) {
+    return undefined
+  }
   const { top, left, width, height } = element.getBoundingClientRect()
   const nodeTag = element?.getAttribute(NODE_TAG)
   const nodeId = element?.getAttribute(NODE_UID)

149-168: Add validation for multiSelectedStates parameter.

The setMultiState function should validate the multiSelectedStates parameter to ensure it's a valid reactive reference.

+/**
+ * Sets the state of multiple selected nodes
+ * @param {Ref<Array>} multiSelectedStates - Reactive reference to selected states
+ * @param {Object|Array} node - Node or array of nodes to set
+ * @param {boolean} [append=false] - Whether to append or replace states
+ */
 export function setMultiState(multiSelectedStates, node, append = false) {
+  if (!multiSelectedStates?.value) {
+    console.warn('Invalid multiSelectedStates parameter')
+    return
+  }
   if (!node || typeof node !== 'object') {
     multiSelectedStates.value = []
     return
   }

380-393: Add error handling and documentation for container insertion.

The insertContainer function should include:

  1. Documentation explaining the OUT position usage
  2. Validation for required parameters
+/**
+ * Inserts a container node as a parent of the target node
+ * @param {Object} params - Insertion parameters
+ * @param {Object} params.parent - Parent node
+ * @param {Object} params.node - Target node
+ * @param {Object} params.data - Container node data
+ * @throws {Error} If required parameters are missing
+ */
 const insertContainer = ({ parent, node, data }) => {
+  if (!parent?.id || !node?.id || !data) {
+    throw new Error('Missing required parameters for container insertion')
+  }
   if (!data.id) {
     data.id = utils.guid()
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e267d5e and b96bb99.

📒 Files selected for processing (9)
  • packages/canvas/DesignCanvas/src/api/useCanvas.js (2 hunks)
  • packages/canvas/container/src/CanvasContainer.vue (11 hunks)
  • packages/canvas/container/src/components/CanvasAction.vue (6 hunks)
  • packages/canvas/container/src/components/CanvasMenu.vue (1 hunks)
  • packages/canvas/container/src/container.js (6 hunks)
  • packages/canvas/container/src/keyboard.js (7 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (2 hunks)
  • packages/plugins/materials/src/meta/component/src/Main.vue (2 hunks)
  • packages/plugins/materials/src/meta/layout/src/Main.vue (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/canvas/container/src/components/CanvasMenu.vue
  • packages/plugins/materials/src/composable/useMaterial.js
  • packages/plugins/materials/src/meta/layout/src/Main.vue
  • packages/plugins/materials/src/meta/component/src/Main.vue
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (5)
packages/canvas/container/src/keyboard.js (2)

13-17: LGTM! Imports are well organized.

The imports are logically grouped and include all necessary dependencies for the new multi-selection functionality.


28-30: LGTM! Good use of Vue's reactivity system.

The multiSelectedStates ref is well-named and appropriately uses Vue's reactivity system for managing multiple selected nodes.

packages/canvas/container/src/CanvasContainer.vue (1)

1-14: LGTM! Well-structured template for multi-selection support.

The template effectively handles multiple selections by iterating over multiSelectedStates and properly passing state information to child components.

packages/canvas/container/src/container.js (2)

33-34: LGTM! Position enum updated for external container support.

The addition of OUT position enhances the canvas's container management capabilities.


800-819: LGTM! Node insertion logic updated for external container support.

The switch case addition for POSITION.OUT maintains consistency with existing position handling.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
packages/canvas/container/src/keyboard.js (4)

28-30: Add JSDoc documentation for the multiSelectedStates ref.

While the implementation is good, adding documentation would help other developers understand the purpose and structure of the multiSelectedStates ref.

Add documentation above the ref:

+ /**
+  * Reactive reference for tracking multiple selected nodes in the canvas
+  * @type {import('vue').Ref<Array<{id: string, schema: Object, parent: Object}>>}
+  */
const multiSelectedStates = ref([])

39-44: Add null checks for safer navigation.

The current implementation could throw errors if parent or children are undefined.

Consider adding null checks:

-  const id = (parent?.children[index - 1] || parent)?.id
+  const id = parent?.children?.[index - 1]?.id || parent?.id

-  const id = parent?.children[index + 1]?.id
+  const id = parent?.children?.[index + 1]?.id

113-126: Add error handling for clipboard operations.

The multi-node paste operation could benefit from additional error handling and user feedback.

Consider adding try-catch and user feedback:

 const handleMultiNodesPaste = (node, schema, parent) => {
+  try {
     if (multiSelectedStates.value.length === 1) {
       handleClipboardPaste(node, schema, parent)
       return
     }

     const selectedStates = multiSelectedStates.value.map(({ schema, parent }) => {
       return { node: copyObject(schema), schema: toRaw(schema), parent: toRaw(parent) }
     })

     selectedStates.forEach(({ node, schema, parent }) => {
       handleClipboardPaste(node, schema, parent)
     })
+  } catch (error) {
+    console.error('Failed to paste multiple nodes:', error)
+    // Consider adding user notification here
+  }
 }

162-169: Consider using AbortController for cleaner event handling.

The current implementation could benefit from using AbortController for more robust cleanup.

Consider this pattern:

+const controller = new AbortController()
+const { signal } = controller

 const registerHotkeyEvent = (dom) => {
   removeHotkeyEvent(dom)

-  dom.addEventListener('keydown', keyboardHandler)
-  dom.addEventListener('copy', handlerClipboardEvent)
-  dom.addEventListener('cut', handlerClipboardEvent)
-  dom.addEventListener('paste', handlerClipboardEvent)
+  dom.addEventListener('keydown', keyboardHandler, { signal })
+  dom.addEventListener('copy', handlerClipboardEvent, { signal })
+  dom.addEventListener('cut', handlerClipboardEvent, { signal })
+  dom.addEventListener('paste', handlerClipboardEvent, { signal })
 }

 const removeHotkeyEvent = (dom) => {
-  dom.removeEventListener('keydown', keyboardHandler)
-  dom.removeEventListener('copy', handlerClipboardEvent)
-  dom.removeEventListener('cut', handlerClipboardEvent)
-  dom.removeEventListener('paste', handlerClipboardEvent)
+  controller.abort()
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b96bb99 and 724b3cf.

📒 Files selected for processing (1)
  • packages/canvas/container/src/keyboard.js (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (3)
packages/canvas/container/src/keyboard.js (3)

13-20: LGTM! Well-organized imports and constants.

The new imports and constants are properly structured and necessary for the added functionality.


46-52: Use conventional array clearing syntax.

As previously suggested, use the conventional way to clear the array.

-  multiSelectedStates.value = []
+  multiSelectedStates.value.length = 0

76-80: LGTM! Proper cross-platform keyboard shortcut handling.

The implementation correctly handles both Ctrl (Windows/Linux) and Command (Mac) keys for shortcuts.

Also applies to: 147-150

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/canvas/container/src/CanvasContainer.vue (2)

214-220: Enhance multi-selection key handling for better cross-platform support.

While the code now handles both Ctrl and Command keys, the implementation could be more robust.

Consider using a more explicit check for modifier keys:

-if ((event.ctrlKey && event.button === 0) || event.metaKey) {
+if (event.button === 0 && (event.ctrlKey || event.metaKey)) {

This change:

  1. Prioritizes the primary button check
  2. Groups modifier keys more clearly

1-384: Consider enhancing keyboard accessibility.

The component could benefit from improved keyboard navigation support:

  1. Add visible focus indicators for interactive elements
  2. Implement proper tab order
  3. Add ARIA attributes for better screen reader support

Would you like me to provide specific examples of ARIA attributes and focus management implementation?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 724b3cf and a49ecf5.

📒 Files selected for processing (1)
  • packages/canvas/container/src/CanvasContainer.vue (11 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (4)
packages/canvas/container/src/CanvasContainer.vue (4)

2-14: LGTM! Multi-selection implementation looks solid.

The v-for loop correctly handles multiple selections with proper key binding and state management.


51-51: LGTM! Import changes align with new features.

The imports and naming updates correctly support the multi-selection and container management features.

Also applies to: 77-80


109-112: LGTM! State management is well-structured.

The reactive state setup efficiently handles both container management and multi-selection tracking.


313-321: LGTM! Clean handling of multi-selection state changes.

The watcher appropriately manages the property panel state when multiple items are selected.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/canvas/container/src/CanvasContainer.vue (2)

35-43: Consider adding keyboard navigation handling for the container panel.

The panel should handle keyboard navigation appropriately to ensure good accessibility.

Add keyboard event handling to prevent Tab key from moving focus outside the panel:

 <div v-if="insertContainer" ref="containerPanel" class="insert-panel">
   <component
     :is="materialsPanel"
     :shortcut="insertContainer"
     groupName="layout"
+    @keydown.tab.prevent
     @close="insertContainer = false"
   ></component>
 </div>

313-321: Consider optimizing the watcher cleanup.

The watcher clears the property panel when multiple nodes are selected, but it might be better to preserve the previous selection when returning to single selection.

 watch(
   () => multiStateLength.value,
   (newVal) => {
     if (newVal > 1) {
-      // 清空属性面板
-      selectNode(null)
+      const previousSelection = selectState.value
+      selectNode(null)
+      // Store previous selection to restore when returning to single selection
+      if (previousSelection) {
+        multiSelectedStates.value.previousSelection = previousSelection
+      }
     } else if (newVal === 1 && multiSelectedStates.value.previousSelection) {
+      // Restore previous selection when returning to single selection
+      selectNode(multiSelectedStates.value.previousSelection)
+      delete multiSelectedStates.value.previousSelection
     }
   }
 )
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a49ecf5 and c7ef6eb.

📒 Files selected for processing (1)
  • packages/canvas/container/src/CanvasContainer.vue (11 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (3)
packages/canvas/container/src/CanvasContainer.vue (3)

2-14: LGTM! Multi-selection implementation looks good.

The v-for loop correctly handles multiple selections with proper key binding and conditional state management.


214-220: LGTM! Multi-selection keyboard handling is now complete.

The implementation now correctly handles both Control (Windows/Linux) and Command (Mac) keys for multi-selection.


51-51:

❓ Verification inconclusive

Verify the impact of renaming hotkeyEvent registration.

The change from registerHostkeyEvent to registerHotkeyEvent might be a breaking change. Please ensure all usages are updated.


🏁 Script executed:

#!/bin/bash
# Search for any remaining usage of the old name
rg "registerHostkeyEvent|removeHostkeyEvent" -l

Length of output: 49


Confirm consistent hotkey event naming across the repository.

I verified that no instances of the old names (registerHostkeyEvent or removeHostkeyEvent) appear in the codebase via an rg search. However, since the initial search produced no output—which might be due to how the command was executed—please manually verify that all usages (including in tests and integrations) have been updated to use registerHotkeyEvent and removeHotkeyEvent.

  • File of concern: packages/canvas/container/src/CanvasContainer.vue (line 51) now correctly imports:
    import { registerHotkeyEvent, removeHotkeyEvent, multiSelectedStates } from './keyboard'
  • Action: Double-check any dependent modules or integration tests that might rely on the old naming.

@hexqi hexqi merged commit 8d51ad4 into opentiny:refactor/develop Feb 19, 2025
1 check passed
@chilingling chilingling mentioned this pull request Feb 24, 2025
14 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request refactor-main refactor/develop branch feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants