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: schemaDataAsync #885

Open
wants to merge 24 commits into
base: refactor/develop
Choose a base branch
from

Conversation

chilingling
Copy link
Member

@chilingling chilingling commented Oct 29, 2024

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?

  • Feature
  • Bugfix
  • 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?

  1. 当前对 schema 的修改都是直接对画布内的 schema 引用进行修改。
  2. 画布外的主进程 host 与画布内是两个 vue 实例。导致 host 主进程无法主动监听到最新 schema 的变化。
    • 比如大纲树无法观察到元素的拖拽删除,及时渲染最新的大纲树。
    • 比如 schema 面板无法实时观察到画布的变化,只能在打开面板的时候,才获取最新的 schema。
  3. 其他插件有观察 schema 变化的场景,当前都无法满足。

【解决方案】What is the new behavior?

统一 schema 修改入口 + schema 变更事件发布订阅 + jsondiff 同步差异
image

【PR主要改动点】

  1. 修改 schema 统一入口。
    以往修改 schema 的方法:直接 schema.props = xxx ,即拿到引用直接进行修改
    当前 schema 修改的入口改为:

    • useCanvas().opeateNode(operation) 主要用于操作节点删除、节点新增、节点 props 属性修改。
    • useCanvas().updateSchema(data) 主要用于更新 schema 的 statecssmethodlifecycles 等非节点属性。
    • useCanvas().importSchema(data) 主要用于页面、区块之间的切换。
  2. 事件通知 schema 改变事件。

    • schemaChange 事件。调用 opeateNode 以及 updateSchema 会触发该事件的发布。
    • schemaImport 事件。调用 importSchema 会触发该事件的发布。
  3. 修改 utilsbridgeglobalStatedataSourceMap 等入口从直接修改画布改为直接修改 useResource 中的 appSchemaState

  4. 画布 RenderMain 减少相关 schema 的操作接口(修改移动到统一的页面修改入口以及 appSchema 修改)

    • 减少的相关接口:setUtilsupdateUtilsdeleteUtilsgetBridgesetBridgegetMethodssetMethodsgetSchemasetSchemagetStatedeleteStatesetStategetPropssetPropsgetContextgetRootgetNodesetNodegetConditiongetGlobalStatesetGlobalStategetDataSourceMapsetDataSourceMap
  5. 画布对 schema 变更的状态的感知,改为 schema 相关修改事件的监听以及状态的订阅。

  6. 语义化重命名:resState 改为 appSchemaState

  7. 内置组件数据源 collection 改造:
    组件内置的逻辑:刷新 children schema、检测 children 变更。(即 CanvasCollection.vue 中的 setup 逻辑、canvasCollection.js ,迁移到 configurator/src/collection-configurator 中 )
    迁移过来之后遗留问题: configurator 中无法感知 children 的变化(需要手动点击刷新按钮),所以无法自动提示刷新 children 中的 相关数据源字段。
    新旧版本皆有的遗留问题:数据源刷新了,表格数据无法刷新(需要刷新页面)

【PR 分拆任务】 (待确认)
由于本次 PR 改动基础 api。涉及的改动点较多,代码量也较大,为了避免单次PR合入过多特性,我们将一些新特性分拆到后续的 PR 中逐次迭代演进。简要梳理如下:

  • 历史堆栈:需要改造成为快照型,基于 schemaChange 事件记录堆栈。并支持 batchChange 批量事件的历史堆栈记录。(即 schema 历史堆栈自动化记录、配置化能力支持)。
  • useProperties 相关的 api 整改。整改为以 node 节点为基础的对象实例,自带增删查改的相关 api。
    • 由此特性可以衍生出来设置面板、样式面板的设置组件自定义能力、可插拔能力。
  • 更丰富的节点事件:比如父节点监听子节点增加、删除、属性之间联动监听
  • 画布插件化:支持画布自定义逻辑注入,比如我们往 schema 中扩展了私有的协议,需要在画布中进行监听,此时就可以注入局部的逻辑进行监听。

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

  • New Features

    • Introduced a new section in the DesignCanvas README for experimental APIs related to schema management.
    • Enhanced functionality for managing node operations within the canvas.
    • Added a new method for refreshing data in the DataSourceList component.
    • Streamlined schema retrieval across various components, improving data handling.
  • Bug Fixes

    • Improved error handling in various components, including BlockDeployDialog and DataSourceRecordList.
  • Documentation

    • Updated README files to reflect new functionalities and usage examples.
  • Style

    • Refactored styles and class management in several components for improved clarity and maintainability.
  • Chores

    • Updated dependencies in package.json files across multiple packages.

Copy link
Contributor

coderabbitai bot commented Oct 29, 2024

Walkthrough

The pull request includes extensive modifications across multiple files, focusing on enhancing functionality and improving the structure of code related to schema management and component interactions. Key changes involve the formatting of JSON objects in bundle.json, the introduction of new methods and properties in various components, and a shift towards using the useCanvas hook for schema operations. Additionally, several components have been updated to streamline data handling and improve user interactions, while some redundant methods have been removed to simplify the codebase.

Changes

File Path Change Summary
designer-demo/public/mock/bundle.json Reformatted JSON properties in shortcuts, contextMenu, and invalidity to single-line arrays.
packages/canvas/DesignCanvas/README.md Added documentation for new experimental APIs related to schema management, including code examples and type definitions.
packages/canvas/DesignCanvas/src/DesignCanvas.vue Updated removeNode and nodeSelected methods to improve node handling and messaging functionality. Added useMessage import.
packages/canvas/DesignCanvas/src/api/useCanvas.js Introduced new node management functions and modified existing ones to enhance schema manipulation and state management.
packages/canvas/container/src/CanvasContainer.vue Enhanced interactivity with new imports and updated event handling for mouse actions and context menus.
packages/canvas/container/src/components/CanvasAction.vue Updated showToParent computed property to use useCanvas().getSchema().
packages/canvas/container/src/container.js Refactored functions to utilize useCanvas for state operations and schema management.
packages/canvas/container/src/keyboard.js Updated schema retrieval method to use useCanvas().getSchema().
packages/canvas/package.json Added dependencies for diff-match-patch and jsondiffpatch.
packages/canvas/render/src/RenderMain.js Simplified state management and added throttling for schema updates.
packages/canvas/render/src/builtin/CanvasCollection.js Updated functions to enhance data handling and state updates with new parameters.
packages/canvas/render/src/builtin/CanvasCollection.vue Added reactive reference for updateKey to manage component updates.
packages/canvas/render/src/context.js Removed several node management functions to streamline context handling.
packages/canvas/render/src/render.js Simplified rendering logic by removing unnecessary parameters.
packages/common/component/BlockDeployDialog.vue Updated schema access methods for improved functionality.
packages/common/component/BlockLinkEvent.vue Simplified schema access in event handling functions.
packages/common/component/BlockLinkField.vue Updated schema retrieval method to streamline data access.
packages/common/component/ConfigItem.vue Enhanced property handling and error management in component logic.
packages/common/component/LifeCycles.vue Updated lifecycle data handling with a simplified schema access method.
packages/common/component/MetaListItems.vue Renamed variable for clarity in language translations.
packages/common/js/canvas.js Updated demo status retrieval method to use appSchemaState.
packages/common/js/completion.js Changed data source for user words to appSchemaState.
packages/configurator/src/collection-configurator/CollectionConfigurator.vue Replaced input element with a custom component and improved child management logic.
packages/configurator/src/container-configurator/ContainerConfigurator.vue Enhanced child element management and validation during initialization.
packages/configurator/src/html-attributes-configurator/HtmlAttributesConfigurator.vue Streamlined attribute management and updates through operateNode.
packages/configurator/src/html-text-configurator/HtmlTextConfigurator.vue Updated attribute handling methods to use operateNode.
packages/configurator/src/js-slot-configurator/JsSlotConfigurator.vue Refined slot management and validation logic.
packages/configurator/src/layout-grid-configurator/LayoutGridConfigurator.vue Enhanced layout updates through structured schema interactions.
packages/configurator/src/related-editor-configurator/RelatedEditorConfigurator.vue Simplified schema access method for improved readability.
packages/configurator/src/slot-configurator/SlotConfigurator.vue Improved slot management logic with confirmation prompts.
packages/configurator/src/table-columns-configurator/TableColumnsConfigurator.vue Updated column management to use structured API calls for schema updates.
packages/configurator/src/variable-configurator/VariableConfigurator.vue Enhanced variable binding logic and schema access methods.
packages/plugins/block/src/composable/useBlock.js Improved schema initialization logic in initBlock.
packages/plugins/bridge/src/js/resource.js Updated resource management to use appSchemaState.
packages/plugins/datasource/src/DataSourceGlobalDataHandler.vue Updated state management to source values from appSchemaState.
packages/plugins/datasource/src/DataSourceList.vue Added refresh functionality and updated data source handling.
packages/plugins/datasource/src/DataSourceRecordList.vue Enhanced data fetching logic with event emission for updates.
packages/plugins/datasource/src/js/datasource.js Updated handler retrieval to use appSchemaState.
packages/plugins/i18n/src/composable/useTranslate.js Updated language data retrieval to use appSchemaState.
packages/plugins/materials/src/composable/useMaterial.js Adjusted comments for clarity regarding state management.
packages/plugins/materials/src/composable/useResource.js Renamed and restructured state management logic for clarity.
packages/plugins/schema/package.json Added @vueuse/core dependency.
packages/plugins/schema/src/Main.vue Enhanced schema handling and state management with new lifecycle hooks.
packages/plugins/script/src/js/method.js Streamlined schema retrieval and method management logic.
packages/plugins/state/src/DataSourceList.vue Updated resource state access for store deletion confirmation.
packages/plugins/state/src/Main.vue Refactored state management logic for clarity and efficiency.
packages/plugins/tree/src/Main.vue Enhanced schema filtering and state management for drag-and-drop functionality.
packages/settings/events/src/components/AdvanceConfig.vue Improved configuration update logic and schema management.
packages/settings/props/src/components/Empty.vue Updated state management to directly observe schema properties.
packages/settings/props/src/components/modal/ModalContent.vue Updated title computation to reflect current schema state.
packages/settings/props/src/composable/useProperties.js Refactored property management logic to reduce direct mutations.
packages/settings/styles/src/Main.vue Added method for updating style properties in schema.
packages/settings/styles/src/components/classNamesContainer/index.vue Enhanced property management and selector handling logic.
packages/settings/styles/src/js/useEditor.js Updated schema interaction methods for saving styles.
packages/settings/styles/src/js/useStyle.js Introduced new watcher initialization function and streamlined style management.
packages/toolbars/generate-code/src/Main.vue Simplified schema access and improved error handling in code generation.
packages/toolbars/preview/src/Main.vue Updated schema retrieval method for preview functionality.
packages/toolbars/redoundo/src/composable/useHistory.js Updated schema handling methods for history management.
packages/toolbars/save/src/js/index.js Enhanced state management during save operations.
packages/utils/src/constants/index.js Removed SchemaLength property from BROADCAST_CHANNEL.

Possibly related PRs

  • fix: fix exportName mismatch #929: The changes in bundle.json involve modifications to the configuration of UI components, including updates to the exportName for components like TinyBreadcrumb, which is relevant to the formatting changes made in the main PR's bundle.json file.
  • fix: state & globalState getter #930: The modifications in RenderMain.js and CreateVariable.vue focus on state management and getter functions, which may relate to the overall structure and functionality of components as seen in the main PR's changes to bundle.json, particularly in how properties are defined and accessed.

Suggested labels

bug, ready_for_review

Suggested reviewers

  • rhlin
  • yy-wow

🐰 In the fields so wide and green,
Changes hop, a joyful scene!
With schemas bright and code refined,
A canvas fresh, creativity aligned.
Let’s celebrate with leaps and cheer,
For every change, a new frontier!
🐇


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. (Experiment)
  • @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 Oct 29, 2024
@chilingling chilingling added the refactoring Refactoring label Nov 9, 2024
@chilingling chilingling marked this pull request as ready for review December 11, 2024 09:37
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: 16

🧹 Outside diff range and nitpick comments (48)
packages/canvas/container/src/components/CanvasAction.vue (2)

238-238: Consider caching the useCanvas hook result

The useCanvas() hook is called on every computation of showToParent. Consider extracting the hook call to the setup function to avoid potential performance impacts.

-    const showToParent = computed(() => getCurrent().parent !== useCanvas().getSchema())
+    const canvas = useCanvas()
+    const showToParent = computed(() => getCurrent().parent !== canvas.getSchema())

Line range hint 347-452: Consider adding documentation for the positioning strategy

The Align class and positioning logic are complex but crucial for the component's functionality. Consider adding JSDoc comments to explain:

  • The purpose and strategy of the positioning system
  • The significance of alignment calculations
  • Edge cases and their handling
packages/plugins/state/src/DataSourceList.vue (2)

Line range hint 71-91: Consider enhancing error handling and user feedback

While the code correctly prevents deletion of referenced stores, consider these improvements:

  1. Add error handling for cases where pageTree might be undefined
  2. Enhance the message UI to show the exact locations where the store is referenced

Here's a suggested improvement:

      const appPages = useResource().appSchemaState.pageTree.filter(
        (page) => page.componentName === COMPONENT_NAME.Page && page?.meta?.group !== 'publicPages'
      )
      const expression = `stores.${key}`
