Skip to content

Conversation

@MinCrohn
Copy link
Collaborator

📑 Overview

This update focuses on improving the core architecture of the patch-map library to provide users with more powerful and flexible event-driven interactions. We've introduced a WildcardEventEmitter to refactor key modules (Patchmap, StateManager, UndoRedoManager) to be event-driven and added a new SelectionModel for managing the selection state within the Transformer.

✨ What's New? (New Features)

1. Powerful Wildcard Event System Introduced

The core classes of the library now inherit from WildcardEventEmitter, which supports namespaces and wildcards (*). This allows users to subscribe to specific events individually or subscribe to all events within a specific feature group at once, making event management more flexible and concise.

Code Example:

const patchmap = new Patchmap();
// ... initialize patchmap ...

// Subscribe only to the 'undone' event in the 'history' namespace
patchmap.undoRedoManager.on('history:undone', ({ command }) => {
  console.log('Undo executed:', command);
});

// Subscribe to all events in the 'history' namespace (*: executed, undone, redone, etc.)
patchmap.undoRedoManager.on('history:*', ({ type, command }) => {
  console.log(`History event occurred: ${type}`, command);
  // Logic to update the enabled state of UI undo/redo buttons, etc.
});

2. Detailed Lifecycle Events Provided

Patchmap and StateManager now emit detailed lifecycle events for major operations such as initialization, drawing, updating, and state changes. This makes it easier to implement logic that corresponds to the application's state changes.

Code Example:

// Subscribe to Patchmap instance lifecycle events
patchmap.on('patchmap:initialized', () => {
  console.log('Patchmap has been successfully initialized.');
});

patchmap.on('patchmap:draw', ({ data }) => {
  console.log('New data has been rendered.', data);
});

// Subscribe to StateManager state change events
patchmap.stateManager.on('state:set', ({ state }) => {
  console.log(`State has been changed to ${state.constructor.name}.`);
});

3. Selection State Management Model (SelectionModel) Added

A new SelectionModel class has been introduced to professionally manage the selection state of the Transformer. You can now easily control selected elements programmatically via transformer.selection and handle subsequent actions through events that are triggered whenever the selection state changes.

Code Example:

const transformer = new Transformer();
patchmap.transformer = transformer;

const item1 = patchmap.selector('$..[?(@.id=="item-1")]')[0];
const item2 = patchmap.selector('$..[?(@.id=="item-2")]')[0];

// Add/remove selected elements
transformer.selection.add(item1);
transformer.selection.add([item1, item2]); // Duplicate elements are ignored
transformer.selection.remove(item1);
transformer.selection.set([item2]); // Replaces all selections with item2

// Subscribe to selection change events
transformer.on('update_elements', ({ current, added, removed }) => {
  console.log('Selection has changed.');
  console.log('Currently selected elements:', current);
  console.log('Added elements:', added);
  console.log('Removed elements:', removed);
  // Logic to display the list of selected elements in the UI, etc.
});

🔄 What's Changed? (Changes & Refactoring)

1. Transformer Internal Structure Improvement

The Transformer has been refactored to use the SelectionModel internally. The .elements property is still supported, but it is now recommended to subscribe to events through the selection model.

⚠️ Breaking Changes

UndoRedoManager API Change: subscribe -> on

The event subscription method for UndoRedoManager has been changed to follow the universal EventEmitter pattern. The existing subscribe method has been removed, and you must now use the on method to subscribe to more specific events.

Before:

// Previously, only a single 'change' event could be subscribed to.
const unsubscribe = patchmap.undoRedoManager.subscribe((manager) => {
  const canUndo = manager.canUndo();
  const canRedo = manager.canRedo();
  // UI update logic...
});

// Unsubscribe
unsubscribe();

After:

