-
Notifications
You must be signed in to change notification settings - Fork 14
Refactor Browser God Object #404
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR refactors the HICBrowser "God Object" by extracting distinct responsibilities into dedicated coordinator/manager classes following the Single Responsibility Principle. The refactoring introduces StateManager, NotificationCoordinator, InteractionHandler, DataLoader, and RenderCoordinator to separate state management, UI notifications, user interactions, data loading, and rendering concerns.
Key Changes
- Extracted state management into StateManager class for centralized dataset and state handling
- Created NotificationCoordinator to handle all UI component notifications
- Introduced InteractionHandler for user interaction methods (goto, zoom, pan)
- Added DataLoader to manage Hi-C file, track, and normalization data loading
- Implemented RenderCoordinator to handle rendering coordination and queuing
Reviewed Changes
Copilot reviewed 36 out of 36 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| js/hicBrowser.js | Refactored to delegate responsibilities to new coordinator classes while maintaining backward compatibility |
| js/stateManager.js | New class managing active dataset, state transitions, and cross-browser synchronization |
| js/notificationCoordinator.js | New class coordinating UI component updates when browser state changes |
| js/interactionHandler.js | New class handling navigation, zoom, and pan operations |
| js/dataLoader.js | New class managing data loading operations for Hi-C files, tracks, and normalizations |
| js/renderCoordinator.js | New class coordinating rendering with queuing logic for rapid calls |
| js/hicState.js | Added helper methods for state validation, pixel size adjustment, and update finalization |
| js/urlUtils.js | Added session loading from File objects and file paths with URL shortcut expansion |
| js/session.js | Added URL shortcut expansion for backward compatibility |
| test/testSession.js | New test file for session loading functionality |
| vite.config.js | Added plugins to suppress source map warnings for third-party dependencies |
| various widget files | Added helper methods to encapsulate DOM manipulation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| juicebox.init(document.getElementById("app-container"), config_deep_map) | ||
| juicebox.init(document.getElementById("app-container"), config) |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable config_deep_map is changed to config, but config is not defined in this file. This will cause a ReferenceError at runtime. Either define config or revert to config_deep_map.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot open a new pull request to apply changes based on this feedback
| } catch (error) { | ||
| this.browser.contactMapLabel.textContent = ""; | ||
| this.browser.contactMapLabel.title = ""; | ||
| config.name = name; |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable name is used before it's defined. The line config.name = name references name from line 84, but this code is in the catch block after the try block where name was defined. If an error occurs before line 84, name will be undefined, causing this assignment to fail silently or set config.name to undefined.
| customLogger: { | ||
| warn(msg, options) { | ||
| // Filter out source map warnings for igv dependencies | ||
| const message = typeof msg === 'string' ? msg : String(msg); | ||
| if (message.includes('igv-ui.css.map') || | ||
| message.includes('Failed to load source map') || | ||
| (message.includes('ENOENT') && message.includes('igv'))) { | ||
| return; // Suppress these warnings | ||
| } | ||
| // Use default warning logger for other messages | ||
| console.warn(msg, options); | ||
| }, | ||
| error(msg, options) { | ||
| // Filter out source map errors for igv dependencies | ||
| const message = typeof msg === 'string' ? msg : String(msg); | ||
| if (message.includes('igv-ui.css.map') || | ||
| message.includes('Failed to load source map') || | ||
| (message.includes('ENOENT') && message.includes('igv'))) { | ||
| return; // Suppress these errors | ||
| } | ||
| // Use default error logger for other messages | ||
| console.error(msg, options); |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The custom logger's warn and error methods call console.warn(msg, options) and console.error(msg, options). However, the standard console methods don't accept an options parameter in this way. This may cause issues or unexpected behavior. Consider spreading the arguments or checking if options is defined before passing it.
| "height": 1000, | ||
| "url": "https://www.encodeproject.org/files/ENCFF799QGA/@@download/ENCFF799QGA.hic", | ||
| "name": "in-situ agar GM12878 MboI experiment", | ||
| // "name": "in-situ agar GM12878 MboI experiment", |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Property "name" is commented out but should either be removed entirely or have an explanation for why it's commented out. Leaving commented-out code reduces maintainability.
| // "name": "in-situ agar GM12878 MboI experiment", |
| for (i = 0; i < resolutions.length; i++) { | ||
| if (this.browser.state.zoom === resolutions[i].index) break; | ||
| } | ||
| if (i !== undefined) { |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This guard always evaluates to true.
| if (i !== undefined) { | |
| if (i < resolutions.length) { |
| * @param {boolean} eventData.dragging - Whether currently dragging | ||
| */ | ||
| notifyLocusChange(eventData) { | ||
| const { state, resolutionChanged, chrChanged, dragging } = eventData; |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable dragging.
| const { state, resolutionChanged, chrChanged, dragging } = eventData; | |
| const { state, resolutionChanged, chrChanged } = eventData; |
| this.updating = false; | ||
| if (this.pending.size > 0) { | ||
| const queued = []; | ||
| for (let [k, v] of this.pending) { |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable k.
| for (let [k, v] of this.pending) { | |
| for (let v of this.pending.values()) { |
|
|
||
| import { describe, test, expect } from 'vitest'; | ||
| import { extractConfig } from "../js/urlUtils.js"; | ||
| import { restoreSession } from "../js/session.js"; |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import restoreSession.
No description provided.