-      const expresstionPages = findExpressionInAppSchema(appPages, expression)
+      try {
+        const expressionPages = findExpressionInAppSchema(appPages, expression)
+        
+        if (expressionPages.length > 0) {
+          useModal().message({
+            title: '提示',
+            message: (
+              <div>
+                <div>不允许删除,因为检查到 {expression} 在以下页面中被引用:</div>
+                <div style="max-height: 200px; overflow-y: auto; margin-top: 8px;">
+                  {expressionPages.map((pageName) => (
+                    <div key={pageName} style="padding: 4px 0;">• {pageName}</div>
+                  ))}
+                </div>
+              </div>
+            )
+          })
+        } else {
+          useModal().confirm({
+            title: '提示',
+            message: `您确定要删除 ${key} 吗?`,
+            exec: () => emit('removeStore', key)
+          })
+        }
+      } catch (error) {
+        useModal().message({
+          title: '错误',
+          message: '检查store引用时发生错误,请稍后重试'
+        })
+      }

Line range hint 71-91: Consider implementing store reference caching

For better performance, consider caching the store references instead of searching through the entire schema on every deletion attempt. This would be especially beneficial for large applications with many pages.

Would you like me to provide an example implementation of a caching mechanism for store references?

packages/plugins/datasource/src/js/datasource.js (2)

65-66: Consider request lifecycle implications

The migration of willFetch handler to appSchemaState completes the handler migration pattern. This change affects the request lifecycle, particularly pre-request validation and transformation.

Consider documenting the request lifecycle flow in the component's documentation, especially highlighting:

  • The order of handler execution
  • The interaction between global and local handlers
  • The state management flow through appSchemaState

57-66: Consider implementing handler state management pattern

The migration of all handlers to appSchemaState suggests an opportunity for a more structured state management pattern.

Consider implementing a dedicated handler management system that could:

  1. Provide type safety for handlers
  2. Implement handler composition patterns
  3. Add handler lifecycle hooks

Example structure:

interface HandlerState {
  dataHandler: Handler<Data>;
  errorHandler: Handler<Error>;
  willFetch: Handler<Request>;
}

class HandlerManager {
  constructor(private state: HandlerState) {}
  
  composeHandlers<T>(handler: Handler<T>, defaultHandler: Handler<T>): Handler<T> {
    return this.state[handler] || defaultHandler;
  }
}
packages/common/component/LifeCycles.vue (1)

158-161: Consider adding error handling for schema access.

While the simplified schema access is good, consider adding error handling to gracefully handle cases where neither props nor schema contains the lifecycle data.

Consider this enhancement:

-      const bindLifeCycleSource = props.bindLifeCycles?.[item] || useCanvas().getSchema()?.lifeCycles?.[item]
+      const bindLifeCycleSource = props.bindLifeCycles?.[item] || useCanvas().getSchema()?.lifeCycles?.[item]
+      if (!bindLifeCycleSource) {
+        console.warn(`No lifecycle source found for ${item}`)
+      }
       state.editorValue =
         bindLifeCycleSource?.value ||
         `function ${item} (${item === 'setup' ? '{ props, state, watch, onMounted }' : ''}) {} `
packages/settings/events/src/components/AdvanceConfig.vue (3)

138-138: Improve number checking logic for clarity

The current method for checking if value is a number can be simplified and made more readable. Using isNaN provides a clearer intention.

Apply this diff to improve the number checking logic:

- const isNumber = Number(value).toString() !== 'NaN'
+ const isNumber = !isNaN(Number(value))

202-207: Avoid mutating loopArgs directly in setLoopIndex

Directly modifying loopArgs can lead to unintended side effects since it mutates the original array in the schema. It's better to create a new array to ensure immutability.

Apply this diff to clone loopArgs before modifying:

- loopArgs[1] = value || DEFAULT_LOOP_NAME.INDEX
+ loopArgs = loopArgs ? [...loopArgs] : []
+ loopArgs[1] = value || DEFAULT_LOOP_NAME.INDEX

237-242: Avoid mutating loopArgs directly in setLoopItem

Similarly, in setLoopItem, cloning loopArgs before modification prevents potential side effects from mutating the original array.

Apply this diff to clone loopArgs before modifying:

- loopArgs[0] = value || DEFAULT_LOOP_NAME.ITEM
+ loopArgs = loopArgs ? [...loopArgs] : []
+ loopArgs[0] = value || DEFAULT_LOOP_NAME.ITEM
packages/configurator/src/container-configurator/ContainerConfigurator.vue (1)

84-86: Confirm insertion position when adding new tabs

In line 86, the new child node is inserted with position: 'after'. If the intention is to add the new tab at the end of the existing tabs, consider using position: 'append' for clarity and to avoid unexpected ordering.

Apply this diff if necessary:

-operateNode({ type: 'insert', parentId: schema.id, newNodeData, position: 'after' })
+operateNode({ type: 'insert', parentId: schema.id, newNodeData, position: 'append' })
packages/canvas/render/src/builtin/CanvasCollection.vue (1)

51-53: Add Null Checks When Accessing Global Objects

When accessing window.host.schemaUtils, consider adding checks to ensure that window, window.host, and window.host.schemaUtils are defined. This will prevent potential runtime errors if any of these objects are undefined.

packages/plugins/schema/src/Main.vue (2)

105-107: Implement History Stack Management for Undo/Redo Functionality

The history stack management (useHistory().addHistory()) is commented out with a TODO note. Implementing this is crucial for supporting undo and redo actions within the application.

Would you like assistance in implementing the history stack management? I can help draft the necessary code changes.


130-136: Ensure Proper Subscription Management in Lifecycle Hooks

In the onActivated hook, you subscribe to the schemaChange topic, and in onDeactivated, you unsubscribe. Confirm that these hooks are appropriate for your component's lifecycle, especially if it's wrapped with <keep-alive>. Ensure that subscriptions are managed correctly to prevent memory leaks or missed messages.

packages/configurator/src/html-attributes-configurator/HtmlAttributesConfigurator.vue (2)

98-117: Optimize Attribute Processing Logic

The current logic iterates over props and updates attrs by checking if keys are excluded. Consider refactoring this block to improve readability and efficiency. For example, filter the entries first and then map them to attrs, reducing the complexity of the loop.

Apply this diff to refactor the attribute processing logic:

+const filteredProps = Object.entries(useProperties().getSchema()?.props || {}).filter(
+  ([key]) => !properties.includes(key)
+)
+attrs.value = filteredProps.map(([key, value]) => ({
+  id: utils.guid(),
+  text: `${key} = '${value}'`,
+  data: { key, value }
+}))

Line range hint 127-143: Improve Schema Update Method by Avoiding Direct Schema Modification

Currently, updateSchema retrieves the schema multiple times and directly modifies it. Consider minimizing direct schema access and utilize operateNode consistently for updates.

Apply this diff to streamline the schema update process:

-const schema = useProperties().getSchema()
-operateNode({ type: 'updateAttributes', id: schema.id, value: { props: mergeProps } })
+operateNode({ type: 'updateAttributes', id: useProperties().getSchema().id, value: { props: mergeProps } })
packages/configurator/src/js-slot-configurator/JsSlotConfigurator.vue (2)

97-110: Clarify updateSlot Function Logic

The updateSlot function has been updated to handle slot binding and unbinding with additional parameters. Ensure that the logic for toggling slots and updating the slot data is clear and accounts for all edge cases.


157-167: Separate Parameter Validation from Slot Update Logic

In setParams, parameters are set only if the slot is bound. Ensure that parameter validation and setting are performed consistently, and consider separating concerns by validating parameters before updating the slot.

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

Line range hint 167-177: Ensure proper cleanup of event listeners added to window

In the run function, event listeners are added to window for 'mousedown' and 'dragenter' events. However, these listeners are not removed in the onUnmounted lifecycle hook, which can lead to memory leaks and unintended side effects. It's important to remove these listeners when the component is unmounted.

To fix this issue, add the corresponding window.removeEventListener calls in the onUnmounted hook:

onUnmounted(() => {
  if (iframe.value?.contentDocument) {
    removeHostkeyEvent(iframe.value.contentDocument)
  }
  window.removeEventListener('message', updateI18n, false)
+ window.removeEventListener('mousedown', windowMouseDownHandler)
+ window.removeEventListener('dragenter', windowDragEnterHandler)
})

You will need to define the event handler functions separately to enable removing them properly:

- window.addEventListener('mousedown', (e) => {
-   insertPosition.value = insertPanel.value?.contains(e.target)
-   target.value = e.target
- })
+ const windowMouseDownHandler = (e) => {
+   insertPosition.value = insertPanel.value?.contains(e.target)
+   target.value = e.target
+ }
+ window.addEventListener('mousedown', windowMouseDownHandler)

- window.addEventListener('dragenter', () => {
-   // 如果拖拽范围超出了iframe范围,则清空拖拽位置数据
-   clearLineState()
- })
+ const windowDragEnterHandler = () => {
+   // 如果拖拽范围超出了iframe范围,则清空拖拽位置数据
+   clearLineState()
+ }
+ window.addEventListener('dragenter', windowDragEnterHandler)
packages/configurator/src/variable-configurator/VariableConfigurator.vue (1)

Line range hint 402-418: Address potential issue with schema state update

The updateSchema function is called with a spread of pageSchema.state:

-updateSchema({ ...pageSchema.state, [stateName]: staticData })
+updateSchema({ state: { ...pageSchema.state, [stateName]: staticData } })

Passing { ...pageSchema.state, [stateName]: staticData } to updateSchema may unintentionally overwrite the entire schema with the state properties. To correctly update the state property without affecting other parts of the schema, wrap the state updates within the state key.

packages/configurator/src/table-columns-configurator/TableColumnsConfigurator.vue (1)

26-26: Architectural improvement with potential error handling enhancement

The change to use operateNode aligns with the PR's objective of providing a unified entry point for schema modifications. However, consider adding error handling for the operation.

-  useCanvas().operateNode({ type: 'updateAttributes', id: useProperties().getSchema().id, value: { children } })
+  try {
+    useCanvas().operateNode({ 
+      type: 'updateAttributes', 
+      id: useProperties().getSchema().id, 
+      value: { children } 
+    })
+  } catch (error) {
+    console.error('Failed to update table columns:', error)
+    // Consider adding user notification here
+  }
packages/settings/styles/src/js/useEditor.js (1)

18-18: Track TODO comment

The TODO comment about checking references should be tracked and addressed.

Would you like me to create a GitHub issue to track this TODO item? I can help search for references to determine if this code can be safely removed.

packages/toolbars/preview/src/Main.vue (1)

69-69: Consider strengthening error handling for getSchema

The optional chaining (?.) suggests getSchema might be undefined. Consider adding explicit error handling to provide better feedback when schema access fails.

-          schema: getSchema?.()
+          schema: getSchema?.() || {}
packages/configurator/src/html-text-configurator/HtmlTextConfigurator.vue (1)

56-61: Consider batching multiple attribute updates

The implementation correctly uses operateNode for schema updates. However, if there are cases where multiple attributes need to be updated simultaneously, consider batching them in a single operateNode call to reduce the number of update events.

 const change = (value) => {
   const { operateNode } = useCanvas()
   const schema = useProperties().getSchema()

   if (schema) {
-    operateNode({ type: 'updateAttributes', id: schema.id, value: { children: value } })
+    // If multiple attributes need updating, batch them:
+    operateNode({ 
+      type: 'updateAttributes', 
+      id: schema.id, 
+      value: { 
+        children: value,
+        // other attributes...
+      } 
+    })
   }
 }
packages/configurator/src/related-editor-configurator/RelatedEditorConfigurator.vue (1)

Line range hint 93-97: Consider caching schema lookups

While the transition to useCanvas().getSchema() is correct, consider caching the schema lookup result if it's used multiple times within the same execution context to improve performance.

 const getStateValue = (value) => {
   let newValue = value

   if (value?.type === CONSTANT.JSEXPRESSION) {
-    const pageSchema = useCanvas().getSchema()
+    // Cache schema lookup if used frequently
+    if (!this.cachedSchema) {
+      this.cachedSchema = useCanvas().getSchema()
+    }
+    const pageSchema = this.cachedSchema
     const stateName = value?.value?.replace(CONSTANT.STATE, '')
     newValue = pageSchema?.state?.[stateName]
   }

   return newValue
 }
packages/canvas/container/src/keyboard.js (1)

65-65: Consider error handling for schema retrieval

While the change to use useCanvas().getSchema() aligns with the new pattern, consider adding error handling for cases where schema retrieval fails.

-    schema = useCanvas().getSchema()
+    try {
+      schema = useCanvas().getSchema()
+    } catch (error) {
+      console.error('Failed to retrieve schema:', error)
+      return
+    }
packages/canvas/DesignCanvas/README.md (4)

17-17: Consider adding error handling guidance

The warning about node manipulation limitations is crucial. Consider adding examples of error cases and how to handle them to prevent runtime issues.


48-48: Fix markdown heading format

Remove trailing colons from headings to comply with markdown standards:

-### 获取当前页面/区块 schema:
+### 获取当前页面/区块 schema

-### 获取当前选中节点 schema:
+### 获取当前选中节点 schema

Also applies to: 56-56

🧰 Tools
🪛 Markdownlint (0.35.0)

48-48: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


179-179: Add type information for value property example

The example for updateAttributes operation should include more specific type information in the spread operator to better guide developers.

-  value: { props: { ... }, loop: { ... } },
+  value: { 
+    props: { /* INodeProps */ }, 
+    loop: { /* ILoopConfig */ } 
+  },

20-20: Consider versioning experimental APIs

Add version numbers or feature flags for experimental APIs to help track adoption and future stability changes.

packages/settings/props/src/composable/useProperties.js (2)