// Now, use the 'history:*' wildcard or individual events to detect state changes.
const onHistoryChange = ({ type }) => { // 'type' is 'executed', 'undone', 'redone', etc.
  const canUndo = patchmap.undoRedoManager.canUndo();
  const canRedo = patchmap.undoRedoManager.canRedo();
  console.log(`History event: ${type}`);
  // UI update logic...
};

patchmap.undoRedoManager.on('history:*', onHistoryChange);

// Unsubscribe
patchmap.undoRedoManager.off('history:*', onHistoryChange);

📢 Full List of Available Events

Here is a list of subscribable events introduced with this update.

Patchmap

  • patchmap:initialized: Fired when patchmap.init() completes successfully.
  • patchmap:draw: Fired when new data is rendered via patchmap.draw().
  • patchmap:updated: Fired when elements are updated via patchmap.update().
  • patchmap:destroyed: Fired when the instance is destroyed by calling patchmap.destroy().

UndoRedoManager

  • history:executed: Fired when a new command is added to the execution stack.
  • history:undone: Fired when undo() is executed.
  • history:redone: Fired when redo() is executed.
  • history:cleared: Fired when all history is deleted with clear().
  • history:destroyed: Fired when destroy() is called.
  • history:*: Subscribes to all of the above history: namespace events.

StateManager

  • state:pushed: Fired when a new state is added to the stack.
  • state:popped: Fired when the current state is removed from the stack.
  • state:set: Fired when the state stack is reset and a new state is set via setState().
  • state:reset: Fired when all states are removed with resetState().
  • state:destroyed: Fired when destroy() is called.
  • modifier:activated: Fired when a modifier state is activated.
  • modifier:deactivated: Fired when a modifier state is deactivated.
  • state:*: Subscribes to all of the above state: namespace events.
  • modifier:*: Subscribes to all of the above modifier: namespace events.

Transformer

  • update_elements: Fired when the content of transformer.elements or transformer.selection changes.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @MinCrohn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a comprehensive event-driven system to the patch-map library by integrating a new WildcardEventEmitter. This refactoring significantly enhances the flexibility and extensibility of the library, allowing users and internal components to react to various lifecycle and state changes through a unified event mechanism. Key modules like Patchmap, StateManager, and UndoRedoManager have been updated to leverage this new system, and a dedicated SelectionModel has been added for managing element selections within the Transformer.

Highlights

  • Wildcard Event System: Implemented WildcardEventEmitter to enable namespaced events (e.g., history:undone) and wildcard subscriptions (e.g., history:*), providing granular and flexible event handling.
  • Event-Driven Core Modules: Patchmap, StateManager, and UndoRedoManager now extend WildcardEventEmitter, emitting detailed lifecycle and state-change events for better observability and integration.
  • Dedicated Selection Management: Introduced SelectionModel to professionally manage the selection state within the Transformer, allowing programmatic control and event-driven responses to selection changes.
  • UndoRedoManager API Update: The UndoRedoManager's event subscription method has been standardized from subscribe to on, aligning with the new EventEmitter pattern.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a powerful EventEmitter-based event system, which is a significant architectural improvement. The refactoring of core modules like Patchmap, StateManager, and UndoRedoManager to be event-driven is well-executed. The addition of the SelectionModel for managing selection state is also a great enhancement. The overall code quality is high, but I've identified a few areas for improvement, mainly concerning data encapsulation in getters and fixing a test case to align with the new event system. Addressing these points will make the implementation more robust.

@MinCrohn
Copy link
Collaborator Author

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a robust EventEmitter-based event system, which is a significant architectural improvement. The refactoring of Patchmap, StateManager, and UndoRedoManager to be event-driven is well-implemented, and the new SelectionModel effectively encapsulates selection logic. My review includes a minor documentation correction and a critical fix to prevent a potential memory leak in the Patchmap destroy lifecycle.

@MinCrohn MinCrohn merged commit 76f5203 into v0.3.x Aug 21, 2025
@MinCrohn MinCrohn deleted the feat/add-eventEmitter branch August 21, 2025 07:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants