feat: add global context menu with actions and integration

This commit is contained in:
Will Miao
2025-09-23 11:19:36 +08:00
parent b91f06405d
commit d477050239
5 changed files with 78 additions and 21 deletions

View File

@@ -0,0 +1,24 @@
import { BaseContextMenu } from './BaseContextMenu.js';
export class GlobalContextMenu extends BaseContextMenu {
constructor() {
super('globalContextMenu');
}
showMenu(x, y) {
super.showMenu(x, y, null);
}
handleMenuAction(action, menuItem) {
switch (action) {
case 'placeholder-one':
case 'placeholder-two':
case 'placeholder-three':
console.info(`Global context menu action triggered: ${action}`);
break;
default:
console.warn(`Unhandled global context menu action: ${action}`);
break;
}
}
}

View File

@@ -2,12 +2,14 @@ export { LoraContextMenu } from './LoraContextMenu.js';
export { RecipeContextMenu } from './RecipeContextMenu.js'; export { RecipeContextMenu } from './RecipeContextMenu.js';
export { CheckpointContextMenu } from './CheckpointContextMenu.js'; export { CheckpointContextMenu } from './CheckpointContextMenu.js';
export { EmbeddingContextMenu } from './EmbeddingContextMenu.js'; export { EmbeddingContextMenu } from './EmbeddingContextMenu.js';
export { GlobalContextMenu } from './GlobalContextMenu.js';
export { ModelContextMenuMixin } from './ModelContextMenuMixin.js'; export { ModelContextMenuMixin } from './ModelContextMenuMixin.js';
import { LoraContextMenu } from './LoraContextMenu.js'; import { LoraContextMenu } from './LoraContextMenu.js';
import { RecipeContextMenu } from './RecipeContextMenu.js'; import { RecipeContextMenu } from './RecipeContextMenu.js';
import { CheckpointContextMenu } from './CheckpointContextMenu.js'; import { CheckpointContextMenu } from './CheckpointContextMenu.js';
import { EmbeddingContextMenu } from './EmbeddingContextMenu.js'; import { EmbeddingContextMenu } from './EmbeddingContextMenu.js';
import { GlobalContextMenu } from './GlobalContextMenu.js';
// Factory method to create page-specific context menu instances // Factory method to create page-specific context menu instances
export function createPageContextMenu(pageType) { export function createPageContextMenu(pageType) {
@@ -24,3 +26,7 @@ export function createPageContextMenu(pageType) {
return null; return null;
} }
} }
export function createGlobalContextMenu() {
return new GlobalContextMenu();
}

View File

@@ -16,7 +16,7 @@ import { migrateStorageItems } from './utils/storageHelpers.js';
import { i18n } from './i18n/index.js'; import { i18n } from './i18n/index.js';
import { onboardingManager } from './managers/OnboardingManager.js'; import { onboardingManager } from './managers/OnboardingManager.js';
import { BulkContextMenu } from './components/ContextMenu/BulkContextMenu.js'; import { BulkContextMenu } from './components/ContextMenu/BulkContextMenu.js';
import { createPageContextMenu } from './components/ContextMenu/index.js'; import { createPageContextMenu, createGlobalContextMenu } from './components/ContextMenu/index.js';
import { initializeEventManagement } from './utils/eventManagementInit.js'; import { initializeEventManagement } from './utils/eventManagementInit.js';
// Core application class // Core application class
@@ -116,6 +116,10 @@ export class AppCore {
initializeContextMenus(pageType) { initializeContextMenus(pageType) {
// Create page-specific context menu // Create page-specific context menu
window.pageContextMenu = createPageContextMenu(pageType); window.pageContextMenu = createPageContextMenu(pageType);
if (!window.globalContextMenuInstance) {
window.globalContextMenuInstance = createGlobalContextMenu();
}
} }
} }

View File

@@ -86,18 +86,20 @@ function setupPageUnloadCleanup() {
function registerContextMenuEvents() { function registerContextMenuEvents() {
eventManager.addHandler('contextmenu', 'contextMenu-coordination', (e) => { eventManager.addHandler('contextmenu', 'contextMenu-coordination', (e) => {
const card = e.target.closest('.model-card'); const card = e.target.closest('.model-card');
if (!card) { const pageContent = e.target.closest('.page-content');
// Hide all menus if not right-clicking on a card
window.pageContextMenu?.hideMenu(); if (!pageContent) {
window.bulkManager?.bulkContextMenu?.hideMenu(); window.globalContextMenuInstance?.hideMenu();
return false; return false;
} }
if (card) {
e.preventDefault(); e.preventDefault();
// Hide all menus first // Hide all menus first
window.pageContextMenu?.hideMenu(); window.pageContextMenu?.hideMenu();
window.bulkManager?.bulkContextMenu?.hideMenu(); window.bulkManager?.bulkContextMenu?.hideMenu();
window.globalContextMenuInstance?.hideMenu();
// Determine which menu to show based on bulk mode and selection state // Determine which menu to show based on bulk mode and selection state
if (state.bulkMode && card.classList.contains('selected')) { if (state.bulkMode && card.classList.contains('selected')) {
@@ -107,7 +109,15 @@ function registerContextMenuEvents() {
// Show regular menu when not in bulk mode // Show regular menu when not in bulk mode
window.pageContextMenu?.showMenu(e.clientX, e.clientY, card); window.pageContextMenu?.showMenu(e.clientX, e.clientY, card);
} }
// Don't show any menu for unselected cards in bulk mode } else {
e.preventDefault();
window.pageContextMenu?.hideMenu();
window.bulkManager?.bulkContextMenu?.hideMenu();
window.globalContextMenuInstance?.hideMenu();
window.globalContextMenuInstance?.showMenu(e.clientX, e.clientY, null);
}
return true; // Stop propagation return true; // Stop propagation
}, { }, {
@@ -125,6 +135,7 @@ function registerGlobalClickHandlers() {
if (!e.target.closest('.context-menu')) { if (!e.target.closest('.context-menu')) {
window.pageContextMenu?.hideMenu(); window.pageContextMenu?.hideMenu();
window.bulkManager?.bulkContextMenu?.hideMenu(); window.bulkManager?.bulkContextMenu?.hideMenu();
window.globalContextMenuInstance?.hideMenu();
} }
return false; // Allow other handlers to process return false; // Allow other handlers to process
}, { }, {

View File

@@ -84,6 +84,18 @@
</div> </div>
</div> </div>
<div id="globalContextMenu" class="context-menu">
<div class="context-menu-item" data-action="placeholder-one">
<i class="fas fa-bolt"></i> <span>Global action one</span>
</div>
<div class="context-menu-item" data-action="placeholder-two">
<i class="fas fa-layer-group"></i> <span>Global action two</span>
</div>
<div class="context-menu-item" data-action="placeholder-three">
<i class="fas fa-cog"></i> <span>Global action three</span>
</div>
</div>
<div id="nsfwLevelSelector" class="nsfw-level-selector"> <div id="nsfwLevelSelector" class="nsfw-level-selector">
<div class="nsfw-level-header"> <div class="nsfw-level-header">
<h3>{{ t('modals.contentRating.title') }}</h3> <h3>{{ t('modals.contentRating.title') }}</h3>