124-125: Consider optimizing props spread for large objects

For components with many props, spreading the entire props object could impact performance. Consider using Object.assign or selective property copying for better performance with large prop objects.

-  const newProps = { ...(properties.schema.props || {}) }
+  const newProps = Object.assign({}, properties.schema.props || {})

150-159: Consider consolidating duplicate code with setProp

The delProp function shares similar logic with setProp. Consider refactoring to reuse the logic.

-const delProp = (name) => {
-  const newProps = { ...(properties.schema.props || {}) }
-
-  delete newProps[name]
-
-  useCanvas().operateNode({
-    type: 'changeProps',
-    id: properties.schema.id,
-    value: { props: newProps },
-    option: { overwrite: true }
-  })
-  propsUpdateKey.value++
-}
+const delProp = (name) => setProp(name, null)
🧰 Tools
🪛 Biome (1.9.4)

[error] 157-158: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/toolbars/save/src/js/index.js (1)

Line range hint 156-186: Remove or implement commented code

The commented code block contains important functionality for retrieving original schema data. Consider either implementing this feature or removing the comments if they're no longer relevant.

packages/configurator/src/slot-configurator/SlotConfigurator.vue (1)

101-106: LGTM: Improved template removal logic

The change from splice to filter improves code clarity. The consistent use of operateNode maintains the unified schema modification approach.

Consider extracting the filter condition to a named function for better readability:

-const newChildren = schema.children.filter(
-  ({ componentName, props }) =>
-    componentName !== 'Template' || (props?.slot !== name && props?.slot?.name !== name)
-)
+const isNotMatchingTemplate = ({ componentName, props }) => 
+  componentName !== 'Template' || (props?.slot !== name && props?.slot?.name !== name)
+const newChildren = schema.children.filter(isNotMatchingTemplate)
packages/plugins/materials/src/composable/useResource.js (1)

Line range hint 124-153: LGTM: Well-structured app state fetching

The fetchAppState function provides a clean implementation for fetching and updating the application schema state, with proper fallbacks for interceptors.

Consider implementing a caching mechanism for the app schema to improve performance for frequently accessed data. This could be particularly beneficial for the dataSource and utils properties which might not change frequently.

packages/common/component/BlockLinkField.vue (1)

Line range hint 82-83: Consider refactoring getCurrent access pattern

The functions still use canvasApi.value?.getCurrent?.() while the rest of the code has moved to using direct methods from useCanvas. Consider refactoring for consistency with the new pattern.

- const {
-   schema: { id, componentName }
- } = canvasApi.value?.getCurrent?.() || {}
+ const {
+   schema: { id, componentName }
+ } = useCanvas().getCurrent?.() || {}

Also applies to: 108-109

packages/common/component/BlockLinkEvent.vue (1)

Line range hint 89-90: Consider consistent schema access pattern

Similar to BlockLinkField.vue, these functions use the old canvasApi.value?.getCurrent?.() pattern. Consider refactoring for consistency.

- const {
-   schema: { id, componentName }
- } = useCanvas().canvasApi.value?.getCurrent?.() || {}
+ const {
+   schema: { id, componentName }
+ } = useCanvas().getCurrent?.() || {}

Also applies to: 125-126

packages/canvas/DesignCanvas/src/DesignCanvas.vue (1)

135-149: LGTM! Improved node selection logic.

The changes enhance node selection by:

  1. Using dedicated methods like getNodeById
  2. Clearly separating canvas-level and node-level schema handling
  3. Maintaining a clean flow of data

Consider implementing a publish-subscribe pattern for node selection events to allow other components to react to selection changes without tight coupling.

packages/common/component/BlockDeployDialog.vue (1)

Line range hint 213-219: Consider improving the error message.

The change to use importSchema is good, but the error message could be more specific about the JSON parsing error.

Consider this improvement:

-          message: '代码静态检查有错误,请先修改后再保存'
+          message: `JSON 解析错误: ${err.message},请检查格式后重试`
packages/settings/styles/src/Main.vue (1)

138-152: Good implementation of centralized style updates.

The updateStyleToSchema method properly handles both regular schema updates and canvas page schema updates. However, there's a TODO comment that needs to be addressed.

The TODO comment on line 150 indicates a need to support style key removal. Would you like me to help implement this feature or create a GitHub issue to track it?

packages/plugins/block/src/composable/useBlock.js (1)

279-285: Good improvement in schema management!

The changes improve schema handling by:

  1. Using explicit getSchema() instead of accessing through canvasApi
  2. Ensuring proper initialization with await
  3. Maintaining a single source of truth for block schema

Consider adding error handling for the async operation.

Add error handling:

-  await resetBlockCanvasState({ pageSchema: getBlockPageSchema(block) })
-  block.content = getSchema()
+  try {
+    await resetBlockCanvasState({ pageSchema: getBlockPageSchema(block) })
+    block.content = getSchema()
+  } catch (error) {
+    console.error('Failed to initialize block schema:', error)
+    throw error
+  }
packages/canvas/render/src/RenderMain.js (2)

357-364: Consider making the throttle interval configurable in throttleUpdateSchema

The throttle interval for throttleUpdateSchema is hardcoded to 100 milliseconds. Making this interval configurable would provide flexibility to adjust the frequency of schema updates based on performance needs.

Proposed change:

+const THROTTLE_INTERVAL = 100; // You can adjust this value as needed

const throttleUpdateSchema = useThrottleFn(
  () => {
    window.host.patchLatestSchema(schema)
  },
-  100,
+  THROTTLE_INTERVAL,
  true
)

390-415: Assess performance implications of deep watchers on large objects

Using deep watchers on schema.methods, schema.css, and schema.state can have performance overhead, especially if these objects are large or change frequently. Consider optimizing the watchers or restructuring data to minimize performance impact.

Possible approaches:

  • Use shallow watchers if deep observation isn't necessary.
  • Debounce or throttle the callbacks to reduce frequent executions.
  • Split large objects into smaller, more manageable parts and watch them individually.
packages/settings/styles/src/js/useStyle.js (1)

133-173: Good refactoring of style panel watch initialization.

The centralization of watch setup logic improves code organization and maintainability. The deep watch on schema changes ensures proper reactivity.

Consider adding error boundaries around the schema retrieval:

- let schema = getCurrentSchema()
+ let schema
+ try {
+   schema = getCurrentSchema()
+ } catch (error) {
+   console.error('Failed to get current schema:', error)
+   return
+ }
packages/canvas/render/src/builtin/CanvasCollection.js (4)

Line range hint 104-112: Consider improving robustness and immutability

While the function works, consider these improvements:

  1. Add input validation
  2. Return a new array instead of mutating the input
  3. Use deep cloning for nested objects if needed
-const generateAssignColumns = (newColumns, oldColumns) => {
+const generateAssignColumns = (newColumns, oldColumns) => {
+  if (!Array.isArray(newColumns) || !Array.isArray(oldColumns)) {
+    return newColumns;
+  }
+  return newColumns.map((item) => {
+    const targetColumn = oldColumns.find((value) => value.field === item.field);
+    return targetColumn ? { ...item, ...targetColumn } : item;
+  });
-  newColumns.forEach((item) => {
-    const targetColumn = oldColumns.find((value) => value.field === item.field)
-    if (targetColumn) {
-      Object.assign(item, targetColumn)
-    }
-  })
-  return newColumns
}

Line range hint 137-183: Extract pagination configuration to constants

The pagination configuration is hardcoded. Consider extracting it to a configurable constant or making it customizable through props.

+const DEFAULT_PAGER_CONFIG = {
+  attrs: {
+    currentPage: 1,
+    pageSize: 50,
+    pageSizes: [10, 20, 50],
+    total: 0,
+    layout: 'sizes,total, prev, pager, next, jumper'
+  }
+};

-  node.props.pager = pageConfig
+  node.props.pager = { ...DEFAULT_PAGER_CONFIG }
🧰 Tools
🪛 Biome (1.9.4)

[error] 146-148: Avoid the delete operator which can impact performance.

(lint/performance/noDelete)


Line range hint 185-205: Enhance cleanup in clearBindVar

The cleanup function only removes the method but should also clean up any related props or state.

    const clearBindVar = () => {
      // 当数据源组件children字段为空时,及时清空创建的methods
      delete pageSchema.methods[methodName]
+     // Clean up related props
+     if (node.props) {
+       delete node.props.fetchData;
+       delete node.props.pager;
+     }
    }

281-283: Improve readability of handler selection

Consider restructuring the ternary operation for better readability.

-export const getHandler = ({ node, sourceRef, schemaId, pageSchema, updateKey }) =>
-  extraHandlerMap[node.componentName]
-    ? extraHandlerMap[node.componentName]({ node, sourceRef, schemaId, pageSchema, updateKey })
-    : defaultHandlerTemplate({ node, sourceRef, schemaId, pageSchema })
+export const getHandler = ({ node, sourceRef, schemaId, pageSchema, updateKey }) => {
+  const handler = extraHandlerMap[node.componentName];
+  const params = { node, sourceRef, schemaId, pageSchema, updateKey };
+  
+  return handler ? handler(params) : defaultHandlerTemplate(params);
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between dee65eb and c777b18.

📒 Files selected for processing (60)
  • designer-demo/public/mock/bundle.json (52 hunks)
  • packages/canvas/DesignCanvas/README.md (1 hunks)
  • packages/canvas/DesignCanvas/src/DesignCanvas.vue (4 hunks)
  • packages/canvas/DesignCanvas/src/api/useCanvas.js (6 hunks)
  • packages/canvas/container/src/CanvasContainer.vue (2 hunks)
  • packages/canvas/container/src/components/CanvasAction.vue (2 hunks)
  • packages/canvas/container/src/container.js (12 hunks)
  • packages/canvas/container/src/keyboard.js (2 hunks)
  • packages/canvas/package.json (1 hunks)
  • packages/canvas/render/src/RenderMain.js (9 hunks)
  • packages/canvas/render/src/builtin/CanvasCollection.js (5 hunks)
  • packages/canvas/render/src/builtin/CanvasCollection.vue (5 hunks)
  • packages/canvas/render/src/context.js (0 hunks)
  • packages/canvas/render/src/render.js (4 hunks)
  • packages/common/component/BlockDeployDialog.vue (2 hunks)
  • packages/common/component/BlockLinkEvent.vue (1 hunks)
  • packages/common/component/BlockLinkField.vue (1 hunks)
  • packages/common/component/ConfigItem.vue (2 hunks)
  • packages/common/component/LifeCycles.vue (2 hunks)
  • packages/common/component/MetaListItems.vue (2 hunks)
  • packages/common/js/canvas.js (1 hunks)
  • packages/common/js/completion.js (1 hunks)
  • packages/configurator/src/collection-configurator/CollectionConfigurator.vue (2 hunks)
  • packages/configurator/src/container-configurator/ContainerConfigurator.vue (3 hunks)
  • packages/configurator/src/html-attributes-configurator/HtmlAttributesConfigurator.vue (4 hunks)
  • packages/configurator/src/html-text-configurator/HtmlTextConfigurator.vue (2 hunks)
  • packages/configurator/src/js-slot-configurator/JsSlotConfigurator.vue (4 hunks)
  • packages/configurator/src/layout-grid-configurator/LayoutGridConfigurator.vue (3 hunks)
  • packages/configurator/src/related-editor-configurator/RelatedEditorConfigurator.vue (1 hunks)
  • packages/configurator/src/slot-configurator/SlotConfigurator.vue (3 hunks)
  • packages/configurator/src/table-columns-configurator/TableColumnsConfigurator.vue (1 hunks)
  • packages/configurator/src/variable-configurator/VariableConfigurator.vue (7 hunks)
  • packages/plugins/block/src/composable/useBlock.js (1 hunks)
  • packages/plugins/bridge/src/js/resource.js (4 hunks)
  • packages/plugins/datasource/src/DataSourceGlobalDataHandler.vue (2 hunks)
  • packages/plugins/datasource/src/DataSourceList.vue (4 hunks)
  • packages/plugins/datasource/src/DataSourceRecordList.vue (3 hunks)
  • packages/plugins/datasource/src/js/datasource.js (1 hunks)
  • packages/plugins/i18n/src/composable/useTranslate.js (2 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (1 hunks)
  • packages/plugins/materials/src/composable/useResource.js (6 hunks)
  • packages/plugins/schema/package.json (1 hunks)
  • packages/plugins/schema/src/Main.vue (4 hunks)
  • packages/plugins/script/src/js/method.js (4 hunks)
  • packages/plugins/state/src/DataSourceList.vue (1 hunks)
  • packages/plugins/state/src/Main.vue (8 hunks)
  • packages/plugins/tree/src/Main.vue (2 hunks)
  • packages/settings/events/src/components/AdvanceConfig.vue (2 hunks)
  • packages/settings/props/src/components/Empty.vue (2 hunks)
  • packages/settings/props/src/components/modal/ModalContent.vue (2 hunks)
  • packages/settings/props/src/composable/useProperties.js (2 hunks)
  • packages/settings/styles/src/Main.vue (1 hunks)
  • packages/settings/styles/src/components/classNamesContainer/index.vue (3 hunks)
  • packages/settings/styles/src/js/useEditor.js (2 hunks)
  • packages/settings/styles/src/js/useStyle.js (5 hunks)
  • packages/toolbars/generate-code/src/Main.vue (1 hunks)
  • packages/toolbars/preview/src/Main.vue (2 hunks)
  • packages/toolbars/redoundo/src/composable/useHistory.js (2 hunks)
  • packages/toolbars/save/src/js/index.js (2 hunks)
  • packages/utils/src/constants/index.js (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/canvas/render/src/context.js
✅ Files skipped from review due to trivial changes (1)
  • designer-demo/public/mock/bundle.json
👮 Files not reviewed due to content moderation or server errors (3)
  • packages/plugins/datasource/src/DataSourceGlobalDataHandler.vue
  • packages/plugins/datasource/src/DataSourceList.vue
  • packages/plugins/datasource/src/DataSourceRecordList.vue
🧰 Additional context used
🪛 Biome (1.9.4)
packages/settings/props/src/composable/useProperties.js

[error] 157-158: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/plugins/script/src/js/method.js

[error] 121-122: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🪛 Markdownlint (0.35.0)
packages/canvas/DesignCanvas/README.md

48-48: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


56-56: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)

🔇 Additional comments (77)
packages/canvas/container/src/components/CanvasAction.vue (2)

130-130: LGTM: Import changes align with schema management objectives

The addition of useCanvas hook aligns with the PR's goal of providing a unified entry point for schema operations.


Line range hint 516-704: LGTM: Well-structured styling with proper theming support

The styling implementation:

  • Uses CSS variables for consistent theming
  • Handles complex positioning and resize interactions
  • Maintains clear separation of concerns
packages/plugins/state/src/DataSourceList.vue (1)

71-71: Verify the migration from resState to appSchemaState

The change aligns with the PR's objective of consolidating schema state management. However, we should ensure that appSchemaState.pageTree is always available when this code executes.

Let's verify the availability and initialization of appSchemaState.pageTree:

✅ Verification successful

The schema state access is properly initialized and safe to use

The verification shows that appSchemaState.pageTree is:

  • Properly initialized in useResource composable during app schema fetch
  • Set from appData.componentsTree which is retrieved via HTTP API
  • Used consistently across multiple components
  • Accessed safely through the useResource() hook which is properly imported

The migration from resState to appSchemaState is correctly implemented and the state is guaranteed to be available when the component loads.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for appSchemaState initialization and usage patterns
rg -A 3 "appSchemaState\s*=|appSchemaState:" packages/

# Search for pageTree initialization
ast-grep --pattern 'appSchemaState.pageTree = $_'

Length of output: 529


Script:

#!/bin/bash
# Let's verify the initialization flow and usage
rg -B 2 -A 5 "appSchemaState.pageTree = appData.componentsTree" packages/

# Check where useResource is imported and used
rg "useResource" packages/

# Check the structure and initialization of appData
rg "appData\s*=|appData:" packages/

Length of output: 7974

packages/plugins/datasource/src/js/datasource.js (2)

57-58: Verify dataHandler availability in appSchemaState

The change from resState to appSchemaState aligns with the PR's objective of unifying schema operations. However, we should verify that dataHandler is consistently available in appSchemaState.

✅ Verification successful

The search results provide sufficient context to generate a final response. The findings show that:

  1. appSchemaState.dataHandler is properly initialized in useResource.js with a fallback to DEFAULT_INTERCEPTOR.dataHandler
  2. It's used consistently in datasource.js for request handling
  3. It's properly updated in DataSourceGlobalDataHandler.vue with the correct structure { type: 'JSFunction', value: ... }

The change to use appSchemaState.dataHandler is verified and safe

The dataHandler is consistently available in appSchemaState with proper initialization, fallback values, and correct structure. The change aligns with the unified schema management approach while maintaining the expected handler functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other occurrences of dataHandler in appSchemaState
rg "appSchemaState.dataHandler" -A 2 -B 2

Length of output: 1965


61-62: Ensure consistent error handling across state transitions

The migration to appSchemaState for error handling is consistent with the overall changes. However, this is a critical path for error handling.

✅ Verification successful

Let me gather more information about the error handler usage across the codebase.


Based on the analysis of the shell script results, I can now provide the final response:

The error handler migration to appSchemaState is properly implemented and consistent

The error handling implementation follows the established pattern across the codebase:

  • Global error handler is properly initialized from appSchemaState with fallback to DEFAULT_INTERCEPTOR
  • Error handling is consistently applied in HTTP interceptors for both requests and responses
  • The error handler state is properly managed in the Vue components and data source configurations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify error handler initialization and usage
rg "errorHandler.*appSchemaState" -A 2 -B 2

# Check for any error handling patterns that might need updating
ast-grep --pattern 'errorHandler = $$$'

Length of output: 633


Script:

#!/bin/bash
# Search for error handler usage patterns
rg "errorHandler" -A 3 -B 3

# Look for error handling function definitions
ast-grep --pattern 'function $_(error) {
  $$$
}'

# Check Vue component error handling
ast-grep --pattern 'onError($$$) {
  $$$
}'

Length of output: 36202

packages/common/component/LifeCycles.vue (2)

Line range hint 135-139: LGTM! Simplified schema access pattern.

The change aligns with the PR's objective of providing a unified schema modification entry point. The new getSchema() method provides a cleaner and more direct way to access the schema.

Let's verify that the new schema access pattern is consistently implemented:

✅ Verification successful

Based on the comprehensive search results, I can now generate the final response:

LGTM! The schema access pattern is consistently implemented across the codebase.

The change from useCanvas().canvasApi.value?.getSchema?.() to useCanvas().getSchema() aligns with the codebase's unified schema access pattern. The search results confirm that:

  • The new getSchema() method is properly implemented in the canvas API (packages/canvas/DesignCanvas/src/api/useCanvas.js)
  • All other components consistently use this pattern for schema access
  • No instances of the old pattern (canvasApi.value?.getSchema) remain in the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining instances of the old pattern and verify the new pattern exists
rg "canvasApi\.value\?\.getSchema" 
ast-grep --pattern 'useCanvas().getSchema()'

Length of output: 258


Script:

#!/bin/bash
# Let's verify if there are any other variations of schema access patterns
rg "getSchema" -A 2

Length of output: 31604


Line range hint 134-165: Verify the impact of breaking changes on existing applications.

Since this PR introduces breaking changes in schema access patterns, we should verify that existing applications using this component continue to work as expected, particularly:

  1. Custom lifecycle implementations
  2. External components that might depend on the old schema access pattern
  3. Applications that might have extended this component's functionality

Let's check for potential impacts:

✅ Verification successful

Breaking changes appear to have minimal impact on dependent components

Based on the codebase analysis, the changes to lifecycle management appear to be safe because:

  1. PageSetting.vue and BlockSetting.vue only interact with LifeCycles component through the bindLifeCycles prop and don't directly access schema patterns
  2. The schema access pattern change is internal to the LifeCycles component and doesn't affect its public API
  3. No custom implementations or extensions of the LifeCycles component were found
  4. The existing components use their own lifecycle storage mechanisms (page_content.lifeCycles and content.lifeCycles) which remain unchanged
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for components that might be affected by the breaking changes
rg -l "lifeCycles.*getSchema" 

# Look for potential custom implementations or extensions
ast-grep --pattern 'extends LifeCycles'

Length of output: 73


Script:

#!/bin/bash
# Let's try a broader search to find lifecycle-related patterns
rg -l "lifeCycles" --type vue --type js --type ts

# Search for schema access patterns
rg -l "getSchema()" --type vue --type js --type ts

# Look for potential imports of LifeCycles
rg -l "import.*LifeCycles"

Length of output: 273


Script:

#!/bin/bash
# Let's examine the content of files that import LifeCycles
rg "lifeCycles" packages/plugins/page/src/PageSetting.vue -A 5 -B 5
rg "lifeCycles" packages/plugins/block/src/BlockSetting.vue -A 5 -B 5

# Let's also check for any usage patterns without file type restrictions
rg "getSchema.*lifeCycles" -A 3

Length of output: 2259

packages/configurator/src/container-configurator/ContainerConfigurator.vue (1)

42-65: Initialize child nodes with default properties if missing

The updateChildrenToValid function ensures that each child node has props with default values. This prevents potential errors caused by missing properties and maintains schema integrity.

packages/canvas/render/src/builtin/CanvasCollection.vue (2)

2-2: Ensure Proper Management of updateKey for Component Re-rendering

The :key="updateKey" prop has been added to the <component> to trigger re-renders when updateKey changes. Verify that updateKey is incremented appropriately to prevent unnecessary re-renders or missed updates.


108-111: Confirm Message Publication and Update of updateKey

After publishing the schemaChange message, updateKey.value is incremented to trigger a re-render. Ensure that subscribers to the schemaChange topic are handling the message correctly and that incrementing updateKey synchronizes the component state as intended.

packages/plugins/schema/src/Main.vue (1)

116-123: Verify Throttle Function Parameters in throttleUpdateData

The useThrottleFn is used with parameters (callback, 100, true). Confirm that the third parameter true corresponds to the trailing option and that this behavior aligns with the expected update strategy. Adjust the options if you intend to control leading and trailing executions differently.

packages/configurator/src/js-slot-configurator/JsSlotConfigurator.vue (1)

170-173: Ensure Correct Validation Call in validParams Function

In the validParams function, after validation succeeds, setParams is called. Verify that this callback correctly updates the slot parameters and that validation errors are handled appropriately.

packages/plugins/tree/src/Main.vue (1)

121-141: LGTM!

The implementation of message subscription and unsubscription in the onActivated and onDeactivated hooks is correct. The component properly updates pageSchema upon schema changes, ensuring the outline tree stays synchronized.

packages/settings/styles/src/components/classNamesContainer/index.vue (5)

114-114: Addition of setProp enhances property management

The inclusion of setProp from useProperties allows for more streamlined updates to properties within the component. This change improves code clarity and functionality.


174-180: Proper use of setProp within conditional schema handling

The code correctly checks for the existence of schema and utilizes setProp to update properties when schema is present. Incrementing propsUpdateKey ensures reactivity.


183-186: Fallback to page schema when component schema is unavailable

When schema is not present, the code appropriately retrieves the pageSchema and updates it using updateSchema. The spread operator is used correctly to merge existing properties with the new value.


192-192: Consistent retrieval of getCanvasPageSchema

Extracting getCanvasPageSchema from useCanvas aligns with the pattern used elsewhere in the code, ensuring consistency in schema retrieval.


450-453: Ensure that canvasApi.value is defined before using updateRect

Accessing updateRect from useCanvas().canvasApi.value may lead to errors if canvasApi.value is undefined. Please verify that canvasApi.value is always initialized before calling updateRect.

packages/common/component/ConfigItem.vue (5)

268-268: Included getSchema improves access to current schema

Adding getSchema from useProperties within updateValue facilitates direct access to the current schema, enhancing the function’s capability to manipulate component properties.


272-272: Safely retrieve componentName from the current schema

Using getSchema().componentName to obtain the current component's name is appropriate. Ensure that getSchema() reliably returns a valid schema to prevent potential runtime errors.


285-286: Integration of operateNode and isSaved improves node operations

Importing operateNode and isSaved from useCanvas enhances node manipulation capabilities and aligns with the application’s canvas management strategy.


288-289: Correct usage of getSchema for handling 'children' property

Retrieving the schema using getSchema() when updating the 'children' property ensures that the operation targets the correct node, maintaining data integrity.


291-291: Appropriate condition check before updating properties

The condition ensures that updates occur only when the canvas is saved and the page status is appropriate, preventing unintended changes during restricted states.

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

22-23: Updated imports reflect necessary dependencies

Importing useCanvas, useLayout, useTranslate, and useMaterial brings in essential functions for canvas operations. Including utils from @opentiny/tiny-engine-utils is appropriate for utility functions like guid().


76-76: Refactored getSchema function aligns with new API

Changing getSchema to use useCanvas().getPageSchema() centralizes schema retrieval and adheres to the updated canvas API structure.


247-257: Refactored node insertion using operateNode

The insertAfter function now uses useCanvas().operateNode to insert nodes, which standardizes node operations and improves code maintainability. Generating a unique ID when data.id is not provided ensures each node is identifiable.


287-291: Simplified node deletion with operateNode

Using useCanvas().operateNode to delete nodes in removeNode function streamlines the deletion process and maintains consistency with other node operations.


425-431: Accurate parent retrieval in ancestor check

Fetching the parent node using useCanvas().getNodeWithParentById in the isAncestor function accurately determines the ancestral relationship between nodes, ensuring correct insertion logic.


649-651: Enhanced node selection using updated API

In selectNode, retrieving the node and parent with useCanvas().getNodeWithParentById improves reliability. Emitting the 'selected' event with relevant data keeps the application state synchronized.

Also applies to: 654-654, 664-664


711-712: Consistent node copying functionality

The copyNode function correctly uses useCanvas().getNodeWithParentById to obtain the node and parent before inserting a copy, ensuring the copied node is accurately placed in the hierarchy.

packages/configurator/src/variable-configurator/VariableConfigurator.vue (5)

154-162: Refactored node access with useCanvas improves robustness

Updating the retrieval of getCurrent and getNodeWithParentById through useCanvas enhances the consistency of accessing canvas state and node hierarchy, leading to more reliable functionality.


453-453: Safely accessing state variables from the schema

Retrieving state variables using useCanvas().getSchema()?.state || {} ensures that even if getSchema() returns undefined, state.variables will default to an empty object, preventing potential runtime errors.


Line range hint 461-477: Dynamic variable selection logic enhanced

Using useCanvas().getSchema() within selectItem method streamlines access to properties and state variables, improving the flexibility of variable binding functionality.


470-470: Accessing application schema state using useResource

Fetching resources from useResource().appSchemaState accurately retrieves the necessary application state, supporting variable binding and dynamic configurations.

Also applies to: 505-505


526-526: Correctly obtaining variables based on selected item

Assigning state.variables = getSchema()?.[item.id] appropriately updates the variable list based on the user's selection, ensuring the correct variables are presented for binding.

packages/settings/props/src/components/Empty.vue (2)

6-7: Architectural improvement: Migration to useCanvas

The change from useBroadcastChannel to useCanvas aligns well with the PR's objective of creating a unified entry point for schema operations.


23-28: Consider potential race condition in watch setup

The watch setup might miss initial schema updates if getSchema() is not immediately available. Consider using watchEffect or adding immediate: true option to ensure proper initialization.

 watch(
   () => getSchema()?.children?.length,
   (len) => {
     tipsDesc.value = len ? EMPTY_SELECTION : EMPTY_COMPONENT
-  }
+  },
+  { immediate: true }
 )
packages/plugins/schema/package.json (1)

31-31: Verify @vueuse/core version compatibility and security

The addition of @vueuse/core is appropriate for the new functionality. However, let's verify the version compatibility and check for any known security issues.

packages/common/js/canvas.js (1)

19-19: Verify the state transition from resState to appSchemaState

The change from resState.isDemo to appSchemaState.isDemo aligns with the PR's objective of unifying schema management. However, we should ensure this transition doesn't affect existing demo mode functionality.

✅ Verification successful

State transition from resState to appSchemaState is properly implemented

The verification shows that:

  • The isDemo state is correctly migrated from resState to appSchemaState
  • The source of truth for isDemo is properly set in useResource.js from appData.meta?.is_demo
  • The demo mode functionality in canvas.js remains intact with proper state management and conditional logic
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other occurrences of resState.isDemo to ensure complete migration
rg "resState\.isDemo" 

# Search for all usages of appSchemaState.isDemo to verify consistency
rg "appSchemaState\.isDemo"

Length of output: 238


Script:

#!/bin/bash
# Check the previous implementation to understand the migration
git show HEAD~1 packages/common/js/canvas.js

# Look for any other demo mode related state management
rg "is_demo|isDemo" -A 2

Length of output: 5469

packages/canvas/package.json (1)

47-49: Review dependency versions and security implications

The addition of diff-match-patch and jsondiffpatch supports the schema change tracking functionality. However:

  1. Consider pinning exact versions for better reproducibility
  2. Verify these versions for known vulnerabilities
✅ Verification successful

Dependencies are secure, but consider version pinning

No security vulnerabilities found in either diff-match-patch@^1.0.5 or jsondiffpatch@^0.6.0. While the caret ranges allow for minor updates, the current versions are safe to use. However, for better build reproducibility in a monorepo context, consider pinning exact versions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for known vulnerabilities in the new dependencies
gh api graphql -f query='
{
  securityVulnerabilities(first: 10, ecosystem: NPM, package: "diff-match-patch") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
    }
  }
}
'

gh api graphql -f query='
{
  securityVulnerabilities(first: 10, ecosystem: NPM, package: "jsondiffpatch") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
    }
  }
}
'

Length of output: 589

packages/toolbars/preview/src/Main.vue (1)

31-31: LGTM: Improved method access pattern

The change from canvasApi to destructured methods aligns with the PR's goal of providing a unified entry point for schema operations.

packages/toolbars/redoundo/src/composable/useHistory.js (2)

98-98: LGTM: Consistent schema access pattern

The change to use useCanvas().getSchema() aligns with the unified schema access pattern.


64-64: Verify breaking change: setSchema → importSchema

This change from setSchema to importSchema is a breaking change that could affect existing applications. Please ensure all consumers of this API are updated accordingly.

✅ Verification successful

Migration from setSchema to importSchema is correct and safe

The change from setSchema to importSchema in the history implementation is correct and aligned with the codebase:

  • setSchema is only used internally in the render package and is not exposed as a public API
  • importSchema is the official public API method exposed through useCanvas() and is already being used across multiple components
  • The history functionality remains intact as both methods handle schema updates appropriately
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining uses of setSchema that might need updating
rg "setSchema" --type vue --type js

Length of output: 63


Script:

#!/bin/bash
# Search for any remaining uses of setSchema in all files
rg "setSchema"

# Also search for importSchema to understand the migration pattern
rg "importSchema"

Length of output: 861


Script:

#!/bin/bash
# Let's check the implementation of both methods to understand the migration
ast-grep --pattern 'const setSchema = $_' -l js
ast-grep --pattern 'const importSchema = $_' -l js

# Get the actual implementation
rg -A 10 "const setSchema = " packages/canvas/render/src/RenderMain.js
rg -A 10 "const importSchema = " packages/canvas/DesignCanvas/src/api/useCanvas.js

Length of output: 6777

packages/configurator/src/collection-configurator/CollectionConfigurator.vue (2)

14-16: LGTM: Improved import organization

The reorganization of imports improves code readability by grouping related imports together.


7-7: Verify icon functionality after rename

The icon component has been renamed from icon-conment-refresh to icon-common-refresh. While this appears to be a typo fix, please verify that:

  1. The icon still displays correctly
  2. The click handler still works as expected

Also applies to: 15-15, 22-22

✅ Verification successful

Let me analyze the component registration and usage patterns to verify the icon functionality. The previous command failed because the file type was not recognized, so I've modified the approach to search for both old and new naming patterns, and also look for component registrations.


Let me try a different approach by searching for icon-related content more broadly and examining the specific file content to understand the component usage and imports.


Icon component is correctly renamed and imported

The verification shows that the icon component is properly handled:

  1. The import statement correctly aliases iconConmentRefresh to iconCommonRefresh from @opentiny/vue-icon
  2. The component is properly registered as IconCommonRefresh in the components section
  3. The template uses the kebab-case version icon-common-refresh consistently
  4. The click handler @click="refreshData" is properly bound and the refreshData method is implemented
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining instances of the old icon name
rg "iconConment" --type vue --type js

Length of output: 65


Script:

#!/bin/bash
# Search for any instances of the old or new icon name patterns
rg -t js -t vue "icon-co[mn][mn]ent-refresh|iconCo[mn][mn]entRefresh"

# Search for icon component registration patterns
ast-grep --pattern 'components: {
  $$$
  iconCommonRefresh: $_,
  $$$
}'

Length of output: 175


Script:

#!/bin/bash
# Search for any files containing icon-related content
rg "icon.*refresh" -l

# Let's also check the specific file content
cat "packages/configurator/src/collection-configurator/CollectionConfigurator.vue"

# Check imports in the file
rg "import.*icon" "packages/configurator/src/collection-configurator/CollectionConfigurator.vue"

Length of output: 2974

packages/configurator/src/html-text-configurator/HtmlTextConfigurator.vue (2)

18-18: LGTM: Import aligns with new schema management approach

The addition of useCanvas import aligns with the PR's objective of providing a unified entry point for schema modifications.


47-52: Verify schema update propagation

The transition to using operateNode for component name updates aligns with the new schema management approach. However, we should verify that schema change events are properly propagated to dependent components like the outline tree.

✅ Verification successful

Let's gather more information about how schema changes are handled in the codebase, particularly focusing on the outline tree component.


Let's try a different approach to check the event handling and schema updates:


Schema updates are properly propagated through the event system

The verification shows that:

  • The outline tree (packages/plugins/tree/src/Main.vue) properly subscribes to 'schemaChange' events
  • When schema changes occur, the tree component updates its view using filterSchema(pageState.pageSchema)
  • The updateAttributes operation in useCanvas correctly handles attribute updates and triggers the necessary state changes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for schema change event subscriptions in the codebase
# This helps verify that components like the outline tree are properly listening for schema updates

rg -l "schemaChange|useCanvas\(\).subscribe" 

Length of output: 359


Script:

#!/bin/bash
# Check the implementation of schema change subscriptions in the tree plugin
ast-grep --pattern 'useCanvas().subscribe($$$)' packages/plugins/tree/src/Main.vue

# Also check how the schema plugin handles these updates
ast-grep --pattern 'useCanvas().subscribe($$$)' packages/plugins/schema/src/Main.vue

# Look for updateAttributes handler in useCanvas
ast-grep --pattern 'updateAttributes($$$)' packages/canvas/DesignCanvas/src/api/useCanvas.js

Length of output: 266


Script:

#!/bin/bash
# Check for event handling patterns in tree plugin
rg -A 5 "useCanvas\(\)" packages/plugins/tree/src/Main.vue

# Check schema update handling in useCanvas
rg -A 10 "updateAttributes" packages/canvas/DesignCanvas/src/api/useCanvas.js

# Look for schema change subscriptions with different pattern
rg -A 5 "subscribe\(" packages/plugins/tree/src/Main.vue

Length of output: 1879

packages/settings/props/src/components/modal/ModalContent.vue (2)

48-48: LGTM: State management transition

The transition from resState to appSchemaState aligns with the centralized schema management approach.


68-70: Verify i18n fallback behavior

The i18n handling logic has been updated to use appSchemaState. Ensure proper fallback behavior when translations are missing.

✅ Verification successful

Let me gather more information about the i18n fallback behavior by checking the implementation and usage patterns.


Based on the gathered information, I can now provide a final response about the i18n fallback behavior:

The i18n fallback implementation is correctly handled

The code follows the established i18n pattern across the codebase where:

  • Primary lookup is done via appSchemaState.langs[i18nKey][appSchemaState.currentLang]
  • Fallback to props.values.title[appSchemaState.currentLang] when the i18n key is not found
  • Further fallback to props.values.title for non-i18n values

This matches the i18n handling patterns found in other components like MetaListItems.vue and is properly integrated with the centralized i18n state management.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for i18n fallback handling patterns across the codebase
# This helps verify consistent i18n handling

rg -A 3 "langs\[.*?\]\[.*?currentLang" 

Length of output: 795


Script:

#!/bin/bash
# Check for appSchemaState type definitions and initialization
ast-grep --pattern 'interface $_State {
  $$$
  langs: $$$
  currentLang: $$$
  $$$
}'

# Check for other i18n related patterns
rg -A 3 "currentLang" --type vue

Length of output: 149


Script:

#!/bin/bash
# Check for i18n fallback patterns without file type restriction
rg -A 5 "currentLang"

# Check for appSchemaState initialization or definition
rg -A 5 "appSchemaState.*=|const.*appSchemaState"

# Look for any i18n related configuration files
fd -e json -e js -e ts "i18n|lang"

Length of output: 22953

packages/configurator/src/related-editor-configurator/RelatedEditorConfigurator.vue (1)

Line range hint 102-107: Verify event propagation chain

The saveValue function publishes messages for related components. Ensure the event propagation chain is complete and all subscribers receive updates.

✅ Verification successful

Let me gather more information about the message publishing and subscription system to verify the complete event propagation chain.


Based on the analysis of the message system implementation, I can now provide the final response:

Event propagation chain is properly implemented

The saveValue function in RelatedEditorConfigurator.vue correctly publishes messages using the established message system. The verification shows:

  • The component uses the standard useMessage hook from the meta-register package
  • Messages are published with the correct topic format lowcode.${componentName}.dataChange
  • The RelatedColumnsConfigurator component is properly subscribed to these messages, handling them in its onMounted lifecycle hook
  • The message system supports proper unsubscription on component unmount
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for message subscribers in the component chain
# This helps verify the complete event propagation path

# Look for subscribers to the lowcode.<component>.dataChange topic
rg "lowcode\..+\.dataChange" -A 3

Length of output: 953


Script:

#!/bin/bash
# Search for useMessage hook usage and message subscriptions
rg "useMessage|subscribe\(" -A 3

# Search for components that might be listening to these messages
ast-grep --pattern 'onMounted(() => {
  $$$
  subscribe($$$)
  $$$
})'

Length of output: 18381

packages/utils/src/constants/index.js (1)

92-92: Verify removal of SchemaLength from BROADCAST_CHANNEL

The removal of SchemaLength from BROADCAST_CHANNEL could impact existing code that relies on this broadcast channel.

✅ Verification successful

Removal of SchemaLength from BROADCAST_CHANNEL is safe

Based on the codebase analysis, there are no references to the SchemaLength broadcast channel. The existing BROADCAST_CHANNEL usage is limited to:

  • Notify: For error and warning notifications
  • CanvasLang: For language switching in canvas
  • PreviewLang: For language switching in preview mode
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to SchemaLength in broadcast channel usage
rg -l "SchemaLength|BROADCAST_CHANNEL\.SchemaLength" 

# Search for BroadcastChannel initialization or usage patterns
ast-grep --pattern 'new BroadcastChannel($$$)'

Length of output: 103


Script:

#!/bin/bash
# Search for any references to SchemaLength in a broader context
rg -i "schemalength" -A 3 -B 3

# Search for broadcast channel usage patterns more broadly
rg "BROADCAST_CHANNEL\." -A 3 -B 3

# Look for files that might use broadcast channels
rg "BroadcastChannel" -A 3 -B 3

Length of output: 10421

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

24-24: LGTM: Import of useCanvas aligns with new schema management approach

The addition of useCanvas import supports the unified schema modification entry point objective.

packages/common/component/MetaListItems.vue (1)

106-106: LGTM: Consistent state management variable renaming

The change from resState to appSchemaState aligns with the broader refactoring effort to unify schema state management across the application.

Also applies to: 162-162

packages/settings/props/src/composable/useProperties.js (1)

124-140: LGTM: Improved schema modification handling

The changes implement a more robust approach to property updates by:

  • Using immutable state updates
  • Centralizing schema modifications through operateNode
  • Adding proper handling of property removal
