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(web): add extension settings panel to plugin playground #1383

Merged
merged 26 commits into from
Feb 12, 2025

Conversation

mulengawilfred
Copy link
Contributor

@mulengawilfred mulengawilfred commented Jan 28, 2025

Overview

Adds a widget panel to plugin playground. Converts the widget schema groups found in web/src/beta/features/PluginPlayground/Plugins/presets/custom/myPlugin.ts into UI input fields to display in widget panel. This shows that the plugin developer can adjust the widget schema to display different UI components. Stores the input field data locally.

What I've done

What I haven't done

How I tested

Which point I want you to review particularly

Would need to confirm what other UI components we wish to display. Currently only text, number and boolean fields were added. The fields also store the values in local state. Will need to confirm if these values can be used after editing.

Memo

Summary by CodeRabbit

  • New Features

    • Introduced an "Extension Settings" panel within the Plugin Playground, allowing interactive management of plugin configuration.
    • Added a new extension property plugin under an "Extension" category, expanding available customization options.
    • Enhanced file type detection and YAML processing, leading to improved configuration feedback and error handling.
    • Added a new component, PropertyItem, for rendering various input fields based on schema properties.
    • Introduced a new component, ExtensionSettings, to manage and display plugin settings.
  • Refactor

    • Streamlined type management and underlying utility logic for more robust and dynamic extension configuration.
    • Restructured imports to centralize type management and simplify code organization.
    • Updated method signatures and component structures for improved clarity and functionality.
    • Modified state handling for the showStoryPanel within the SettingsList component for more intuitive user interaction.

Copy link

coderabbitai bot commented Jan 28, 2025

Walkthrough

This pull request streamlines type management and YAML parsing within the Plugin Playground module. It centralizes type imports, removes redundant local definitions and utility functions, and updates function signatures—most notably in the hook. New helper functions and components are added to improve schema field processing and extension settings management. The changes also reconfigure the import paths for utilities and extend the plugin presets with a new extension category.

Changes

File(s) Summary of Changes
web/.../Code/hook.ts Updated function signature with a new parameter and return type (HookReturnType); removed local type definitions and getYmlJson; added the generateProperty helper function.
web/.../Code/index.tsx Changed the import path for getLanguageByFileExtension from ./utils to ../utils.
web/.../Code/utils.ts Removed the old getLanguageByFileExtension; introduced new exported functions: getLanguageByFileExtension, getYmlJson, and customSchemaFieldsToObject.
web/.../hooks.tsx Added a new state variable (fieldValues) and its updater; introduced the ExtensionSettingsPanel component; updated destructuring from useCode to include fieldValues.
web/.../index.tsx Integrated the new ExtensionSettingsPanel into the PluginPlayground layout.
web/.../types.ts Added new type definitions and interfaces: ReearthYML, Widgets, CustomInfoboxBlock, CustomStoryBlock, FieldValue, CustomField, and Group.
web/.../ExtensionSettings/PropertyItem.tsx Introduced a new React component PropertyItem to render various input fields based on schema field properties.
web/.../ExtensionSettings/index.tsx Created a new React component ExtensionSettings that manages plugin settings by processing a YAML file and rendering property items for each field.
web/.../Plugins/presets/extension/extensionProperty.ts Defined a new plugin file for the extension property, setting up both YAML metadata and a UI component.
web/.../Plugins/presets/index.ts Added a new preset category for extensions to the presetPlugins array.

Possibly related PRs

Suggested reviewers

  • mkumbobeaty
  • ZTongci
  • OpeDada

Poem

I hopped through lines of code at night,
Tweaking types in the pale moonlight.
YAML fields danced in a rhythmic sway,
New hooks born to light the way.
In extension lands, settings now gleam bright —
A bunny’s joy in every feature’s flight!
🐰✨ Enjoy the roll of every byte!


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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

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.

Copy link

netlify bot commented Jan 28, 2025

Deploy Preview for reearth-web ready!

Name Link
🔨 Latest commit e4073aa
🔍 Latest deploy log https://app.netlify.com/sites/reearth-web/deploys/67ac5460d5462d0008906ace
😎 Deploy Preview https://deploy-preview-1383--reearth-web.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

@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: 5

🧹 Nitpick comments (3)
web/src/beta/features/PluginPlayground/utils.ts (1)

6-17: Add case-insensitive extension matching.

