diff --git a/static/js/checkpoints.js b/static/js/checkpoints.js index 907e92a6..a26099d3 100644 --- a/static/js/checkpoints.js +++ b/static/js/checkpoints.js @@ -1,7 +1,6 @@ import { appCore } from './core.js'; import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js'; import { createPageControls } from './components/controls/index.js'; -import { CheckpointContextMenu } from './components/ContextMenu/index.js'; import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js'; import { MODEL_TYPES } from './api/apiConfig.js'; @@ -30,10 +29,7 @@ class CheckpointsPageManager { } async initialize() { - // Initialize context menu - new CheckpointContextMenu(); - - // Initialize common page features + // Initialize common page features (including context menus) appCore.initializePageFeatures(); console.log('Checkpoints Manager initialized'); @@ -48,4 +44,4 @@ document.addEventListener('DOMContentLoaded', async () => { // Initialize checkpoints page const checkpointsPage = new CheckpointsPageManager(); await checkpointsPage.initialize(); -}); +}); \ No newline at end of file diff --git a/static/js/components/ContextMenu/BaseContextMenu.js b/static/js/components/ContextMenu/BaseContextMenu.js index e2f9edfc..8ec2d9eb 100644 --- a/static/js/components/ContextMenu/BaseContextMenu.js +++ b/static/js/components/ContextMenu/BaseContextMenu.js @@ -15,17 +15,6 @@ export class BaseContextMenu { init() { // Hide menu on regular clicks document.addEventListener('click', () => this.hideMenu()); - - // Show menu on right-click on cards - document.addEventListener('contextmenu', (e) => { - const card = e.target.closest(this.cardSelector); - if (!card) { - this.hideMenu(); - return; - } - e.preventDefault(); - this.showMenu(e.clientX, e.clientY, card); - }); // Handle menu item clicks this.menu.addEventListener('click', (e) => { diff --git a/static/js/components/ContextMenu/BulkContextMenu.js b/static/js/components/ContextMenu/BulkContextMenu.js index ef206b77..7e228641 100644 --- a/static/js/components/ContextMenu/BulkContextMenu.js +++ b/static/js/components/ContextMenu/BulkContextMenu.js @@ -1,8 +1,7 @@ import { BaseContextMenu } from './BaseContextMenu.js'; import { state } from '../../state/index.js'; import { bulkManager } from '../../managers/BulkManager.js'; -import { translate, updateElementText } from '../../utils/i18nHelpers.js'; -import { MODEL_TYPES } from '../../api/apiConfig.js'; +import { updateElementText } from '../../utils/i18nHelpers.js'; export class BulkContextMenu extends BaseContextMenu { constructor() { @@ -10,39 +9,6 @@ export class BulkContextMenu extends BaseContextMenu { this.setupBulkMenuItems(); } - init() { - // Override parent init to handle bulk-specific context menu logic - document.addEventListener('click', () => this.hideMenu()); - - document.addEventListener('contextmenu', (e) => { - const card = e.target.closest('.model-card'); - if (!card || !state.bulkMode) { - this.hideMenu(); - return; - } - - // Show bulk menu only if right-clicking on a selected card - if (card.classList.contains('selected')) { - e.preventDefault(); - this.showMenu(e.clientX, e.clientY, card); - } else { - this.hideMenu(); - } - }); - - // Handle menu item clicks - this.menu.addEventListener('click', (e) => { - const menuItem = e.target.closest('.context-menu-item'); - if (!menuItem || !this.currentCard) return; - - const action = menuItem.dataset.action; - if (!action) return; - - this.handleMenuAction(action, menuItem); - this.hideMenu(); - }); - } - setupBulkMenuItems() { if (!this.menu) return; diff --git a/static/js/components/ContextMenu/index.js b/static/js/components/ContextMenu/index.js index af539a53..306777ae 100644 --- a/static/js/components/ContextMenu/index.js +++ b/static/js/components/ContextMenu/index.js @@ -2,4 +2,56 @@ export { LoraContextMenu } from './LoraContextMenu.js'; export { RecipeContextMenu } from './RecipeContextMenu.js'; export { CheckpointContextMenu } from './CheckpointContextMenu.js'; export { EmbeddingContextMenu } from './EmbeddingContextMenu.js'; -export { ModelContextMenuMixin } from './ModelContextMenuMixin.js'; \ No newline at end of file +export { ModelContextMenuMixin } from './ModelContextMenuMixin.js'; + +import { LoraContextMenu } from './LoraContextMenu.js'; +import { RecipeContextMenu } from './RecipeContextMenu.js'; +import { CheckpointContextMenu } from './CheckpointContextMenu.js'; +import { EmbeddingContextMenu } from './EmbeddingContextMenu.js'; +import { state } from '../../state/index.js'; + +// Factory method to create page-specific context menu instances +export function createPageContextMenu(pageType) { + switch (pageType) { + case 'loras': + return new LoraContextMenu(); + case 'recipes': + return new RecipeContextMenu(); + case 'checkpoints': + return new CheckpointContextMenu(); + case 'embeddings': + return new EmbeddingContextMenu(); + default: + return null; + } +} + +// Initialize context menu coordination for pages that support it +export function initializeContextMenuCoordination(pageContextMenu, bulkContextMenu) { + // Centralized context menu event handler + document.addEventListener('contextmenu', (e) => { + const card = e.target.closest('.model-card'); + if (!card) { + // Hide all menus if not right-clicking on a card + pageContextMenu?.hideMenu(); + bulkContextMenu?.hideMenu(); + return; + } + + e.preventDefault(); + + // Hide all menus first + pageContextMenu?.hideMenu(); + bulkContextMenu?.hideMenu(); + + // Determine which menu to show based on bulk mode and selection state + if (state.bulkMode && card.classList.contains('selected')) { + // Show bulk menu for selected cards in bulk mode + bulkContextMenu?.showMenu(e.clientX, e.clientY, card); + } else if (!state.bulkMode) { + // Show regular menu when not in bulk mode + pageContextMenu?.showMenu(e.clientX, e.clientY, card); + } + // Don't show any menu for unselected cards in bulk mode + }); +} \ No newline at end of file diff --git a/static/js/core.js b/static/js/core.js index 21839d97..eb7d8dba 100644 --- a/static/js/core.js +++ b/static/js/core.js @@ -16,11 +16,14 @@ import { migrateStorageItems } from './utils/storageHelpers.js'; import { i18n } from './i18n/index.js'; import { onboardingManager } from './managers/OnboardingManager.js'; import { BulkContextMenu } from './components/ContextMenu/BulkContextMenu.js'; +import { createPageContextMenu, initializeContextMenuCoordination } from './components/ContextMenu/index.js'; // Core application class export class AppCore { constructor() { this.initialized = false; + this.pageContextMenu = null; + this.bulkContextMenu = null; } // Initialize core functionality @@ -93,13 +96,27 @@ export class AppCore { initializePageFeatures() { const pageType = this.getPageType(); - // Initialize virtual scroll for pages that need it if (['loras', 'recipes', 'checkpoints', 'embeddings'].includes(pageType)) { + this.initializeContextMenus(pageType); initializeInfiniteScroll(pageType); } return this; } + + // Initialize context menus for the current page + initializeContextMenus(pageType) { + // Create page-specific context menu + this.pageContextMenu = createPageContextMenu(pageType); + + // Get bulk context menu from bulkManager + this.bulkContextMenu = bulkManager.bulkContextMenu; + + // Initialize context menu coordination + if (this.pageContextMenu || this.bulkContextMenu) { + initializeContextMenuCoordination(this.pageContextMenu, this.bulkContextMenu); + } + } } document.addEventListener('DOMContentLoaded', () => { diff --git a/static/js/embeddings.js b/static/js/embeddings.js index c2276ce8..1352471c 100644 --- a/static/js/embeddings.js +++ b/static/js/embeddings.js @@ -1,7 +1,6 @@ import { appCore } from './core.js'; import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js'; import { createPageControls } from './components/controls/index.js'; -import { EmbeddingContextMenu } from './components/ContextMenu/index.js'; import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js'; import { MODEL_TYPES } from './api/apiConfig.js'; @@ -30,10 +29,7 @@ class EmbeddingsPageManager { } async initialize() { - // Initialize context menu - new EmbeddingContextMenu(); - - // Initialize common page features + // Initialize common page features (including context menus) appCore.initializePageFeatures(); console.log('Embeddings Manager initialized'); diff --git a/static/js/loras.js b/static/js/loras.js index d8820cac..6566235f 100644 --- a/static/js/loras.js +++ b/static/js/loras.js @@ -1,7 +1,6 @@ import { appCore } from './core.js'; import { state } from './state/index.js'; import { updateCardsForBulkMode } from './components/shared/ModelCard.js'; -import { LoraContextMenu } from './components/ContextMenu/index.js'; import { createPageControls } from './components/controls/index.js'; import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } from './utils/modalUtils.js'; import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js'; @@ -37,13 +36,10 @@ class LoraPageManager { } async initialize() { - // Initialize page-specific components - new LoraContextMenu(); - // Initialize cards for current bulk mode state (should be false initially) updateCardsForBulkMode(state.bulkMode); - // Initialize common page features (virtual scroll) + // Initialize common page features (including context menus and virtual scroll) appCore.initializePageFeatures(); } } diff --git a/static/js/state/index.js b/static/js/state/index.js index f905eb09..65d5619f 100644 --- a/static/js/state/index.js +++ b/static/js/state/index.js @@ -71,6 +71,8 @@ export const state = { pageSize: 20, showFavoritesOnly: false, duplicatesMode: false, + bulkMode: false, + selectedModels: new Set(), }, [MODEL_TYPES.CHECKPOINT]: {