packages/toolbars/save/src/js/index.js (2)

67-75: LGTM: Improved canvas state management

The changes properly implement context-aware state resets using the unified canvas API.


97-97: 🛠️ Refactor suggestion

Consider enhancing error handling for schema operations

The destructured getSchema could be undefined if useCanvas() fails. Consider adding a null check.

-  const { isSaved, getSchema } = useCanvas()
+  const canvas = useCanvas()
+  if (!canvas) {
+    useNotify({
+      type: 'error',
+      message: 'Failed to initialize canvas'
+    })
+    return
+  }
+  const { isSaved, getSchema } = canvas
packages/configurator/src/slot-configurator/SlotConfigurator.vue (1)

88-92: LGTM: Schema update now uses the unified entry point

The change to use operateNode aligns with the PR's objective of providing a unified entry point for schema modifications. This approach is more maintainable and consistent.

Let's verify that all schema modifications use the new pattern:

packages/plugins/script/src/js/method.js (2)

79-84: LGTM: Well-structured method item creation

The method item creation follows a consistent pattern using the SCHEMA_DATA_TYPE constant, which improves type safety and maintainability.


119-124: LGTM: Consistent method creation with validation

The code properly validates method names before creation and maintains a consistent structure with the individual saveMethod function.

🧰 Tools
🪛 Biome (1.9.4)

[error] 121-122: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

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

155-162: LGTM: Clean resource fetching implementation

The refactored fetchResource function properly separates concerns by delegating app state fetching to fetchAppState and maintains a clear event-driven architecture.

packages/common/component/BlockLinkField.vue (1)

51-51: LGTM! Schema access refactoring looks good.

The change to use destructured getSchema from useCanvas aligns with the PR's objective of providing a unified entry point for schema operations.

Let's verify consistent usage of this pattern across the codebase:

Also applies to: 54-54

✅ Verification successful

Schema access refactoring is consistently implemented

The codebase shows consistent usage of the new pattern where getSchema is destructured from useCanvas(). There are no remaining instances of the old canvasApi.value.getSchema pattern, and all files are using the new unified schema access approach. The change in BlockLinkField.vue aligns perfectly with the established pattern across the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining direct schema access through canvasApi
# Expected: No instances of old pattern (canvasApi.value.getSchema)

rg "canvasApi\.value\.getSchema" --type vue

Length of output: 73


Script:

#!/bin/bash
# Search for any remaining direct schema access through canvasApi, considering .vue extension
rg "canvasApi\.value\.getSchema" -g "*.vue"

# Also search for the new pattern to verify consistent usage
rg "const.*getSchema.*=.*useCanvas" -g "*.vue"

# Let's also check for any other potential schema access patterns
rg "getSchema.*=.*canvasApi" -g "*.vue"

Length of output: 1889

packages/common/component/BlockLinkEvent.vue (1)

Line range hint 41-46: LGTM! Schema access and event list mapping look good.

The schema access pattern is consistent with the unified approach, and the event list mapping is clean and well-structured.

packages/canvas/DesignCanvas/src/DesignCanvas.vue (2)

33-34: LGTM! Enhanced messaging capabilities.

The addition of useMessage and META_SERVICE improves the component's messaging and service integration capabilities.


189-191: LGTM! Enhanced controller capabilities.

The addition of ast, useModal, and useMessage to the controller properly encapsulates these functionalities for use in canvas/render and internal components.

packages/plugins/i18n/src/composable/useTranslate.js (1)

Line range hint 119-137: LGTM! Verify schema state access across the application.

The change from resState to appSchemaState aligns with the PR's objective of unifying schema access. The implementation correctly uses optional chaining for safe property access.

Let's verify consistent usage across the codebase:

✅ Verification successful

Let me check the remaining resState usage to ensure it's not related to our current changes.


LGTM! The schema state access is consistent across the application.

The verification shows that:

  1. The remaining resState usage in packages/vue-generator/src/generator/vue/sfc/generateScript.js is unrelated to our changes as it's in a different context.
  2. All instances of appSchemaState.langs access follow the same pattern with proper optional chaining.
  3. The useResource hook is consistently imported and used across the codebase for accessing the schema state.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining instances of resState that might need updating
# and verify consistent usage of appSchemaState

# Search for remaining resState usage
echo "Checking for remaining resState usage..."
rg "resState" -l

# Verify consistent appSchemaState.langs access pattern
echo "Verifying consistent appSchemaState.langs access..."
rg "appSchemaState\\.langs" -A 2

Length of output: 1512


Script:

#!/bin/bash
# Check the context of resState usage in the remaining file
echo "Checking resState usage context..."
rg "resState" -A 2 packages/vue-generator/src/generator/vue/sfc/generateScript.js

# Also verify the useResource import and usage pattern
echo "Checking useResource usage..."
rg "useResource" -A 2

Length of output: 13803

packages/common/js/completion.js (1)

74-74: LGTM! Consistent schema state access.

The change maintains consistency with the unified schema access pattern and properly handles defaults for destructured properties.

packages/common/component/BlockDeployDialog.vue (1)

Line range hint 180-188: LGTM! Improved schema access pattern.

The change simplifies schema access by using the unified getSchema method, with proper null checking.

packages/configurator/src/layout-grid-configurator/LayoutGridConfigurator.vue (2)

39-39: LGTM: Import statement updated to use unified schema management.

The addition of useCanvas import aligns with the PR's objective of providing a unified entry point for schema modifications.


198-209: Good addition of error handling for schema operations.

The code properly checks for schema existence before proceeding with operations, preventing potential runtime errors.

Let's verify the schema error handling across the codebase:

✅ Verification successful

Schema error handling is consistently implemented across the codebase

The error handling pattern for schema validation in LayoutGridConfigurator.vue aligns with the codebase's overall approach. The search results show consistent schema validation across multiple components:

  • Similar null checks (if (!schema)) are implemented in various components like:
    • useHistory.js
    • useStyle.js
    • useProperties.js
    • AdvanceConfig.vue
    • CanvasMenu.vue

The error handling includes appropriate fallbacks, early returns, and error messages where necessary, making this implementation a good fit with the existing patterns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar error handling patterns in schema operations
ast-grep --pattern 'if (!$schema) {
  $$$
  return
}'

Length of output: 55


Script:

#!/bin/bash
# Let's search for schema-related error handling patterns with more variations
rg -U "if.*!schema.*\{[\s\S]*?return" -g "*.{vue,js,ts,jsx,tsx}"

# Also search for console.error messages related to schema
rg "console.error.*schema" -g "*.{vue,js,ts,jsx,tsx}"

# Search for schema validation or checks
rg "(!schema|schema\s*===\s*null|schema\s*==\s*null|schema\s*===\s*undefined|schema\s*==\s*undefined)" -g "*.{vue,js,ts,jsx,tsx}"

Length of output: 21075

packages/toolbars/generate-code/src/Main.vue (1)

54-54: LGTM: Simplified schema retrieval using useCanvas.

The change aligns with the PR's objective of providing a unified entry point for schema access.

packages/canvas/render/src/render.js (3)

662-665: Good fix for empty children handling!

Returning null instead of an empty array for empty children prevents components from misinterpreting the absence of children as custom slots. This is particularly important for components like TinyTree.


726-732: LGTM! Consistent empty children handling.

The additional check to convert empty arrays to null in the render function maintains consistency with the getChildren implementation, providing a robust solution for handling empty children throughout the rendering pipeline.


Line range hint 620-673: LGTM! Verify parent-child relationships.

The removal of the parent parameter streamlines the rendering logic. The function maintains its core responsibilities while reducing parameter coupling.

Let's verify that parent-child relationships are still properly maintained:

✅ Verification successful

Based on the search results, I can now generate the final response:

Parent-child relationships are properly maintained without the parent parameter

The removal of the parent parameter from renderGroup is safe because:

  • Parent-child relationships are managed through the schema tree structure itself, not through an explicit parent parameter
  • The component hierarchy is maintained through:
    • The nested structure of children arrays in the schema
    • Scope inheritance via mergeScope for proper data context
    • Slot rendering logic that correctly handles parent context when needed (e.g., for Block components)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential parent-child relationship issues
# Look for instances where parent context might be needed

# Search for parent references in component rendering
ast-grep --pattern 'parent: $_'

# Search for parent-dependent logic
rg -A 3 'parent\??\.' 

Length of output: 12785

packages/canvas/render/src/RenderMain.js (1)

471-484: Verify that removed methods are not used elsewhere in the codebase

Several methods have been removed from the api export, including:

  • updateUtils
  • deleteUtils
  • getBridge
  • setBridge
  • getMethods
  • getSchema
  • setSchema
  • getState
  • deleteState
  • setState
  • getProps
  • setProps
  • getContext
  • getNode
  • getRoot
  • setNode
  • getGlobalState

Ensure that these methods are not referenced elsewhere in the codebase to prevent runtime errors.

Run the following script to check for usages of the removed methods:

This script uses rg (ripgrep) to recursively search for occurrences of the removed methods, excluding the .git and node_modules directories.

packages/settings/styles/src/js/useStyle.js (2)

334-341: Good addition of initialization guard.

The hasWatchInitialized flag effectively prevents multiple initializations of watchers, avoiding potential memory leaks and performance issues.


190-257: Verify selector list generation logic.

The watch implementation for selector list generation is complex and might have edge cases.

✅ Verification successful

Let me gather more information about the selector list generation logic by examining the related functions and dependencies.


Let me check one more aspect to understand the complete flow of selector handling, particularly the getSelectorArr function and any validation logic.


Let me check the implementation of the parser module since it contains the critical getSelectorArr function.


After analyzing the implementation, I can now provide a final response about the selector list generation logic.

The selector list generation implementation is robust and well-handled

The implementation correctly handles:

  • Individual class and ID selectors with proper prefix mapping (. and #)
  • Combination selectors (e.g., .class1.class2) through proper parsing in getSelectorArr
  • Mouse states (hover, pressed, focused, disabled)
  • Input validation and edge cases:
    • Empty or invalid selectors
    • Unsupported selector types (attribute selectors, combinators)
    • Null/undefined checks

The watch implementation follows a clear logic:

  • Builds options from current class and ID lists
  • Validates combination selectors against existing classes/IDs
  • Maintains proper state synchronization with default selections
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for potential issues with selector list generation
# Test: Look for similar patterns in other style-related files
rg -A 5 "selectorOptionLists.*=" 

Length of output: 1711


Script:

#!/bin/bash
# Check for getSelectorArr function implementation and usage
ast-grep --pattern 'function getSelectorArr($_) {
  $$$
}'

# Look for other watchers or computed properties related to selectors
rg -A 5 "watch\(\(\) => \[.*selector" 

# Check for any other usage of currentClassNameList and currentIdList
rg "currentClassNameList|currentIdList" -A 3

Length of output: 2443


Script:

#!/bin/bash
# Look for getSelectorArr implementation and selector validation
rg "getSelectorArr" -B 2 -A 5

# Check for any selector-related utility functions
ast-grep --pattern 'function $_(selector) {
  $$$
}'

# Look for any selector validation or error handling
rg "selector.*valid|valid.*selector" -A 3

Length of output: 3954


Script:

#!/bin/bash
# Check the complete implementation of parser.js
cat packages/settings/styles/src/js/parser.js

# Look for any error handling or validation in the parser
rg "throw|error|warning" packages/settings/styles/src/js/parser.js -A 3

Length of output: 5684

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

369-369: Documentation update reflects state management changes.

The comment update from 'resState' to 'appSchemaState' indicates a shift in state management approach. This change should be part of a broader refactoring effort.

✅ Verification successful

Documentation update is consistent with state management implementation.

Based on the search results, the change from 'resState' to 'appSchemaState' is consistent with the actual implementation in the codebase. The codebase shows extensive usage of appSchemaState across multiple files, particularly in the materials plugin and other components. There are no remaining references to resState.thirdPartyDeps, confirming this was a proper documentation update to match the actual state management approach being used.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify consistent usage of appSchemaState across the codebase
# Test: Look for any remaining references to resState
rg "resState\.thirdPartyDeps"

Length of output: 31


Script:

#!/bin/bash
# Let's search for both old and new state references more broadly
rg "resState" -A 2
rg "appSchemaState" -A 2

# Also check for any state management related imports
rg "import.*State" -A 2

Length of output: 20181

packages/plugins/bridge/src/js/resource.js Outdated Show resolved Hide resolved
packages/plugins/bridge/src/js/resource.js Outdated Show resolved Hide resolved
packages/settings/styles/src/Main.vue Show resolved Hide resolved
packages/canvas/render/src/RenderMain.js Outdated Show resolved Hide resolved
packages/canvas/render/src/RenderMain.js Outdated Show resolved Hide resolved
packages/canvas/render/src/builtin/CanvasCollection.js Outdated Show resolved Hide resolved
packages/canvas/render/src/builtin/CanvasCollection.js Outdated Show resolved Hide resolved
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: 7

🧹 Outside diff range and nitpick comments (10)
packages/plugins/bridge/src/js/resource.js (4)

186-189: Use English for error messages.

For consistency and internationalization purposes, error messages should be in English.

-          message: '修改失败'
+          message: 'Modification failed'

197-197: Remove or update the orphaned comment.

This comment about canvas refresh seems outdated since the code no longer directly interacts with the canvas.

-        // 更新画布工具函数环境,保证渲染最新工具类返回值, 并触发画布的强制刷新

238-239: Use English for error messages.

For consistency and internationalization purposes, error messages should be in English.

-          message: '删除失败'
+          message: 'Deletion failed'