The function should handle uppercase file extensions (e.g., .JS, .YML).

 export const getLanguageByFileExtension = (fileTitle: string) => {
-  const ext = fileTitle.split(".").pop();
+  const ext = fileTitle.split(".").pop()?.toLowerCase();
   switch (ext) {
     case "js":
       return "javascript";
web/src/beta/features/PluginPlayground/Plugins/presets/custom/myPlugin.ts (1)

20-36: Consider improving schema field definitions.

The schema structure could be enhanced for better maintainability:

  1. Use descriptive IDs instead of numeric values (e.g., "text_field" instead of "1")
  2. Remove redundancy between name and title properties as they serve similar purposes
     schema:
       groups:
       - id: default
         title: Data
         fields:
-          - id: 1
+          - id: text_field
             type: string
             title: Text
-            name: Title
-          - id: 2
+          - id: number_field
             type: number
             title: Number
-            name: Number
-          - id: 3
+          - id: boolean_field
             type: bool
             title: Boolean
-            name: Boolean
web/src/beta/features/PluginPlayground/WidgetsList/index.tsx (1)

22-24: Use optional chaining for better code readability.

The current nested check can be simplified using optional chaining.

-  const ymlFile =
-    selectedPlugin.files &&
-    selectedPlugin.files.find((f) => f.title.endsWith("reearth.yml"));
+  const ymlFile = selectedPlugin.files?.find((f) => f.title.endsWith("reearth.yml"));
🧰 Tools
🪛 Biome (1.9.4)

[error] 23-24: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75dbc0f and 147ab9b.

📒 Files selected for processing (10)
  • web/src/beta/features/PluginPlayground/Code/hook.ts (1 hunks)
  • web/src/beta/features/PluginPlayground/Code/index.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/Code/utils.ts (0 hunks)
  • web/src/beta/features/PluginPlayground/Plugins/presets/custom/myPlugin.ts (1 hunks)
  • web/src/beta/features/PluginPlayground/WidgetsList/PropertyItem.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/WidgetsList/index.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/hooks.tsx (2 hunks)
  • web/src/beta/features/PluginPlayground/index.tsx (2 hunks)
  • web/src/beta/features/PluginPlayground/types.ts (1 hunks)
  • web/src/beta/features/PluginPlayground/utils.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • web/src/beta/features/PluginPlayground/Code/utils.ts
✅ Files skipped from review due to trivial changes (1)
  • web/src/beta/features/PluginPlayground/Code/index.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
web/src/beta/features/PluginPlayground/WidgetsList/index.tsx

[error] 23-24: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 56-56: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Redirect rules - reearth-web
  • GitHub Check: Header rules - reearth-web
  • GitHub Check: Pages changed - reearth-web
  • GitHub Check: ci-server / ci-server-lint
  • GitHub Check: ci-web / ci
  • GitHub Check: ci-server / ci-server-test
🔇 Additional comments (5)
web/src/beta/features/PluginPlayground/utils.ts (1)

19-34: Consider adding validation for the parsed YAML structure.

While the function handles parsing errors well, it doesn't validate if the parsed data matches the expected ReearthYML structure.

Consider adding runtime type checking:

 try {
   const data = yaml.load(file.sourceCode) as ReearthYML;
+  if (!data?.id || !data?.name || !data?.version) {
+    return { 
+      success: false, 
+      message: "Invalid YAML structure: missing required fields" 
+    } as const;
+  }
   return { success: true, data } as const;
✅ Verification successful

Runtime validation for ReearthYML structure is indeed necessary

The suggestion is valid as the current type assertion (as ReearthYML) only provides compile-time checking. The proposed validation ensures the required fields (id, name, version) are present at runtime, preventing potential issues with malformed YAML files that pass parsing but lack mandatory fields.

🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Find ReearthYML type definition
ast-grep --pattern 'type ReearthYML = {$$$}'

# Find interface definition if type not found
ast-grep --pattern 'interface ReearthYML {$$$}'

# Find usages of getYmlJson
rg "getYmlJson" -A 2

# Look for any existing validation patterns
rg "ReearthYML" -A 5

Length of output: 6101

web/src/beta/features/PluginPlayground/types.ts (1)

7-28: Consider making schema required for widget extensions.

The schema property is optional, but widgets typically require configuration schema to be functional.

 export type ReearthYML = {
   id: string;
   name: string;
   version: string;
   extensions?: {
     id: string;
     type: string;
     name: string;
     description: string;
     widgetLayout?: {
       extended: boolean;
       defaultLocation: {
         zone: WidgetLocation["zone"];
         section: WidgetLocation["section"];
         area: WidgetLocation["area"];
       };
     };
-    schema?: {
+    schema: {
       groups: Group;
     };
   }[];
 };
web/src/beta/features/PluginPlayground/index.tsx (1)

35-37: LGTM! Panel implementation follows existing patterns.

The WidgetsPanel integration is clean and consistent with the existing layout structure.

web/src/beta/features/PluginPlayground/Code/hook.ts (1)

8-9: LGTM! Good refactoring of types and utilities.

The changes improve code organization by:

  1. Centralizing type definitions
  2. Moving YAML parsing logic to a dedicated utility file
web/src/beta/features/PluginPlayground/hooks.tsx (1)

192-194: LGTM! Clean integration of the widgets panel.

The WidgetsPanel component is well-integrated and follows the established pattern of panel components in the codebase.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
web/src/beta/features/PluginPlayground/types.ts (1)

7-28: Consider improving type safety for extension type.

The type property in extensions could be more strictly typed to prevent invalid values.

   extensions?: {
     id: string;
-    type: string;
+    type: "widget" | "block" | string;
     name: string;
     description: string;

Also, consider adding JSDoc comments to explain the purpose of widgetLayout.extended.

web/src/beta/features/PluginPlayground/WidgetsList/PropertyItem.tsx (1)

44-69: Improve type safety in render logic.

Consider using type narrowing instead of type assertions for better type safety.

-          value={value as number}
+          value={typeof value === 'number' ? value : 0}

-          value={value as boolean}
+          value={typeof value === 'boolean' ? value : false}

-          value={value as string}
+          value={typeof value === 'string' ? value : ''}
web/src/beta/features/PluginPlayground/WidgetsList/index.tsx (3)

22-32: Improve code readability and error handling.

  1. Use optional chaining for better readability
  2. Consider using a proper error state component instead of empty div
-  const ymlFile =
-    selectedPlugin.files &&
-    selectedPlugin.files.find((f) => f.title.endsWith("reearth.yml"));
+  const ymlFile = selectedPlugin.files?.find((f) => 
+    f.title.endsWith("reearth.yml")
+  );

   if (!ymlFile) {
     setNotification({
       type: "error",
       text: "No YAML configuration file found"
     });
-    return <div />;
+    return <EmptyState message="No YAML configuration file found" />;
   }
🧰 Tools
🪛 Biome (1.9.4)

[error] 23-24: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


66-72: Improve error message specificity.

The error message "No schema found in widget extension" is used for different error conditions. Consider using more specific messages:

   if (!widgetSchema || widgetSchema.length == 0) {
     setNotification({
       type: "error",
-      text: "No schema found in widget extension"
+      text: "Widget schema is empty or undefined"
     });
-    return <div />;
+    return <EmptyState message="No widget schema available" />;
   }

74-81: Add support for multiple schema groups.

The current implementation only renders fields from the first group. Consider supporting multiple groups and add error handling for empty fields.

-  const { fields } = widgetSchema[0];
+  return (
+    <Wrapper>
+      {widgetSchema.map(group => (
+        <div key={group.id}>
+          <h3>{group.title}</h3>
+          {group.fields?.map((field) => (
+            <PropertyItem key={field.id} field={field} />
+          )) ?? <EmptyState message="No fields in this group" />}
+        </div>
+      ))}
+    </Wrapper>
+  );
-  return (
-    <Wrapper>
-      {fields.map((field) => (
-        <PropertyItem key={field.id} field={field} />
-      ))}
-    </Wrapper>
-  );
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 147ab9b and 48e26aa.

📒 Files selected for processing (3)
  • web/src/beta/features/PluginPlayground/WidgetsList/PropertyItem.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/WidgetsList/index.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/types.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
web/src/beta/features/PluginPlayground/WidgetsList/PropertyItem.tsx

[error] 32-32: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

web/src/beta/features/PluginPlayground/WidgetsList/index.tsx

[error] 23-24: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Redirect rules - reearth-web
  • GitHub Check: Header rules - reearth-web
  • GitHub Check: Pages changed - reearth-web
  • GitHub Check: ci-server / ci-server-lint
  • GitHub Check: ci-server / ci-server-test
  • GitHub Check: ci-web / ci
🔇 Additional comments (3)
web/src/beta/features/PluginPlayground/types.ts (2)

30-30: LGTM!

Clean and effective type extraction from Visualizer component props.


43-47: LGTM! Type-safe implementation achieved.

The defaultValue property uses a proper union type as previously suggested, which enhances type safety.

web/src/beta/features/PluginPlayground/WidgetsList/PropertyItem.tsx (1)

15-24: LGTM! Clean state management implementation.

Good use of a single state variable with type-safe initialization.

@mulengawilfred mulengawilfred changed the title feat(web): add widget panel to plugin playground [WIP] feat(web): add widget panel to plugin playground Jan 29, 2025
@mulengawilfred mulengawilfred changed the title [WIP] feat(web): add widget panel to plugin playground feat(web): add widget panel to plugin playground Feb 5, 2025
Copy link

@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: 3

🧹 Nitpick comments (8)
web/src/beta/features/PluginPlayground/utils.ts (2)

6-17: Consider using a Map for language mapping.

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

-export const getLanguageByFileExtension = (fileTitle: string) => {
-  const ext = fileTitle.split(".").pop();
-  switch (ext) {
-    case "js":
-      return "javascript";
-    case "yml":
-    case "yaml":
-      return "yaml";
-    default:
-      return "plaintext";
-  }
-};
+const languageMap = new Map([
+  ["js", "javascript"],
+  ["yml", "yaml"],
+  ["yaml", "yaml"]
+]);
+
+export const getLanguageByFileExtension = (fileTitle: string) => {
+  const ext = fileTitle.split(".").pop();
+  return languageMap.get(ext ?? "") ?? "plaintext";
+};

36-48: Add type guard for array check.

The array check could be more type-safe.

-export const customSchemaFieldsToObject = (array: CustomSchemaField[]) => {
+export const customSchemaFieldsToObject = (array: CustomSchemaField[] | undefined) => {
   if (Array.isArray(array)) {
     return array.reduce(
       (acc, item) => {
         const { id, ...rest } = item;
         acc[id as string] = rest;
         return acc;
       },
       {} as Record<string, unknown>
     );
   }
   return {};
 };
web/src/beta/features/PluginPlayground/Plugins/presets/custom/myPlugin.ts (1)

20-34: Consider adding validation constraints to schema fields.

The schema fields could benefit from additional validation constraints.

           - id: bgColor
             type: string
             title: Background Color
             name: Background Color
             ui: color
+            required: true
+            defaultValue: "#ffffff"
           - id: title 
             type: string
             title: Title
             name: Title
+            required: true
+            maxLength: 100
+            pattern: "^[a-zA-Z0-9\\s]+$"
web/src/beta/features/PluginPlayground/WidgetsList/index.tsx (3)

41-43: Use optional chaining for better readability.

The nested property access could be simplified using optional chaining.

-  const ymlFile =
-    selectedPlugin.files &&
-    selectedPlugin.files.find((f) => f.title.endsWith("reearth.yml"));
+  const ymlFile = selectedPlugin.files?.find((f) => f.title.endsWith("reearth.yml"));
🧰 Tools
🪛 Biome (1.9.4)

[error] 42-43: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


37-115: Consider memoizing the component for better performance.

Since the component receives props that could frequently change, memoization could improve performance.

-const WidgetsList: FC<Props> = ({
+const WidgetsList: FC<Props> = memo(({
   selectedPlugin,
   setUpdatedField
-}): JSX.Element => {
+}) => {
   // ... component logic ...
-};
+});
+
+WidgetsList.displayName = "WidgetsList";
🧰 Tools
🪛 Biome (1.9.4)

[error] 42-43: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


47-51: Centralize error messages for better maintainability.

Consider moving error messages to a centralized constants file.

// constants.ts
export const WIDGET_ERRORS = {
  NO_YML: "This plugin does not have a reearth.yml file.",
  INVALID_YML: "This plugin does not have a valid reearth.yml file.",
  NO_WIDGET: "This plugin does not have a valid widget extension.",
  NO_WIDGETS: "This plugin does not have any widgets."
} as const;

Also applies to: 59-61, 69-73, 84-88, 96-98

web/src/beta/features/PluginPlayground/WidgetsList/PropertyItem.tsx (2)

57-73: Add value validation in handleChange.

The current implementation doesn't validate values before updating the state.

Add validation to ensure values match the expected type:

   const handleChange = ({
     newValue,
     fieldId
   }: {
     newValue:
       | boolean
       | LatLng
       | number
       | number[]
       | string
       | string[]
       | SpacingValues;
     fieldId: string;
   }): void => {
+    // Validate value based on field type
+    if (
+      (field.type === "number" && typeof newValue !== "number") ||
+      (field.type === "bool" && typeof newValue !== "boolean") ||
+      (field.type === "array" && !Array.isArray(newValue))
+    ) {
+      console.warn(`Invalid value type for field ${field.id}`);
+      return;
+    }
     setValue(newValue);
     setUpdatedField({ fieldId, value: newValue });
   };

205-210: Handle undefined values consistently in NumberField.

The current implementation uses a fallback of 0 for undefined values, which might not be appropriate for all number fields.

Consider using the field's minimum value as a fallback:

             onChange={(newValue?: number) =>
               handleChange({
-                newValue: newValue ?? 0,
+                newValue: newValue ?? field.min ?? 0,
                 fieldId: field.id
               })
             }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48e26aa and 05e9457.

📒 Files selected for processing (7)
  • web/src/beta/features/PluginPlayground/Code/hook.ts (3 hunks)
  • web/src/beta/features/PluginPlayground/Plugins/presets/custom/myPlugin.ts (1 hunks)
  • web/src/beta/features/PluginPlayground/WidgetsList/PropertyItem.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/WidgetsList/index.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/hooks.tsx (3 hunks)
  • web/src/beta/features/PluginPlayground/types.ts (1 hunks)
  • web/src/beta/features/PluginPlayground/utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/beta/features/PluginPlayground/hooks.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
web/src/beta/features/PluginPlayground/WidgetsList/index.tsx

[error] 42-43: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

web/src/beta/features/PluginPlayground/Code/hook.ts

[error] 211-211: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: ci-web / ci
  • GitHub Check: ci-server / ci-server-lint
  • GitHub Check: ci-server / ci-server-test
🔇 Additional comments (3)
web/src/beta/features/PluginPlayground/utils.ts (1)

19-34: LGTM! Well-structured error handling.

The function properly handles empty files and YAML parsing errors with clear error messages.

web/src/beta/features/PluginPlayground/types.ts (1)

8-59: LGTM! Well-structured type definitions.

The types are comprehensive, properly nested, and follow TypeScript best practices.

web/src/beta/features/PluginPlayground/Code/hook.ts (1)

121-125: Improve type safety in widget property initialization.

The current implementation assumes schema fields are always defined when creating widget properties.

Add a null check before accessing schema fields:

                     property: {
-                      default: customSchemaFieldsToObject(
-                        schemaFields as CustomSchemaField[]
-                      )
+                      default: schemaFields ? 
+                        customSchemaFieldsToObject(schemaFields) : {}
                     },
✅ Verification successful

Error: No shell scripts or web queries found


Improve type safety by adding a null-check on schemaFields.
It’s confirmed that in file web/src/beta/features/PluginPlayground/Code/hook.ts (lines 121–125) the widget property is initialized using:

default: customSchemaFieldsToObject(schemaFields as CustomSchemaField[])

This directly assumes that schemaFields is always defined. The improvement suggested—checking if schemaFields exists before calling customSchemaFieldsToObject and defaulting to an empty object if not—enhances type safety and guards against potential runtime errors.

  • File to update: web/src/beta/features/PluginPlayground/Code/hook.ts (lines 121–125)
  • Suggested change:
                       property: {
    -                      default: customSchemaFieldsToObject(
    -                        schemaFields as CustomSchemaField[]
    -                      )
    +                      default: schemaFields ? 
    +                        customSchemaFieldsToObject(schemaFields) : {}
                       },

Copy link

@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: 2

🧹 Nitpick comments (4)
web/src/beta/features/PluginPlayground/types.ts (1)

46-58: Consider adding validation constraints to FieldValue type.

While the type definition is correct, consider adding validation constraints or branded types for specific field types (e.g., range validation for numbers, pattern validation for strings).

web/src/beta/features/PluginPlayground/ExtensionSettings/index.tsx (1)

43-45: Improve optional chaining usage.

Optimize the code by using optional chaining operator.

-  const ymlFile =
-    selectedPlugin.files &&
-    selectedPlugin.files.find((f) => f.title.endsWith("reearth.yml"));
+  const ymlFile = selectedPlugin.files?.find((f) => f.title.endsWith("reearth.yml"));
🧰 Tools
🪛 Biome (1.9.4)

[error] 44-45: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx (1)

58-198: Refactor conditional rendering for better maintainability.

The deep nesting of conditional rendering makes the code hard to maintain. Consider extracting each field type into a separate component.

Example refactor for string fields:

const StringField: FC<FieldProps> = ({ field, value, handleChange }) => {
  switch (field.ui) {
    case 'datetime':
      return <TimePointField ... />;
    case 'selection':
      return <SelectField ... />;
    // ... other cases
    default:
      return <InputField ... />;
  }
};
web/src/beta/features/PluginPlayground/Code/hook.ts (1)

230-230: Optimize useCallback dependencies.

The schemaFields dependency in the useCallback hook's dependency array might cause unnecessary re-renders as it's only used in the type assertion.

Remove the unnecessary dependency:

-  }, [updatedField, schemaFields, files, resetVisualizer, setNotification]);
+  }, [updatedField, files, resetVisualizer, setNotification]);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05e9457 and 5df7e0e.

📒 Files selected for processing (7)
  • web/src/beta/features/PluginPlayground/Code/hook.ts (3 hunks)
  • web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/ExtensionSettings/index.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/Plugins/presets/custom/myPlugin.ts (1 hunks)
  • web/src/beta/features/PluginPlayground/hooks.tsx (3 hunks)
  • web/src/beta/features/PluginPlayground/index.tsx (2 hunks)
  • web/src/beta/features/PluginPlayground/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • web/src/beta/features/PluginPlayground/index.tsx
  • web/src/beta/features/PluginPlayground/Plugins/presets/custom/myPlugin.ts
🧰 Additional context used
🪛 Biome (1.9.4)
web/src/beta/features/PluginPlayground/ExtensionSettings/index.tsx

[error] 44-45: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

web/src/beta/features/PluginPlayground/Code/hook.ts

[error] 209-209: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: ci-server / ci-server-lint
  • GitHub Check: ci-server / ci-server-test
  • GitHub Check: ci-web / ci
  • GitHub Check: Redirect rules - reearth-web
  • GitHub Check: Header rules - reearth-web
  • GitHub Check: Pages changed - reearth-web
🔇 Additional comments (7)
web/src/beta/features/PluginPlayground/types.ts (1)

10-31: LGTM! Well-structured type definition for ReearthYML.

The type definition is comprehensive and properly typed, with clear hierarchical structure for plugin configuration.

web/src/beta/features/PluginPlayground/hooks.tsx (1)

200-207: Avoid type casting and add runtime validation.

The type casting of schemaFields could be unsafe. Consider adding runtime validation before the cast.

web/src/beta/features/PluginPlayground/Code/hook.ts (5)

10-44: LGTM! Well-structured type definitions.

The type definitions are comprehensive and properly encapsulate all necessary functionality for the widget panel, including schema field management.


46-62: LGTM! Clean state management setup.

The state management for schema fields and field updates is well-organized and properly typed.


209-209: Fix performance issue in schema field accumulation.

The spread operator in the accumulator causes O(n²) time complexity. This can impact performance when dealing with a large number of schema fields.

🧰 Tools
🪛 Biome (1.9.4)

[error] 209-209: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


212-227: Improve type safety in field value updates.

The current implementation assumes the value type is string when updating the field value, which could lead to type-related issues.


121-123: Add null check for schema fields.

The type assertion schemaFields as CustomSchemaField[] could be unsafe if schemaFields is undefined.

Add a null check or provide a default value:

-                      property: customSchemaFieldsToObject(
-                        schemaFields as CustomSchemaField[]
-                      ),
+                      property: customSchemaFieldsToObject(
+                        schemaFields ?? []
+                      ),
✅ Verification successful

Add null-check for schemaFields in hook.ts

The review comment is valid. Relying on a type assertion (schemaFields as CustomSchemaField[]) is unsafe if schemaFields is undefined, which could lead to runtime issues. Updating the code to use the nullish coalescing operator (i.e., schemaFields ?? []) ensures a safe default value is provided.

  • Location: web/src/beta/features/PluginPlayground/Code/hook.ts, lines 121–123
  • Action: Replace the type assertion with a null check:
    -                      property: customSchemaFieldsToObject(
    -                        schemaFields as CustomSchemaField[]
    -                      ),
    +                      property: customSchemaFieldsToObject(
    +                        schemaFields ?? []
    +                      ),

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx (3)

23-33: Add JSDoc comments to document the Props type.

Consider adding JSDoc comments to document the purpose and usage of each prop for better maintainability.

+/**
+ * Props for the PropertyItem component
+ * @property {FieldValue} initialValue - The initial value of the field
+ * @property {SchemaField} field - The schema definition for the field
+ * @property {Function} setUpdatedField - Callback function to update the field value
+ */
 type Props = {
   initialValue: FieldValue;
   field: SchemaField;
   setUpdatedField: ({

37-47: Simplify the assetTypes computation logic.

The nested ternary operators make the code harder to read. Consider using a more straightforward approach.

-  const assetTypes: "image"[] | "file"[] | undefined = useMemo(
-    () =>
-      field.type === "url"
-        ? field.ui === "image"
-          ? ["image" as const]
-          : field.ui === "file"
-            ? ["file" as const]
-            : undefined
-        : undefined,
-    [field.type, field.ui]
-  );
+  const assetTypes = useMemo(() => {
+    if (field.type !== "url") return undefined;
+    if (field.ui === "image") return ["image" as const];
+    if (field.ui === "file") return ["file" as const];
+    return undefined;
+  }, [field.type, field.ui]);

63-63: Add type guards to avoid type assertions.

Replace type assertions with proper type guards to improve type safety.

const isStringValue = (value: FieldValue): value is string => 
  typeof value === 'string';

const isNumberValue = (value: FieldValue): value is number =>
  typeof value === 'number';

// Then in your JSX:
value={isStringValue(value) ? value : ''}

Also applies to: 71-71, 87-87, 97-97, 104-104, 119-119, 126-126, 137-137, 145-145, 155-155, 167-167, 175-175, 183-183

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5df7e0e and 23b64a6.

📒 Files selected for processing (1)
  • web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: ci-server / ci-server-lint
  • GitHub Check: ci-server / ci-server-test
  • GitHub Check: ci-web / ci
  • GitHub Check: Redirect rules - reearth-web
  • GitHub Check: Header rules - reearth-web
  • GitHub Check: Pages changed - reearth-web
🔇 Additional comments (1)
web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx (1)

49-54: Replace setTimeout with proper debounce utility.

Using setTimeout for debouncing can lead to memory leaks. Consider using a proper debounce utility.

@mulengawilfred
Copy link
Contributor Author

Will put this as a WIP. Current challenge is updating the state of individual input fields as well as updating the corresponding value in the web/src/beta/features/PluginPlayground/Code/hook.ts. Seems like there is a race-condition for the state and not all input fields in web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx

@mulengawilfred mulengawilfred changed the title feat(web): add widget panel to plugin playground [WIP] feat(web): add widget panel to plugin playground Feb 6, 2025
@airslice airslice changed the title [WIP] feat(web): add widget panel to plugin playground feat(web): add extension settings panel to plugin playground Feb 6, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx (1)

52-192: 🛠️ Refactor suggestion

Refactor conditional rendering logic for better maintainability.

The nested ternary operators and if-else chains make the code hard to maintain. This is a duplicate of a previous review comment, but it's still valid.

Additionally, consider adding error boundaries to handle potential runtime errors in field rendering:

+import { ErrorBoundary } from 'react-error-boundary';
+
+const FieldErrorFallback = ({ error, field }) => (
+  <div>
+    <p>Error rendering {field.name}: {error.message}</p>
+  </div>
+);
+
 const PropertyItem: FC<Props> = ({ id, value, field, onUpdate }) => {
   // ... existing code ...
   
   return (
+    <ErrorBoundary
+      FallbackComponent={({ error }) => (
+        <FieldErrorFallback error={error} field={field} />
+      )}
+    >
       {/* existing rendering logic */}
+    </ErrorBoundary>
   );
 };
🧹 Nitpick comments (4)
web/src/beta/features/PluginPlayground/ExtensionSettings/index.tsx (1)

31-33: Simplify with optional chaining.

The check for selectedPlugin.files can be simplified.

-  const ymlFile =
-    selectedPlugin.files &&
-    selectedPlugin.files.find((f) => f.title.endsWith("reearth.yml"));
+  const ymlFile = selectedPlugin.files?.find((f) => f.title.endsWith("reearth.yml"));
🧰 Tools
🪛 Biome (1.9.4)

[error] 32-33: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx (1)

1-21: Consider organizing imports for better maintainability.

The imports could be better organized by grouping them into:

  1. External dependencies
  2. Internal UI components
  3. Types and utilities
+// External dependencies
 import { FC, useCallback, useMemo } from "react";
 
+// Internal types and utilities
 import { SpacingValues } from "@reearth/beta/ui/fields/SpacingField";
 import { Camera, LatLng } from "@reearth/beta/utils/value";
 import { SchemaField } from "@reearth/services/api/propertyApi/utils";
 import { FieldValue } from "../types";
 
+// UI components
 import {
   ColorField,
   InputField,
   SelectField,
   TimePointField,
   TextareaField,
   AssetField,
   SpacingField,
   SwitchField,
   SliderField,
   NumberField,
   TwinInputField,
   CameraField,
   RangeField
 } from "../../../ui/fields";
web/src/beta/features/PluginPlayground/hooks.tsx (2)

29-31: Consider persisting field values in local storage.

Based on the PR objectives mentioning "input field data will be stored locally", consider persisting the field values in local storage to maintain state across page reloads.

+import { useLocalStorage } from '@reearth/beta/hooks';
+
-const [fieldValues, setFieldValues] = useState<Record<string, FieldValue>>({});
+const [fieldValues, setFieldValues] = useLocalStorage<Record<string, FieldValue>>(
+  'plugin-playground-field-values',
+  {}
+);

197-204: Consider adding loading and error states.

The ExtensionSettingsPanel should handle loading and error states when interacting with field values.

 const ExtensionSettingsPanel: FC = () => (
+  <Suspense fallback={<div>Loading settings...</div>}>
+    <ErrorBoundary fallback={<div>Error loading settings</div>}>
       <ExtensionSettings
         selectedPlugin={selectedPlugin}
         selectedFile={selectedFile}
         fieldValues={fieldValues}
         setFieldValues={setFieldValues}
       />
+    </ErrorBoundary>
+  </Suspense>
 );
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f7a8c3 and 80dfe1e.

📒 Files selected for processing (7)
  • web/src/beta/features/PluginPlayground/Code/hook.ts (4 hunks)
  • web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/ExtensionSettings/index.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/Plugins/presets/extension/extensionProperty.ts (1 hunks)
  • web/src/beta/features/PluginPlayground/Plugins/presets/index.ts (2 hunks)
  • web/src/beta/features/PluginPlayground/hooks.tsx (4 hunks)
  • web/src/beta/features/PluginPlayground/index.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/beta/features/PluginPlayground/index.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
web/src/beta/features/PluginPlayground/ExtensionSettings/index.tsx

[error] 32-33: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Redirect rules - reearth-web
  • GitHub Check: Header rules - reearth-web
  • GitHub Check: Pages changed - reearth-web
🔇 Additional comments (6)
web/src/beta/features/PluginPlayground/Plugins/presets/extension/extensionProperty.ts (1)

1-67: LGTM! Well-structured plugin definition.

The implementation is clean and follows best practices:

  • Clear separation between schema (YAML) and UI (widget) code
  • Proper event handling for property updates
  • Good use of constants and types
web/src/beta/features/PluginPlayground/Plugins/presets/index.ts (1)

8-8: LGTM! Clean addition of extension category.

The changes properly integrate the new extension property plugin into the preset plugins structure.

Also applies to: 99-103

web/src/beta/features/PluginPlayground/ExtensionSettings/index.tsx (1)

25-102: LGTM! Well-structured component with proper error handling.

The component implementation:

  • Properly validates YAML structure
  • Handles errors gracefully
  • Uses appropriate React patterns
🧰 Tools
🪛 Biome (1.9.4)

[error] 32-33: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

web/src/beta/features/PluginPlayground/Code/hook.ts (1)

32-184: LGTM! Clean implementation of field value handling.

The hook implementation:

  • Properly integrates field values
  • Has good error handling
  • Uses appropriate React patterns with useCallback
🧰 Tools
🪛 Biome (1.9.4)

[error] 83-83: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 135-135: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx (1)

43-50: Consider adding error handling for undefined values.

The handleChange callback silently ignores undefined values. Consider whether this is the intended behavior or if we should handle these cases explicitly.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for similar patterns in the codebase to verify the convention
rg -A 5 'const handleChange.*useCallback.*\(.*\).*=>' 

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Let's search within the file to see the full context of handleChange and similar patterns.
rg -A 10 "const handleChange.*useCallback" web/src/beta/features/PluginPlayground/ExtensionSettings/PropertyItem.tsx

Length of output: 305


Based on the information provided, the callback’s behavior is to simply ignore undefined values by not calling onUpdate when newValue is not provided. This pattern appears intentional and consistent with the function’s annotated behavior (using an optional parameter), as there’s no evidence elsewhere in the file (or from similar examples in the codebase) that suggests a need to treat undefined values as an error case.

Therefore, the current behavior seems acceptable if the intended design is to only trigger updates when a valid value is provided.

Final Recommendation:

  • The design choice to ignore undefined values appears deliberate.
  • No additional error handling seems necessary unless future requirements dictate stricter checking.
web/src/beta/features/PluginPlayground/hooks.tsx (1)

49-53: Verify the impact of fieldValues on code execution.

The PR comments mention a race condition affecting state management. Let's verify the usage of fieldValues in the useCode hook.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for fieldValues usage in useCode hook
ast-grep --pattern 'const useCode = ($params) => {
  $$$
  fieldValues
  $$$
}'

Length of output: 81


I'll generate additional scripts to search for the definition of the useCode hook and all its references to fieldValues in the codebase.


🏁 Script executed:

#!/bin/bash
echo "Searching for the definition of the useCode hook:"
rg -n "const useCode\\s*=" .

echo ""
echo "Searching for all occurrences of 'fieldValues' in the codebase:"
rg -n "fieldValues" .

Length of output: 1811


Subject: FieldValues Handling in useCode Hook is Appropriate

After re-examining the implementation across the PluginPlayground:

  • The fieldValues state is declared and managed in hooks.tsx and then passed to the useCode hook.
  • In web/src/beta/features/PluginPlayground/Code/hook.ts, fieldValues is consistently used—most notably as part of a dependency array (e.g., on line 184) and when computing derived values—ensuring updated state is properly captured.

These findings indicate that changes to fieldValues trigger the intended hook re-execution, mitigating the race condition concerns noted in the PR comments.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (9)
web/src/beta/features/PluginPlayground/Plugins/presets/extension/extensionProperty.ts (1)

62-90: Enhance error handling and user feedback.

While the widget implementation is functional, consider these improvements:

  1. Add error handling for message parsing
  2. Provide visual feedback when properties are updated
 const widgetFile: FileType = {
   id: "extension-property-widget",
   title: "extension-property-widget.js",
   sourceCode: `reearth.ui.show(\`
     ${PRESET_PLUGIN_COMMON_STYLE}
     <div id="wrapper">
       <h2>Extension Property</h2>
       <h3 style="text-align:center">Input on Extension Settings and execute code again.</h3>
       <h3 style="text-align:center" id="text"></h3>
+      <div id="status" style="text-align:center; opacity: 0; transition: opacity 0.3s;">
+        Properties updated!
+      </div>
     </div>

     <script>
       window.addEventListener("message", e => {
-        const msg = e.data;
-        if (msg.type === "getWidgetProperty") {
-          document.getElementById("text").textContent = msg.property?.default?.text ?? "";
-          document.getElementById("text").style.color = msg.property?.default?.color ?? "";
+        try {
+          const msg = e.data;
+          if (msg.type === "getWidgetProperty") {
+            document.getElementById("text").textContent = msg.property?.default?.text ?? "";
+            document.getElementById("text").style.color = msg.property?.default?.color ?? "";
+            
+            // Show status message
+            const status = document.getElementById("status");
+            status.style.opacity = "1";
+            setTimeout(() => {
+              status.style.opacity = "0";
+            }, 2000);
+          }
+        } catch (error) {
+          console.error("Error processing message:", error);
         }
       });
     </script>
web/src/beta/features/PluginPlayground/types.ts (5)

10-31: Add JSDoc documentation for better maintainability.

Consider adding JSDoc comments to document the purpose and structure of the ReearthYML type and its properties.

+/**
+ * Represents the structure of a Re:Earth plugin's YAML configuration.
+ */
 export type ReearthYML = {
+  /** Unique identifier for the plugin */
   id: string;
+  /** Display name of the plugin */
   name: string;
+  /** Semantic version of the plugin */
   version: string;
+  /** List of plugin extensions */
   extensions?: {
     id: string;
     type: string;
     name: string;
     description: string;
     widgetLayout?: {
       extended: boolean;
       defaultLocation: {
         zone: WidgetLocation["zone"];
         section: WidgetLocation["section"];
         area: WidgetLocation["area"];
       };
     };
     schema?: {
       groups: Group;
     };
   }[];
 };

33-33: Add JSDoc documentation for the Widgets type.

Consider adding a JSDoc comment to explain the purpose of this type alias.

+/** Type representing the widgets prop of the Visualizer component */
 export type Widgets = ComponentProps<typeof Visualizer>["widgets"];

35-46: Improve type safety and add documentation.

  1. The property field could be more type-safe by using the FieldValue type.
  2. Consider adding JSDoc comments for better documentation.
+/**
+ * Represents a custom infobox block extension.
+ */
 export type CustomInfoboxBlock = {
   id: string;
   name: string;
   description: string;
   __REEARTH_SOURCECODE: string;
   extensionId: string;
   pluginId: string;
-  property: Record<string, unknown>;
+  property: Record<string, Record<string, FieldValue>>;
   extensionType: "storyBlock";
 };

+/** Type alias for CustomInfoboxBlock used in story blocks */
 export type CustomStoryBlock = CustomInfoboxBlock;

48-60: Add JSDoc documentation for field-related types.

Consider adding JSDoc comments to document the purpose of these types.

+/** Union type of all possible field values */
 export type FieldValue =
   | string
   | number
   | boolean
   | LatLng
   | number[]
   | string[]
   | SpacingValues;

+/**
+ * Extends the base Field type with additional properties for customization.
+ */
 export interface CustomField extends Field {
   id?: string;
   ui?: string;
   defaultValue?: FieldValue;
 }

62-70: Improve type naming and add documentation.

  1. Consider renaming Group to SchemaGroup for better clarity.
  2. Add JSDoc comments to document the purpose of these types.
+/** Extends SchemaField with an optional value property */
 export interface CustomSchemaField extends SchemaField {
   value?: FieldValue;
 }

-export type Group = {
+/** Represents a group of schema fields */
+export type SchemaGroup = {
   id: string;
   title: string;
   fields: SchemaField[];
 }[];
web/src/beta/features/PluginPlayground/Code/hook.ts (3)

18-30: Add JSDoc documentation for type definitions.

Consider adding JSDoc comments to document the purpose of these types.

+/** Props for the plugin playground hook */
 type Props = {
   files: FileType[];
   fieldValues: Record<string, FieldValue>;
   resetVisualizer: () => void;
 };

+/** Return type for the plugin playground hook */
 type HookReturnType = {
   executeCode: () => void;
   infoboxBlocks?: CustomInfoboxBlock[];
   schemaFields?: CustomSchemaField[];
   story?: Story;
   widgets?: Widgets;
 };

32-55: Enhance generateProperty function with documentation and validation.

Consider adding JSDoc documentation and improving input validation.

+/**
+ * Generates a property object from schema and field values.
+ * @param schema - The schema containing groups and fields
+ * @param fieldValues - Record of field values indexed by field ID
+ * @param pluginId - The plugin identifier
+ * @param extensionId - The extension identifier
+ * @returns Record of property values grouped by group ID
+ */
 function generateProperty(
   schema:
     | {
         groups: Group;
       }
     | undefined,
   fieldValues: Record<string, FieldValue>,
   pluginId: string,
   extensionId: string
-) {
+): Record<string, Record<string, FieldValue>> {
+  if (!pluginId || !extensionId) {
+    throw new Error("Plugin ID and Extension ID are required");
+  }
+
   const property: Record<string, unknown> = {};
   if (!schema || !schema.groups) return property;

220-220: Optimize useCallback dependencies.

Remove setNotification from the dependency array as it's stable (from useNotification hook).

-  }, [files, fieldValues, resetVisualizer, setNotification]);
+  }, [files, fieldValues, resetVisualizer]);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76ebf1d and d79f315.

📒 Files selected for processing (4)
  • web/src/beta/features/PluginPlayground/Code/hook.ts (6 hunks)
  • web/src/beta/features/PluginPlayground/Plugins/presets/extension/extensionProperty.ts (1 hunks)
  • web/src/beta/features/PluginPlayground/Plugins/presets/index.ts (2 hunks)
  • web/src/beta/features/PluginPlayground/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/beta/features/PluginPlayground/Plugins/presets/index.ts
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: deploy-server / deploy_test
  • GitHub Check: Redirect rules - reearth-web
  • GitHub Check: Header rules - reearth-web
  • GitHub Check: Pages changed - reearth-web
🔇 Additional comments (5)
web/src/beta/features/PluginPlayground/Plugins/presets/extension/extensionProperty.ts (3)

1-2: LGTM!

The imports are clean and appropriate for the plugin implementation.


4-60: Consider reducing schema duplication.

The schema structure is identical across all three extensions (widget, infobox block, and story block). Consider extracting the common schema into a shared configuration to improve maintainability.

 const yamlFile: FileType = {
   id: "extension-property-reearth-yml",
   title: "reearth.yml",
+  const commonSchema = {
+    groups: [{
+      id: "default",
+      title: "Default",
+      fields: [
+        { id: "text", type: "string", name: "Text" },
+        { id: "color", type: "string", name: "Color", ui: "color" }
+      ]
+    }]
+  };
   sourceCode: `id: extension-property-plugin
 name: Extension Property
 version: 1.0.0
 extensions:
   - id: extension-property-widget
     type: widget
     name: Extension Property Widget
     description: Extension Property Widget
-    schema:
-      groups:
-      - id: default
-        title: Default
-        fields:
-          - id: text 
-            type: string
-            name: Text
-          - id: color
-            type: string
-            name: Color
-            ui: color
+    schema: ${JSON.stringify(commonSchema, null, 2)}
   - id: infobox-block
     type: infoboxBlock
     name: Infobox Block
-    schema:
-      groups:
-      - id: default
-        title: Default
-        fields:
-          - id: text 
-            type: string
-            name: Text
-          - id: color
-            type: string
-            name: Color
-            ui: color
+    schema: ${JSON.stringify(commonSchema, null, 2)}
   - id: story-block
     type: storyBlock
     name: Story Block
-    schema:
-      groups:
-      - id: default
-        title: Default
-        fields:
-          - id: text 
-            type: string
-            name: Text
-          - id: color
-            type: string
-            name: Color
-            ui: color
+    schema: ${JSON.stringify(commonSchema, null, 2)}

Verify field types coverage.

Based on the PR objectives, you mentioned that "only text, number, and boolean fields have been added". However, I notice that the schema only includes text fields (string and color). Consider if number and boolean fields should be added to this schema as well.


150-154: LGTM!

The plugin export is correctly structured with all necessary components.

web/src/beta/features/PluginPlayground/types.ts (1)

1-9: LGTM!

The imports are well-organized and properly separated into external and internal dependencies.

web/src/beta/features/PluginPlayground/Code/hook.ts (1)

167-169: Address TODO comment regarding extensionType.

The hardcoded "storyBlock" value for extensionType needs to be fixed. This appears to be a workaround for a type definition issue.

Could you provide more context about the type definition error? This would help in implementing a proper fix instead of using a workaround.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
web/src/beta/features/PluginPlayground/types.ts (3)

10-31: Add JSDoc comments to document the ReearthYML type.

The ReearthYML type is well-structured but would benefit from documentation explaining its purpose and the significance of each field, especially for plugin developers.

Add JSDoc comments like this:

+/**
+ * Represents the structure of a reearth.yml configuration file.
+ * This file defines the metadata and configuration for a Re:Earth plugin.
+ */
 export type ReearthYML = {
+  /** Unique identifier for the plugin */
   id: string;
+  /** Display name of the plugin */
   name: string;
+  /** Semantic version of the plugin */
   version: string;
+  /** List of plugin extensions */
   extensions?: {
     id: string;
     type: string;
     name: string;
     description: string;
     widgetLayout?: {
       extended: boolean;
       defaultLocation: {
         zone: WidgetLocation["zone"];
         section: WidgetLocation["section"];
         area: WidgetLocation["area"];
       };
     };
     schema?: {
       groups: Group;
     };
   }[];
 };

35-51: Consider improving type safety and documentation for block types.

Two suggestions for improvement:

  1. The propertyForPluginAPI field uses Record<string, unknown> which is too permissive and could lead to type-safety issues.
  2. The block types would benefit from JSDoc documentation.

Consider these improvements:

+/** Base type for all block types with common properties */
 type CommonBlock = {
   id: string;
   name: string;
   description: string;
   __REEARTH_SOURCECODE: string;
   extensionId: string;
   pluginId: string;
-  propertyForPluginAPI: Record<string, unknown>;
+  propertyForPluginAPI: Record<string, FieldValue>;
 };

+/** Block type for infobox extensions */
 export type CustomInfoboxBlock = CommonBlock & {
   extensionType: "infoboxBlock";
 };

+/** Block type for story extensions */
 export type CustomStoryBlock = CommonBlock & {
   extensionType: "storyBlock";
 };

71-75: Consider making the array type more explicit.

The Group type could be more clearly defined to show it's an array type.

Consider this alternative definition:

-export type Group = {
+export type GroupItem = {
   id: string;
   title: string;
   fields: SchemaField[];
-}[];
+};
+export type Group = GroupItem[];
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d79f315 and a2e87c6.

📒 Files selected for processing (3)
  • web/src/beta/features/PluginPlayground/Code/hook.ts (6 hunks)
  • web/src/beta/features/PluginPlayground/Plugins/presets/extension/extensionProperty.ts (1 hunks)
  • web/src/beta/features/PluginPlayground/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • web/src/beta/features/PluginPlayground/Plugins/presets/extension/extensionProperty.ts
  • web/src/beta/features/PluginPlayground/Code/hook.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Redirect rules - reearth-web
  • GitHub Check: Header rules - reearth-web
  • GitHub Check: Pages changed - reearth-web
  • GitHub Check: ci-web / ci
  • GitHub Check: ci-server / ci-server-lint
  • GitHub Check: ci-server / ci-server-test
🔇 Additional comments (3)
web/src/beta/features/PluginPlayground/types.ts (3)

1-9: LGTM!

The imports are well-organized and logically grouped, with clear separation between external and internal dependencies.


33-33: LGTM!

The Widgets type is well-defined using React's ComponentProps utility type.


53-69: LGTM!

The field-related types are well-defined:

  • FieldValue provides a comprehensive and type-safe union of possible field values
  • CustomField implements the type-safe defaultValue as suggested in past reviews
  • CustomSchemaField extends SchemaField with an optional value property

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
web/src/beta/features/PluginPlayground/hooks.tsx (2)

41-43: Enhance type safety for field values.

While the implementation works, it could benefit from stronger type safety and validation.

Consider these improvements:

-  const [fieldValues, setFieldValues] = useState<Record<string, FieldValue>>(
-    {}
-  );
+  type FieldValues = Record<string, FieldValue>;
+  
+  const [fieldValues, setFieldValues] = useState<FieldValues>({});
+  
+  const updateFieldValue = useCallback((key: string, value: FieldValue) => {
+    setFieldValues(prev => {
+      // Validate value based on field type
+      if (!isValidFieldValue(value)) return prev;
+      return { ...prev, [key]: value };
+    });
+  }, []);

Also applies to: 63-63


208-215: Optimize ExtensionSettingsPanel performance.

The component could benefit from memoization to prevent unnecessary re-renders.

Apply this diff to optimize the component:

-  const ExtensionSettingsPanel: FC = () => (
+  const ExtensionSettingsPanel: FC = useMemo(() => (
     <ExtensionSettings
       selectedPlugin={selectedPlugin}
       selectedFile={selectedFile}
       fieldValues={fieldValues}
       setFieldValues={setFieldValues}
     />
-  );
+  ), [selectedPlugin, selectedFile, fieldValues, setFieldValues]);

Also applies to: 223-223

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d3179e6 and e4073aa.

📒 Files selected for processing (2)
  • web/src/beta/features/PluginPlayground/SettingsList/index.tsx (1 hunks)
  • web/src/beta/features/PluginPlayground/hooks.tsx (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Redirect rules - reearth-web
  • GitHub Check: Header rules - reearth-web
  • GitHub Check: Pages changed - reearth-web
  • GitHub Check: ci-server / ci-server-lint
  • GitHub Check: ci-server / ci-server-test
  • GitHub Check: ci-web / ci
🔇 Additional comments (2)
web/src/beta/features/PluginPlayground/SettingsList/index.tsx (1)

31-31: LGTM! Improved state management pattern.

The change from toggle-based to direct value assignment is a better practice as it:

  1. Relies on the actual checkbox value rather than inferring it
  2. Follows React's controlled component pattern more closely
web/src/beta/features/PluginPlayground/hooks.tsx (1)

7-7: LGTM! Required imports for extension settings.

The imports are correctly added to support the new extension settings panel functionality.

Also applies to: 13-13

const [enabledVisualizer, setEnabledVisualizer] = useState(true);
const [showStoryPanel, setShowStoryPanel] = useState(false);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix potential race conditions in story panel state management.

The current implementation has several issues:

  1. Multiple timeouts running concurrently could lead to race conditions
  2. The timeout closure captures stale showStoryPanel state
  3. Timeouts might continue running after component unmount

Apply this diff to fix the issues:

-  const resetVisualizer = useCallback(() => {
-    setEnabledVisualizer(false);
-    setShowStoryPanel(false);
-    const timeoutId = setTimeout(() => {
-      setEnabledVisualizer(true);
-    }, 0);
-    let showStoryPanelTimeout: NodeJS.Timeout | undefined;
-    if (showStoryPanel) {
-      showStoryPanelTimeout = setTimeout(() => {
-        setShowStoryPanel(showStoryPanel);
-      }, 100);
-    }
-
-    return () => {
-      clearTimeout(timeoutId);
-      clearTimeout(showStoryPanelTimeout);
-    };
-  }, [showStoryPanel]);
+  const resetVisualizer = useCallback(() => {
+    setEnabledVisualizer(false);
+    setShowStoryPanel(false);
+    const timeoutId = setTimeout(() => {
+      setEnabledVisualizer(true);
+      // Restore story panel state after visualizer is enabled
+      setShowStoryPanel(true);
+    }, 0);
+
+    return () => {
+      clearTimeout(timeoutId);
+    };
+  }, []);

Also applies to: 24-39

@mulengawilfred mulengawilfred merged commit 165b140 into main Feb 12, 2025
19 checks passed
@mulengawilfred mulengawilfred deleted the feat/plugin-playground-widgets-panel branch February 12, 2025 08:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants