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

refactor: TS integration Minimap.js #474

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

ThatDeparted2061
Copy link
Contributor

@ThatDeparted2061 ThatDeparted2061 commented Feb 11, 2025

Fixes #414

@JoshVarga @Arnabdaz

Summary by CodeRabbit

  • New Features

    • Enhanced minimap functionality for simulation with dynamic scaling, conditional rendering, and improved interactivity.
    • Introduced new type interfaces for simulation elements to boost robustness and clarity.
  • Refactor

    • Transitioned from the legacy minimap implementation to a modern, TypeScript-based approach, streamlining the overall code and DOM handling.
  • Bug Fixes

    • Removed deprecated minimap functionality to improve performance and user experience.

Copy link
Contributor

coderabbitai bot commented Feb 11, 2025

Walkthrough

The pull request removes the legacy JavaScript implementation of the minimap in minimap.js and replaces it with a new TypeScript version in minimap.ts. Additionally, new type interfaces for minimap-related entities have been introduced in minimap.types.ts. The updated TypeScript version encapsulates functions for setting up, rendering, and clearing the minimap, as well as managing visibility through exported functions. These changes support the migration of simulator source files to TypeScript.

Changes

File(s) Change Summary
src/simulator/src/minimap.js Removed the legacy JavaScript file that contained the minimap functionality, including the miniMapArea object and its exported functions.
src/simulator/src/minimap.ts Added a new TypeScript file implementing the minimap functionality with updated methods (setup, play, resolve, clear) and exported functions (updatelastMinimapShown, removeMiniMap).
src/simulator/src/types/minimap.types.ts Introduced new TypeScript interfaces: Wire, CircuitElement, GlobalScope, and MiniMapArea for type definitions related to minimap behavior.

Sequence Diagram(s)

sequenceDiagram
    participant S as Simulator
    participant M as MiniMapArea
    participant C as Canvas

    S->>M: setup(simulationArea)
    M->>C: Configure dimensions and update DOM
    S->>M: play()
    M->>C: Render background and simulation elements
    M-->>M: resolve() - Draw wires and circuit elements
    S->>M: clear() (if in light mode or as needed)
    S->>M: updatelastMinimapShown() / removeMiniMap() on user actions
Loading

Suggested reviewers

  • JoshVarga
  • Arnabdaz

Poem

I'm a rabbit in the code, so spry and free,
Hopping through TypeScript, where bugs flee.
Minimaps now gleam in a fresh, typed light,
Old JS fades into the night.
With each new line, carrots of logic grow,
CodeRabbit cheers as our updates flow!
Happy hops to all, let our devs glow!

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link

netlify bot commented Feb 11, 2025

Deploy Preview for circuitverse ready!

Name Link
🔨 Latest commit aa49f95
🔍 Latest deploy log https://app.netlify.com/sites/circuitverse/deploys/67ac27fdc5f70e000815be67
😎 Deploy Preview https://deploy-preview-474--circuitverse.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
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (7)
src/simulator/src/minimap.ts (5)

3-3: Avoid declaring global variables in TypeScript if possible.
Declaring lightMode globally can introduce maintainability and debugging challenges. Consider using an explicit import or a shared config approach instead.


9-10: Consider removing or properly importing globalScope.
Using declare const globalScope may obscure dependencies. A better practice is to import it from a typed module or pass it as a parameter for improved clarity and testability.


23-69: Consider decomposing the setup method and clarifying the '5px' adjustments.
Collecting the dimension logic and the final canvas adjustments into smaller helper methods may enhance readability and testing. The extra 5px might merit a well-documented constant or a configurable parameter.


71-129: Rename play() to a more self-descriptive term (e.g., render()).
The function is primarily responsible for rendering the minimap rather than “playing” an animation or media. A more descriptive name can improve clarity.


131-136: Unify DOM interaction approach.
Here, jQuery is used to update the z-index, but elsewhere vanilla DOM methods are used. Aligning on a single approach can improve consistency and limit dependency issues.

src/simulator/src/types/minimap.types.ts (2)

1-4: Consider factoring out a Node interface.
Currently, each wire node is an inline object with absX and absY. A separate interface (e.g., Node { absX(): number; absY(): number }) can improve clarity and potential reuse.


15-22: Index signature might undermine type safety.
[key: string]: Wire[] | CircuitElement[] | number | unknown; can mask potential type mismatches. Consider specifying additional properties or using more targeted type definitions to maintain stricter type safety.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1ea73f2 and 9110a0a.

📒 Files selected for processing (3)
  • src/simulator/src/minimap.js (0 hunks)
  • src/simulator/src/minimap.ts (1 hunks)
  • src/simulator/src/types/minimap.types.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/simulator/src/minimap.js
🔇 Additional comments (9)
src/simulator/src/minimap.ts (7)

1-2: Looks good.
No issues spotted with these import statements.


4-7: No immediate concerns.
Import statements align well with the usage seen below.


11-22: Verify element existence before casting.
document.getElementById('miniMapArea') could be null, causing runtime errors when cast to HTMLCanvasElement. Guard against this with a null check or optional chaining.

- canvas: document.getElementById('miniMapArea') as HTMLCanvasElement,
+ const elem = document.getElementById('miniMapArea');
+ if (!elem) {
+   console.warn("miniMapArea element not found!");
+ }
+ canvas: elem as HTMLCanvasElement,

138-140: No issues found.
The lastMiniMapShown object is straightforward.


142-145: Logic appears correct.
This function cleanly updates the timestamp for last known usage.


146-156: No critical issues.
removeMiniMap effectively manages the minimap’s fade-out, respecting interaction checks.


157-157: Export statement is consistent.
No issues found with exporting miniMapArea.

src/simulator/src/types/minimap.types.ts (2)

6-13: Looks good.
Well-defined interface for circuit elements.


24-39: Clear and coherent interface.
These definitions for the minimap area align well with its implementation in minimap.ts.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
src/simulator/src/types/minimap.types.ts (2)

15-22: Consider making the interface more type-safe.

The interface could be improved in two ways:

  1. The index signature is very permissive, allowing any string key with various types. Consider using a discriminated union or specific property names.
  2. The root property is typed as unknown. Consider using a more specific type if possible.

Example of a more type-safe approach:

export interface GlobalScope {
    oy: number;
    ox: number;
    scale: number;
    wires: Wire[];
    root: RootType; // Replace with actual type
    circuitElements: Record<string, CircuitElement[]>;
    // Add other specific properties as needed
}

25-49: Add return type annotations to methods.

The interface methods should have explicit return type annotations for better type safety and documentation.

Example improvements:

export interface MiniMapArea {
    // ... existing properties ...
    setup(): void;
    initializeCanvas(): void;
    calculateBounds(): void;
    calculateBound(simulationBound: number | undefined, defaultBound: number): number;
    adjustCanvasSize(): void;
    initializeContext(): void;
    play(ratio: number): void;
    resolve(ratio: number): void;
    drawBackground(ratio: number): void;
    drawCircuitElements(ratio: number): void;
    drawWires(ratio: number): void;
    drawComponents(item: string, ratio: number): void;
    clear(): void;
}
src/simulator/src/minimap.ts (3)

52-73: Extract magic numbers into named constants.

The method uses magic numbers (250.0, 5) which should be extracted into named constants for better maintainability.

+ const MAX_MINIMAP_DIMENSION = 250.0;
+ const PADDING = 5;
 
  adjustCanvasSize() {
      const h = this.maxY - this.minY;
      const w = this.maxX - this.minX;
-     const ratio = Math.min(250 / h, 250 / w);
+     const ratio = Math.min(MAX_MINIMAP_DIMENSION / h, MAX_MINIMAP_DIMENSION / w);

      if (h > w) {
-         this.canvas.height = 250.0;
+         this.canvas.height = MAX_MINIMAP_DIMENSION;
          this.canvas.width = (250.0 * w) / h;
      } else {
-         this.canvas.width = 250.0;
+         this.canvas.width = MAX_MINIMAP_DIMENSION;
          this.canvas.height = (250.0 * h) / w;
      }

-     this.canvas.height += 5;
-     this.canvas.width += 5;
+     this.canvas.height += PADDING;
+     this.canvas.width += PADDING;

156-161: Consider using native DOM API instead of jQuery.

The clear method uses jQuery for DOM manipulation. Consider using native DOM API for better performance and reduced dependencies.

  clear() {
      if (lightMode) return;
-     $('#miniMapArea').css('z-index', '-1');
+     this.canvas.style.zIndex = '-1';
      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  }

171-180: Simplify time comparison and consider using native DOM API.

The removeMiniMap function could be improved by:

  1. Using constants for the timeout duration
  2. Simplifying the time comparison logic
  3. Using native DOM API instead of jQuery
+ const MINIMAP_TIMEOUT_MS = 2000;

  export function removeMiniMap(): void {
      if (lightMode) return;

      if (simulationArea.lastSelected === globalScope.root && simulationArea.mouseDown) return;
-     if (lastMiniMapShown.value + 2000 >= new Date().getTime()) {
-         setTimeout(removeMiniMap, lastMiniMapShown.value + 2000 - new Date().getTime());
+     const timeElapsed = new Date().getTime() - lastMiniMapShown.value;
+     if (timeElapsed < MINIMAP_TIMEOUT_MS) {
+         setTimeout(removeMiniMap, MINIMAP_TIMEOUT_MS - timeElapsed);
          return;
      }
-     $('#miniMap').fadeOut('fast');
+     const miniMap = document.getElementById('miniMap');
+     if (miniMap) {
+         miniMap.style.opacity = '0';
+         miniMap.style.transition = 'opacity 200ms';
+     }
  }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9110a0a and 140bffd.

📒 Files selected for processing (2)
  • src/simulator/src/minimap.ts (1 hunks)
  • src/simulator/src/types/minimap.types.ts (1 hunks)
🔇 Additional comments (3)
src/simulator/src/types/minimap.types.ts (2)

1-4: LGTM!

The Wire interface correctly defines the structure for wire nodes with appropriate method signatures.


6-13: LGTM!

The CircuitElement interface provides a clear and well-structured definition for circuit elements with appropriate property types.

src/simulator/src/minimap.ts (1)

1-10: LGTM!

The imports and declarations are well-organized and properly typed.

src/simulator/src/minimap.ts 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: 2

🧹 Nitpick comments (4)
src/simulator/src/minimap.ts (4)

3-3: Consider moving global variables into a TypeScript module.

Using declare for global variables like lightMode and globalScope works but isn't ideal for TypeScript projects. Consider moving these into a proper TypeScript module for better type safety and maintainability.

-declare const lightMode: boolean;
-declare const globalScope: GlobalScope;
+import { lightMode, globalScope } from './globals';

Also applies to: 9-9


50-54: Simplify the calculateBound method.

The current implementation is hard to read. Consider simplifying it for better maintainability.

-    calculateBound(simulationBound: number | undefined, defaultBound: number): number {
-        return simulationBound !== undefined
-            ? Math[simulationBound === simulationArea.minHeight || simulationBound === simulationArea.minWidth ? 'min' : 'max'](simulationBound, defaultBound)
-            : defaultBound;
+    calculateBound(simulationBound: number | undefined, defaultBound: number): number {
+        if (simulationBound === undefined) return defaultBound;
+        
+        const isMinBound = simulationBound === simulationArea.minHeight || 
+                          simulationBound === simulationArea.minWidth;
+        return isMinBound 
+            ? Math.min(simulationBound, defaultBound)
+            : Math.max(simulationBound, defaultBound);
     },

160-164: Replace jQuery with native DOM API.

Consider using native DOM API instead of jQuery for better performance and reduced dependencies.

     clear() {
         if (lightMode) return;
-        $('#miniMapArea').css('z-index', '-1');
+        this.canvas.style.zIndex = '-1';
         this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
     },

175-184: Improve timeout handling in removeMiniMap.

The timeout logic uses a magic number and could be simplified.

+const MINIMAP_TIMEOUT_MS = 2000;
+
 export function removeMiniMap(): void {
     if (lightMode) return;
 
     if (simulationArea.lastSelected === globalScope.root && simulationArea.mouseDown) return;
-    if (lastMiniMapShown.value + 2000 >= new Date().getTime()) {
-        setTimeout(removeMiniMap, lastMiniMapShown.value + 2000 - new Date().getTime());
+    
+    const currentTime = new Date().getTime();
+    const timeoutEnd = lastMiniMapShown.value + MINIMAP_TIMEOUT_MS;
+    
+    if (currentTime < timeoutEnd) {
+        setTimeout(removeMiniMap, timeoutEnd - currentTime);
         return;
     }
-    $('#miniMap').fadeOut('fast');
+    document.getElementById('miniMap')?.animate(
+        [{ opacity: 1 }, { opacity: 0 }],
+        { duration: 200, fill: 'forwards' }
+    );
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 140bffd and aa49f95.

📒 Files selected for processing (1)
  • src/simulator/src/minimap.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Redirect rules - circuitverse
  • GitHub Check: Header rules - circuitverse
  • GitHub Check: Pages changed - circuitverse
🔇 Additional comments (1)
src/simulator/src/minimap.ts (1)

31-41: LGTM! Null check implemented correctly.

The canvas element null check has been implemented as suggested in the previous review.

Comment on lines +144 to +158
drawComponents(item: string, ratio: number) {
const ledY = ['DigitalLed', 'VariableLed', 'RGBLed'].includes(item) ? 20 : 0;

for (const obj of globalScope[item] as CircuitElement[]) {
this.ctx.beginPath();
this.ctx.rect(
2.5 + (obj.x - obj.leftDimensionX - this.minX) * ratio,
2.5 + (obj.y - obj.upDimensionY - this.minY) * ratio,
(obj.rightDimensionX + obj.leftDimensionX) * ratio,
(obj.downDimensionY + obj.upDimensionY + ledY) * ratio
);
this.ctx.fill();
this.ctx.stroke();
}
},
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 type safety and maintainability in drawComponents.

Several improvements can be made:

  1. Unsafe type casting with as CircuitElement[]
  2. Magic number 20 for LED height
  3. LED type check could be extracted
+    private readonly LED_TYPES = ['DigitalLed', 'VariableLed', 'RGBLed'];
+    private readonly LED_HEIGHT = 20;
+
     drawComponents(item: string, ratio: number) {
-        const ledY = ['DigitalLed', 'VariableLed', 'RGBLed'].includes(item) ? 20 : 0;
+        const ledY = this.LED_TYPES.includes(item) ? this.LED_HEIGHT : 0;
 
-        for (const obj of globalScope[item] as CircuitElement[]) {
+        const components = globalScope[item];
+        if (!Array.isArray(components)) {
+            console.warn(`Invalid component array for ${item}`);
+            return;
+        }
+
+        for (const obj of components as CircuitElement[]) {
📝 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
drawComponents(item: string, ratio: number) {
const ledY = ['DigitalLed', 'VariableLed', 'RGBLed'].includes(item) ? 20 : 0;
for (const obj of globalScope[item] as CircuitElement[]) {
this.ctx.beginPath();
this.ctx.rect(
2.5 + (obj.x - obj.leftDimensionX - this.minX) * ratio,
2.5 + (obj.y - obj.upDimensionY - this.minY) * ratio,
(obj.rightDimensionX + obj.leftDimensionX) * ratio,
(obj.downDimensionY + obj.upDimensionY + ledY) * ratio
);
this.ctx.fill();
this.ctx.stroke();
}
},
private readonly LED_TYPES = ['DigitalLed', 'VariableLed', 'RGBLed'];
private readonly LED_HEIGHT = 20;
drawComponents(item: string, ratio: number) {
const ledY = this.LED_TYPES.includes(item) ? this.LED_HEIGHT : 0;
const components = globalScope[item];
if (!Array.isArray(components)) {
console.warn(`Invalid component array for ${item}`);
return;
}
for (const obj of components as CircuitElement[]) {
this.ctx.beginPath();
this.ctx.rect(
2.5 + (obj.x - obj.leftDimensionX - this.minX) * ratio,
2.5 + (obj.y - obj.upDimensionY - this.minY) * ratio,
(obj.rightDimensionX + obj.leftDimensionX) * ratio,
(obj.downDimensionY + obj.upDimensionY + ledY) * ratio
);
this.ctx.fill();
this.ctx.stroke();
}
},

Comment on lines +11 to +22
const miniMapArea: MiniMapArea = {
canvas: document.getElementById('miniMapArea') as HTMLCanvasElement,
pageY: 0,
pageX: 0,
pageHeight: 0,
pageWidth: 0,
minY: 0,
maxY: 0,
minX: 0,
maxX: 0,
ctx: null as unknown as CanvasRenderingContext2D,

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 object initialization safety.

The current initialization has potential issues:

  1. Direct DOM access during object initialization could fail if the element doesn't exist.
  2. Type casting null as unknown as CanvasRenderingContext2D is unsafe.
 const miniMapArea: MiniMapArea = {
-    canvas: document.getElementById('miniMapArea') as HTMLCanvasElement,
+    canvas: null as unknown as HTMLCanvasElement,
     pageY: 0,
     pageX: 0,
     pageHeight: 0,
     pageWidth: 0,
     minY: 0,
     maxY: 0,
     minX: 0,
     maxX: 0,
-    ctx: null as unknown as CanvasRenderingContext2D,
+    ctx: null as CanvasRenderingContext2D | null,
📝 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 miniMapArea: MiniMapArea = {
canvas: document.getElementById('miniMapArea') as HTMLCanvasElement,
pageY: 0,
pageX: 0,
pageHeight: 0,
pageWidth: 0,
minY: 0,
maxY: 0,
minX: 0,
maxX: 0,
ctx: null as unknown as CanvasRenderingContext2D,
const miniMapArea: MiniMapArea = {
canvas: null as unknown as HTMLCanvasElement,
pageY: 0,
pageX: 0,
pageHeight: 0,
pageWidth: 0,
minY: 0,
maxY: 0,
minX: 0,
maxX: 0,
ctx: null as CanvasRenderingContext2D | null,
// ... any further properties or methods
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Typescript Integration in /simulator/src files
1 participant