186-192: Consider centralizing notification handling.

The success/error notifications are duplicated across different functions. Consider extracting them into a dedicated notification helper to improve maintainability and consistency.

Example implementation:

const notifyOperation = (success, operation) => {
  useNotify({
    type: success ? 'success' : 'error',
    message: `${operation} ${success ? 'successful' : 'failed'}`
  });
};

// Usage
notifyOperation(false, 'Modification');
notifyOperation(true, 'Deletion');

Also applies to: 236-243

packages/canvas/render/src/builtin/CanvasCollection.js (1)

Line range hint 192-201: Add cleanup for updateKey subscription

The TinyGrid handler should clean up the updateKey subscription when the component is unmounted.

 TinyGrid: ({ node, sourceRef, schemaId, pageSchema, updateKey }) => {
   const sourceName = sourceRef.value?.name
   const methodName = `${NAME_PREFIX.table}${schemaId}`
+  let updateKeySubscription = null

   node.props.fetchData = {
     type: 'JSExpression',
     value: `{ api: this.${methodName} }`
   }

-  const updateNode = () => updateNodeHandler({ node, sourceRef, pageSchema, sourceName, methodName, updateKey })
+  const updateNode = () => {
+    updateKeySubscription = updateNodeHandler({ node, sourceRef, pageSchema, sourceName, methodName, updateKey })
+  }

   const clearBindVar = () => {
     delete pageSchema.methods[methodName]
+    if (updateKeySubscription) {
+      updateKeySubscription.unsubscribe()
+      updateKeySubscription = null
+    }
   }
packages/canvas/render/src/RenderMain.js (3)

365-371: Optimize schema update throttling

The throttle implementation is good but could be improved by making the delay configurable and adding error handling.

-const throttleUpdateSchema = useThrottleFn(
-  () => {
-    window.host.patchLatestSchema(schema)
-  },
-  100,
-  true
-)
+const SCHEMA_UPDATE_DELAY = 100
+const throttleUpdateSchema = useThrottleFn(
+  () => {
+    try {
+      window.host.patchLatestSchema(schema)
+    } catch (error) {
+      console.error('Failed to patch schema:', error)
+    }
+  },
+  SCHEMA_UPDATE_DELAY,
+  true
+)

380-392: Consider adding cleanup for failed subscriptions

The subscription setup looks good, but should handle potential subscription failures.

+const subscribeToTopic = (topic, subscriber, callback) => {
+  try {
     window.host.subscribe({
       topic,
       subscriber,
       callback
     })
+  } catch (error) {
+    console.error(`Failed to subscribe to ${topic}:`, error)
+  }
+}

-window.host.subscribe({
-  topic: 'schemaChange',
-  subscriber: 'canvasRenderer',
-  callback: throttleUpdateSchema
-})
+subscribeToTopic('schemaChange', 'canvasRenderer', throttleUpdateSchema)

-window.host.subscribe({
-  topic: 'schemaImport',
-  subscriber: 'canvasRenderer',
-  callback: () => {
-    setSchema(window.host.getSchema())
-  }
-})
+subscribeToTopic('schemaImport', 'canvasRenderer', () => {
+  setSchema(window.host.getSchema())
+})

426-473: Enhance watch handler cleanup

The watch handlers are properly set up with cleanup on unmount, but could benefit from error boundaries.

+const createWatchHandler = (getter, callback, options = {}) => {
+  try {
+    return window.host.watch(getter, callback, {
+      immediate: true,
+      deep: true,
+      ...options
+    })
+  } catch (error) {
+    console.error('Failed to setup watch:', error)
+    return () => {}
+  }
+}

-const utilsWatchCanceler = window.host.watch(
+const utilsWatchCanceler = createWatchHandler(
   () => window.host.appSchema?.utils,
   (data) => {
     setUtils(data)
-  },
-  {
-    immediate: true,
-    deep: true
   }
 )
packages/plugins/state/src/Main.vue (2)

Line range hint 168-172: Add error handling for schema operations

The add function should handle cases where getSchema() returns undefined to prevent silent failures.

Consider this improvement:

 const add = (name, variable) => {
   const { getSchema } = useCanvas()
+  const schema = getSchema()
 
-  if (getSchema()) {
+  if (!schema) {
+    notifySaveError('Failed to get schema')
+    return
+  }
+
   if (updateKey.value !== name && flag.value === OPTION_TYPE.UPDATE) {
     delete state.dataSource[updateKey.value]
   }
   state.dataSource[name] = variable
 }

Line range hint 271-286: Improve security and error handling in lifecycle updates

There are two concerns in the lifecycle update logic:

  1. The regex pattern could be vulnerable to ReDoS attacks
  2. Missing validation for setup.value existence

Consider these improvements:

 const schema = getSchema()
 let { lifeCycles } = schema
 const { [key]: deletedKey, ...restState } = schema.state

 if (key.startsWith('datasource')) {
   const pageSchema = getSchema()
   const { start, end } = getCommentByKey(key)

+  // Limit the input length to prevent ReDoS attacks
+  if (start.length > 100 || end.length > 100) {
+    notifySaveError('Invalid comment key length')
+    return
+  }

   const pattern = new RegExp(`([\\s\\n]*\\/\\*\\* ${start} \\*\\/[\\s\\S]*\\/\\*\\* ${end} \\*\\/)`)
+  
+  if (!pageSchema?.lifeCycles?.setup?.value) {
+    notifySaveError('Invalid lifecycle setup configuration')
+    return
+  }

   lifeCycles.setup.value = pageSchema.lifeCycles.setup.value.replace(pattern, '')
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c777b18 and 7886450.

📒 Files selected for processing (8)
  • packages/canvas/DesignCanvas/src/DesignCanvas.vue (4 hunks)
  • packages/canvas/DesignCanvas/src/api/useCanvas.js (6 hunks)
  • packages/canvas/render/src/RenderMain.js (9 hunks)
  • packages/canvas/render/src/builtin/CanvasCollection.js (5 hunks)
  • packages/configurator/src/container-configurator/ContainerConfigurator.vue (3 hunks)
  • packages/plugins/bridge/src/js/resource.js (4 hunks)
  • packages/plugins/i18n/src/composable/useTranslate.js (2 hunks)
  • packages/plugins/state/src/Main.vue (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/plugins/i18n/src/composable/useTranslate.js
🔇 Additional comments (9)
packages/plugins/bridge/src/js/resource.js (1)

14-14: LGTM! Import changes align with centralization goals.

The addition of meta-register imports and removal of useCanvas aligns with the PR's objective of centralizing schema modifications.

packages/canvas/render/src/builtin/CanvasCollection.js (1)

114-141: 🛠️ Refactor suggestion

Internationalize messages and improve error handling

The implementation has hard-coded messages and could benefit from better error handling.

Additionally, consider adding specific error types and handling:

+const ERRORS = {
+  COLUMN_IMPORT: 'COLUMN_IMPORT_ERROR',
+  INVALID_DATA: 'INVALID_DATA_ERROR'
+}

 try {
   const sourceColumns = sourceRef.value?.data?.columns?.map(({ title, field }) => ({ title, field })) || []
+  if (!Array.isArray(sourceColumns)) {
+    throw new Error(ERRORS.INVALID_DATA)
+  }
   node.props.columns = generateAssignColumns(sourceColumns, node.props.columns)
 } catch (error) {
+  console.error('Failed to import columns:', error)
   getController().useNotify({
     type: 'error',
-    message: '引入配置数据失败'
+    message: error.message === ERRORS.INVALID_DATA 
+      ? i18n.t('canvas.errors.invalid_data')
+      : i18n.t('canvas.errors.import_failed')
   })
 }
packages/plugins/state/src/Main.vue (1)

336-343: LGTM! Proper initialization of data source

The initialization logic correctly handles undefined cases by providing default values.

packages/configurator/src/container-configurator/ContainerConfigurator.vue (4)

7-7: Correct the event handler parameters for @update:modelValue

The event handler is still not properly handling the new value from the tiny-input component.


23-24: LGTM: Component imports and registration

The imports and component registration are properly configured for the tiny-input implementation.

Also applies to: 33-34


108-126: Optimize node reordering in the dragEnd function

The current implementation using delete and insert operations can be optimized.


128-132: 🛠️ Refactor suggestion

Add validation and error handling to onTitleUpdate

The method should validate the input and handle potential errors from schema operations.

-const onTitleUpdate = (value) => {
+const onTitleUpdate = (value) => {
+  if (!value?.id || !value?.props) {
+    useNotify({
+      type: 'error',
+      title: '更新标题失败',
+      message: '无效的节点数据'
+    })
+    return
+  }
+
   const { operateNode } = useCanvas()
   const id = value.id
-
-  operateNode({ type: 'changeProps', id, value: { props: value.props } })
+  try {
+    operateNode({ type: 'changeProps', id, value: { props: value.props } })
+  } catch (error) {
+    useNotify({
+      type: 'error',
+      title: '更新标题失败',
+      message: error.message
+    })
+  }
 }

Likely invalid or redundant comment.

packages/canvas/DesignCanvas/src/DesignCanvas.vue (2)

33-35: LGTM: Added notification utilities

The addition of messaging utilities aligns with the PR's goal of improving error handling and user feedback.


190-193: LGTM: Enhanced controller capabilities

The addition of AST utilities and notification methods to the controller improves the overall functionality.

Comment on lines +227 to +251
const getNodeById = (id) => {
return nodesMap.value.get(id)?.node
}

const getNodeWithParentById = (id) => {
return nodesMap.value.get(id)
}

const delNode = (id) => {
nodesMap.value.delete(id)
}

const clearNodes = () => {
nodesMap.value.clear()
}

const setNode = (schema, parent) => {
schema.id = schema.id || utils.guid()

nodesMap.value.set(schema.id, { node: schema, parent })
}

const getNode = (id, parent) => {
return parent ? nodesMap.value.get(id) : nodesMap.value.get(id)?.node
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for node operations

The node management functions need input validation to prevent potential errors.

 const getNodeById = (id) => {
+  if (!id) {
+    console.warn('getNodeById called with invalid id')
+    return null
+  }
   return nodesMap.value.get(id)?.node
 }

 const getNodeWithParentById = (id) => {
+  if (!id) {
+    console.warn('getNodeWithParentById called with invalid id')
+    return null
+  }
   return nodesMap.value.get(id)
 }

 const delNode = (id) => {
+  if (!id || !nodesMap.value.has(id)) {
+    console.warn('delNode called with invalid or non-existent id')
+    return
+  }
   nodesMap.value.delete(id)
 }
📝 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
const getNodeById = (id) => {
return nodesMap.value.get(id)?.node
}
const getNodeWithParentById = (id) => {
return nodesMap.value.get(id)
}
const delNode = (id) => {
nodesMap.value.delete(id)
}
const clearNodes = () => {
nodesMap.value.clear()
}
const setNode = (schema, parent) => {
schema.id = schema.id || utils.guid()
nodesMap.value.set(schema.id, { node: schema, parent })
}
const getNode = (id, parent) => {
return parent ? nodesMap.value.get(id) : nodesMap.value.get(id)?.node
}
const getNodeById = (id) => {
if (!id) {
console.warn('getNodeById called with invalid id')
return null
}
return nodesMap.value.get(id)?.node
}
const getNodeWithParentById = (id) => {
if (!id) {
console.warn('getNodeWithParentById called with invalid id')
return null
}
return nodesMap.value.get(id)
}
const delNode = (id) => {
if (!id || !nodesMap.value.has(id)) {
console.warn('delNode called with invalid or non-existent id')
return
}
nodesMap.value.delete(id)
}
const clearNodes = () => {
nodesMap.value.clear()
}
const setNode = (schema, parent) => {
schema.id = schema.id || utils.guid()
nodesMap.value.set(schema.id, { node: schema, parent })
}
const getNode = (id, parent) => {
return parent ? nodesMap.value.get(id) : nodesMap.value.get(id)?.node
}

Comment on lines +482 to +540
// 获取传入的 schema 与最新 schema 的 diff
const getSchemaDiff = (schema) => {
return jsonDiffPatchInstance.diff(schema, pageState.pageSchema)
}

const patchLatestSchema = (schema) => {
// 这里 pageSchema 需要 deepClone,不然 patch 的时候,会 patch 成同一个引用,造成画布无法更新
const diff = jsonDiffPatchInstance.diff(schema, deepClone(pageState.pageSchema))

if (diff) {
jsonDiffPatchInstance.patch(schema, diff)
}
}

const importSchema = (data) => {
let importData = data

if (typeof data === 'string') {
try {
importData = JSON.parse(data)
} catch (error) {
// eslint-disable-next-line no-console
console.error('[useCanvas.importSchema] Invalid data')
}
}

// JSON 格式校验
resetCanvasState({
pageSchema: importData
})
}

const exportSchema = () => {
return JSON.stringify(pageState.pageSchema)
}

const getSchema = () => {
return pageState.pageSchema || {}
}

const getNodePath = (id, nodes = []) => {
const { parent, node } = getNodeWithParentById(id) || {}

node && nodes.unshift({ name: node.componentName, node: id })

if (parent) {
parent && getNodePath(parent.id, nodes)
} else {
nodes.unshift({ name: 'BODY', node: id })
}

return nodes
}

const updateSchema = (data) => {
Object.assign(pageState.pageSchema, data)

publish({ topic: 'schemaChange', data: {} })
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve schema diff and update handling

The schema management functions need better error handling and type checking.

 const getSchemaDiff = (schema) => {
+  if (!schema || typeof schema !== 'object') {
+    console.error('Invalid schema provided to getSchemaDiff')
+    return null
+  }
   return jsonDiffPatchInstance.diff(schema, pageState.pageSchema)
 }

 const patchLatestSchema = (schema) => {
+  if (!schema || typeof schema !== 'object') {
+    console.error('Invalid schema provided to patchLatestSchema')
+    return
+  }
   const diff = jsonDiffPatchInstance.diff(schema, deepClone(pageState.pageSchema))

   if (diff) {
+    try {
       jsonDiffPatchInstance.patch(schema, diff)
+    } catch (error) {
+      console.error('Failed to patch schema:', error)
+    }
   }
 }
📝 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
// 获取传入的 schema 与最新 schema 的 diff
const getSchemaDiff = (schema) => {
return jsonDiffPatchInstance.diff(schema, pageState.pageSchema)
}
const patchLatestSchema = (schema) => {
// 这里 pageSchema 需要 deepClone,不然 patch 的时候,会 patch 成同一个引用,造成画布无法更新
const diff = jsonDiffPatchInstance.diff(schema, deepClone(pageState.pageSchema))
if (diff) {
jsonDiffPatchInstance.patch(schema, diff)
}
}
const importSchema = (data) => {
let importData = data
if (typeof data === 'string') {
try {
importData = JSON.parse(data)
} catch (error) {
// eslint-disable-next-line no-console
console.error('[useCanvas.importSchema] Invalid data')
}
}
// JSON 格式校验
resetCanvasState({
pageSchema: importData
})
}
const exportSchema = () => {
return JSON.stringify(pageState.pageSchema)
}
const getSchema = () => {
return pageState.pageSchema || {}
}
const getNodePath = (id, nodes = []) => {
const { parent, node } = getNodeWithParentById(id) || {}
node && nodes.unshift({ name: node.componentName, node: id })
if (parent) {
parent && getNodePath(parent.id, nodes)
} else {
nodes.unshift({ name: 'BODY', node: id })
}
return nodes
}
const updateSchema = (data) => {
Object.assign(pageState.pageSchema, data)
publish({ topic: 'schemaChange', data: {} })
}
// 获取传入的 schema 与最新 schema 的 diff
const getSchemaDiff = (schema) => {
if (!schema || typeof schema !== 'object') {
console.error('Invalid schema provided to getSchemaDiff')
return null
}
return jsonDiffPatchInstance.diff(schema, pageState.pageSchema)
}
const patchLatestSchema = (schema) => {
if (!schema || typeof schema !== 'object') {
console.error('Invalid schema provided to patchLatestSchema')
return
}
// 这里 pageSchema 需要 deepClone,不然 patch 的时候,会 patch 成同一个引用,造成画布无法更新
const diff = jsonDiffPatchInstance.diff(schema, deepClone(pageState.pageSchema))
if (diff) {
try {
jsonDiffPatchInstance.patch(schema, diff)
} catch (error) {
console.error('Failed to patch schema:', error)
}
}
}
const importSchema = (data) => {
let importData = data
if (typeof data === 'string') {
try {
importData = JSON.parse(data)
} catch (error) {
// eslint-disable-next-line no-console
console.error('[useCanvas.importSchema] Invalid data')
}
}
// JSON 格式校验
resetCanvasState({
pageSchema: importData
})
}
const exportSchema = () => {
return JSON.stringify(pageState.pageSchema)
}
const getSchema = () => {
return pageState.pageSchema || {}
}
const getNodePath = (id, nodes = []) => {
const { parent, node } = getNodeWithParentById(id) || {}
node && nodes.unshift({ name: node.componentName, node: id })
if (parent) {
parent && getNodePath(parent.id, nodes)
} else {
nodes.unshift({ name: 'BODY', node: id })
}
return nodes
}
const updateSchema = (data) => {
Object.assign(pageState.pageSchema, data)
publish({ topic: 'schemaChange', data: {} })
}

Comment on lines +253 to +454
if (!node) {
node = pageState.pageSchema
}

if (!node.props) {
node.props = {}
}

if (overwrite) {
node.props = value.props
} else {
Object.assign(node.props, value?.props || {})
}

return {
current: node,
previous
}
},
updateAttributes: (operation) => {
const { id, value, overwrite } = operation
const { id: _id, children, ...restAttr } = value
const node = getNode(id)

// 其他属性直接浅 merge
Object.assign(node, restAttr)

// 配置了 overwrite,需要将没有传入的属性进行删除(不包括 children)
if (overwrite) {
const { id: _unUsedId, children: _unUsedChildren, ...restOrigin } = node
const originKeys = Object.keys(restOrigin)
const newKeysSet = new Set(Object.keys(restAttr))

originKeys.forEach((key) => {
if (!newKeysSet.has(key)) {
delete node[key]
}
})
}

if (!Array.isArray(children)) {
// 非数组类型的 children,比如是直接的字符串作为 children
if (children || typeof children === 'string') {
node.children = children
}

return
}

const newChildren = children.map((item) => {
if (!item.id) {
item.id = utils.guid()
}

return item
})
// 传了 children 进来,需要找出来被删除的、新增的,剩下的是修改的。
const originChildrenIds = (node.children || []).filter(({ id }) => id).map(({ id }) => id)
const originChildrenSet = new Set(originChildrenIds)

const newChildrenSet = new Set(newChildren.map(({ id }) => id))
// 被删除的项
const deletedIds = originChildrenIds.filter((id) => !newChildrenSet.has(id))
const deletedIdsSet = new Set(deletedIds)

for (const id of deletedIds) {
operationTypeMap.delete({ id })
}

// 筛选出来新增的和修改的
const changedChildren = newChildren.filter(({ id }) => !deletedIdsSet.has(id))

changedChildren.forEach((childItem, index) => {
// 新增
if (!originChildrenSet.has(childItem.id)) {
operationTypeMap.insert({
parentId: id,
newNodeData: childItem,
position: 'after',
referTargetNodeId: changedChildren?.[index]?.id
})
return
}

// 直接改引用插入进来,但是没有构建对应的 Map,需要构建Map
if (!getNode(childItem.id)) {
setNode(childItem, node)

// 递归构建 nodeMap
if (Array.isArray(childItem?.children) && childItem.children.length) {
const newNode = getNode(childItem.id)
generateNodesMap(childItem.children, newNode)
}
}

// 递归修改
operationTypeMap.updateAttributes({ id: childItem.id, value: childItem })
})
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance error handling in operation handlers

The operation handlers need better error handling and validation.

 const operationTypeMap = {
   insert: (operation) => {
+    if (!operation?.parentId || !operation?.newNodeData) {
+      console.error('Invalid insert operation:', operation)
+      return {}
+    }
     const { parentId, newNodeData, position, referTargetNodeId } = operation
     // ... rest of the code

   delete: (operation) => {
+    if (!operation?.id) {
+      console.error('Invalid delete operation:', operation)
+      return
+    }
     const { id } = operation
     // ... rest of the code

   changeProps: (operation) => {
+    if (!operation?.id || !operation?.value) {
+      console.error('Invalid changeProps operation:', operation)
+      return { current: null, previous: null }
+    }
     const { id, value, option: changeOption } = operation
     // ... rest of the code

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +312 to +320
const storeList = [...useResource().appSchemaState.globalState] || []
const index = storeList.findIndex((store) => store.id === key)

if (index !== -1) {
const { id } = getMetaApi(META_SERVICE.GlobalService).getBaseInfo()

storeListt.splice(index, 1)
updateGlobalState(id, { global_state: storeListt }).then((res) => {
setGlobalState(res.global_state)
storeList.splice(index, 1)
updateGlobalState(id, { global_state: storeList }).then((res) => {
useResource().appSchemaState.globalState = res.global_state || []
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for global state updates

The global state update lacks error handling and could be susceptible to race conditions.

Consider these improvements:

-const storeList = [...useResource().appSchemaState.globalState] || []
+const resource = useResource()
+const storeList = [...(resource.appSchemaState.globalState || [])]
 const index = storeList.findIndex((store) => store.id === key)

 if (index !== -1) {
   const { id } = getMetaApi(META_SERVICE.GlobalService).getBaseInfo()
   storeList.splice(index, 1)
-  updateGlobalState(id, { global_state: storeList }).then((res) => {
-    useResource().appSchemaState.globalState = res.global_state || []
-    setGlobalStateToDataSource()
-  })
+  updateGlobalState(id, { global_state: storeList })
+    .then((res) => {
+      resource.appSchemaState.globalState = res.global_state || []
+      setGlobalStateToDataSource()
+    })
+    .catch((error) => {
+      notifySaveError('Failed to update global state')
+      console.error('Global state update failed:', error)
+    })
📝 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
const storeList = [...useResource().appSchemaState.globalState] || []
const index = storeList.findIndex((store) => store.id === key)
if (index !== -1) {
const { id } = getMetaApi(META_SERVICE.GlobalService).getBaseInfo()
storeListt.splice(index, 1)
updateGlobalState(id, { global_state: storeListt }).then((res) => {
setGlobalState(res.global_state)
storeList.splice(index, 1)
updateGlobalState(id, { global_state: storeList }).then((res) => {
useResource().appSchemaState.globalState = res.global_state || []
const resource = useResource()
const storeList = [...(resource.appSchemaState.globalState || [])]
const index = storeList.findIndex((store) => store.id === key)
if (index !== -1) {
const { id } = getMetaApi(META_SERVICE.GlobalService).getBaseInfo()
storeList.splice(index, 1)
updateGlobalState(id, { global_state: storeList })
.then((res) => {
resource.appSchemaState.globalState = res.global_state || []
setGlobalStateToDataSource()
})
.catch((error) => {
notifySaveError('Failed to update global state')
console.error('Global state update failed:', error)
})

Comment on lines +219 to +221
const schema = getSchema()
updateSchema({ state: { ...(schema.state || {}), [name]: variable } })

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Preserve existing schema properties during update

The current implementation might accidentally remove other schema properties during the update.

Consider this safer approach:

 const schema = getSchema()
-updateSchema({ state: { ...(schema.state || {}), [name]: variable } })
+updateSchema({
+  ...schema,
+  state: { ...(schema.state || {}), [name]: variable }
+})
📝 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
const schema = getSchema()
updateSchema({ state: { ...(schema.state || {}), [name]: variable } })
const schema = getSchema()
updateSchema({
...schema,
state: { ...(schema.state || {}), [name]: variable }
})

Comment on lines +42 to +65
const updateChildrenToValid = () => {
const schema = useProperties().getSchema()
const schemaChildren = schema.children || []
let hasUpdate = false

const newChildren = schemaChildren.map((item) => {
if (!item.props) {
hasUpdate = true

item.props = {
title: '选项卡',
name: ''
}
}

return item
})

const { operateNode } = useCanvas()

if (hasUpdate) {
operateNode({ type: 'updateAttributes', id: schema.id, value: { children: newChildren } })
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for schema operations

While the validation logic is good, the schema update operation should include error handling to gracefully handle failures.

 const updateChildrenToValid = () => {
   const schema = useProperties().getSchema()
   const schemaChildren = schema.children || []
   let hasUpdate = false

   const newChildren = schemaChildren.map((item) => {
     if (!item.props) {
       hasUpdate = true

       item.props = {
         title: '选项卡',
         name: ''
       }
     }

     return item
   })

   const { operateNode } = useCanvas()

   if (hasUpdate) {
+    try {
       operateNode({ type: 'updateAttributes', id: schema.id, value: { children: newChildren } })
+    } catch (error) {
+      useNotify({
+        type: 'error',
+        title: '更新架构失败',
+        message: error.message
+      })
+    }
   }
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +136 to +150
const nodeSelected = (node, parent, type, id) => {
const { toolbars } = useLayout().layoutState
if (type !== 'clickTree') {
useLayout().closePlugin()
}

const { getSchema, getNodePath } = useCanvas().canvasApi.value
const { getSchema, getNodePath } = useCanvas()
const schemaItem = useCanvas().getNodeById(id)

const pageSchema = getSchema()

const schema = getSchema()
// 如果选中的节点是画布,就设置成默认选中最外层schema
useProperties().getProps(node || schema, parent)
useCanvas().setCurrentSchema(node || schema)
footData.value = getNodePath(node?.id)
useProperties().getProps(schemaItem || pageSchema, parent)
useCanvas().setCurrentSchema(schemaItem || pageSchema)
footData.value = getNodePath(schemaItem?.id)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation and null checks to nodeSelected

The method should validate the id parameter and handle potential null references.

-const nodeSelected = (node, parent, type, id) => {
+const nodeSelected = (node, parent, type, id) => {
   const { toolbars } = useLayout().layoutState
   if (type !== 'clickTree') {
     useLayout().closePlugin()
   }

+  if (!id) {
+    useNotify({
+      type: 'error',
+      title: '节点选择失败',
+      message: '无效的节点ID'
+    })
+    return
+  }

   const { getSchema, getNodePath } = useCanvas()
   const schemaItem = useCanvas().getNodeById(id)
   const pageSchema = getSchema()

+  if (!schemaItem && !pageSchema) {
+    useNotify({
+      type: 'error',
+      title: '节点选择失败',
+      message: '未找到有效的架构'
+    })
+    return
+  }

   useProperties().getProps(schemaItem || pageSchema, parent)
   useCanvas().setCurrentSchema(schemaItem || pageSchema)
   footData.value = getNodePath(schemaItem?.id)
   toolbars.visiblePopover = false
 }

Committable suggestion skipped: line range outside the PR's diff.

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 refactoring Refactoring
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant