mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 18:10:18 +05:30
feat: add command palette (Ctrl+Shift+P)
Refactor inline command palette into a proper CommandPalette class with search highlighting, keyboard navigation, and overlay UI. Register all app actions including formatting, file ops, and sidebar toggles.
This commit is contained in:
@@ -0,0 +1,109 @@
|
|||||||
|
class CommandPalette {
|
||||||
|
constructor() {
|
||||||
|
this.overlay = document.getElementById('command-palette-overlay');
|
||||||
|
this.input = document.getElementById('command-palette-input');
|
||||||
|
this.results = document.getElementById('command-palette-results');
|
||||||
|
this.commands = [];
|
||||||
|
this.selectedIndex = 0;
|
||||||
|
this.filteredCommands = [];
|
||||||
|
this.setupEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
register(label, shortcut, action) {
|
||||||
|
this.commands.push({ label, shortcut, action });
|
||||||
|
}
|
||||||
|
|
||||||
|
open() {
|
||||||
|
this.overlay.classList.remove('hidden');
|
||||||
|
this.input.value = '';
|
||||||
|
this.input.focus();
|
||||||
|
this.selectedIndex = 0;
|
||||||
|
this.renderResults('');
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.overlay.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
isOpen() {
|
||||||
|
return !this.overlay.classList.contains('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEventListeners() {
|
||||||
|
this.input.addEventListener('input', () => {
|
||||||
|
this.selectedIndex = 0;
|
||||||
|
this.renderResults(this.input.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.overlay.addEventListener('click', (e) => {
|
||||||
|
if (e.target === this.overlay) this.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.input.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.close();
|
||||||
|
} else if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.executeSelected();
|
||||||
|
} else if (e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.selectedIndex = Math.min(this.selectedIndex + 1, this.filteredCommands.length - 1);
|
||||||
|
this.updateSelection();
|
||||||
|
} else if (e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.selectedIndex = Math.max(this.selectedIndex - 1, 0);
|
||||||
|
this.updateSelection();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderResults(query) {
|
||||||
|
this.filteredCommands = query
|
||||||
|
? this.commands.filter(cmd => cmd.label.toLowerCase().includes(query.toLowerCase()))
|
||||||
|
: [...this.commands];
|
||||||
|
|
||||||
|
this.results.innerHTML = this.filteredCommands.map((cmd, i) => `
|
||||||
|
<div class="command-item ${i === this.selectedIndex ? 'selected' : ''}" data-index="${i}">
|
||||||
|
<span class="command-label">${this.highlightMatch(cmd.label, query)}</span>
|
||||||
|
${cmd.shortcut ? `<span class="command-shortcut">${cmd.shortcut}</span>` : ''}
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
this.results.querySelectorAll('.command-item').forEach((el) => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
const idx = parseInt(el.dataset.index);
|
||||||
|
this.filteredCommands[idx].action();
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
el.addEventListener('mouseenter', () => {
|
||||||
|
this.selectedIndex = parseInt(el.dataset.index);
|
||||||
|
this.updateSelection();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightMatch(text, query) {
|
||||||
|
if (!query) return text;
|
||||||
|
const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
||||||
|
return text.replace(regex, '<strong>$1</strong>');
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelection() {
|
||||||
|
this.results.querySelectorAll('.command-item').forEach((el, i) => {
|
||||||
|
el.classList.toggle('selected', i === this.selectedIndex);
|
||||||
|
});
|
||||||
|
// Scroll selected into view
|
||||||
|
const selected = this.results.querySelector('.command-item.selected');
|
||||||
|
if (selected) selected.scrollIntoView({ block: 'nearest' });
|
||||||
|
}
|
||||||
|
|
||||||
|
executeSelected() {
|
||||||
|
if (this.filteredCommands[this.selectedIndex]) {
|
||||||
|
this.filteredCommands[this.selectedIndex].action();
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { CommandPalette };
|
||||||
+8
-9
@@ -147,6 +147,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="breadcrumb-bar" id="breadcrumb-bar">
|
||||||
|
<span class="breadcrumb-path" id="breadcrumb-path">Untitled</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Find & Replace Dialog -->
|
<!-- Find & Replace Dialog -->
|
||||||
<div id="find-dialog" class="find-dialog hidden">
|
<div id="find-dialog" class="find-dialog hidden">
|
||||||
<div class="find-controls">
|
<div class="find-controls">
|
||||||
@@ -327,15 +331,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Command Palette -->
|
<!-- Command Palette -->
|
||||||
<div id="command-palette" class="command-palette hidden">
|
<div class="command-palette-overlay hidden" id="command-palette-overlay">
|
||||||
<div class="command-palette-content">
|
<div class="command-palette">
|
||||||
<input type="text" id="command-search" placeholder="Type a command..." autofocus>
|
<input type="text" class="command-palette-input" id="command-palette-input" placeholder="Type a command..." autocomplete="off">
|
||||||
<div id="command-list" class="command-list"></div>
|
<div class="command-palette-results" id="command-palette-results"></div>
|
||||||
<div class="command-palette-footer">
|
|
||||||
<span>↑↓ Navigate</span>
|
|
||||||
<span>Enter Execute</span>
|
|
||||||
<span>Esc Close</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+74
-151
@@ -11,6 +11,7 @@ const hljs = require('highlight.js');
|
|||||||
const { createEditor } = require('./editor/codemirror-setup');
|
const { createEditor } = require('./editor/codemirror-setup');
|
||||||
const { undo, redo } = require('@codemirror/commands');
|
const { undo, redo } = require('@codemirror/commands');
|
||||||
const { SidebarManager } = require('./sidebar/sidebar-manager');
|
const { SidebarManager } = require('./sidebar/sidebar-manager');
|
||||||
|
const { CommandPalette } = require('./command-palette');
|
||||||
|
|
||||||
// Configure marked with highlight extension
|
// Configure marked with highlight extension
|
||||||
marked.use(markedHighlight({
|
marked.use(markedHighlight({
|
||||||
@@ -187,6 +188,7 @@ class TabManager {
|
|||||||
this.restoreTabState(tabId);
|
this.restoreTabState(tabId);
|
||||||
this.focusActiveEditor();
|
this.focusActiveEditor();
|
||||||
this.updateFilePath();
|
this.updateFilePath();
|
||||||
|
this.updateBreadcrumb();
|
||||||
|
|
||||||
// Update cursor position for the newly active tab
|
// Update cursor position for the newly active tab
|
||||||
const tabForCursor = this.tabs.get(tabId);
|
const tabForCursor = this.tabs.get(tabId);
|
||||||
@@ -451,6 +453,15 @@ class TabManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateBreadcrumb() {
|
||||||
|
const tab = this.tabs.get(this.activeTabId);
|
||||||
|
const el = document.getElementById('breadcrumb-path');
|
||||||
|
if (el && tab) {
|
||||||
|
el.textContent = tab.filePath || 'Untitled';
|
||||||
|
el.title = tab.filePath || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setupEditorEvents() {
|
setupEditorEvents() {
|
||||||
// CodeMirror handles editor events via the onChange callback
|
// CodeMirror handles editor events via the onChange callback
|
||||||
// passed to createEditor(). No additional event delegation needed.
|
// passed to createEditor(). No additional event delegation needed.
|
||||||
@@ -941,6 +952,7 @@ class TabManager {
|
|||||||
this.addToRecentFiles(filePath);
|
this.addToRecentFiles(filePath);
|
||||||
this.updateTabBar();
|
this.updateTabBar();
|
||||||
this.updateFilePath();
|
this.updateFilePath();
|
||||||
|
this.updateBreadcrumb();
|
||||||
|
|
||||||
// Notify main process about current file for exports
|
// Notify main process about current file for exports
|
||||||
ipcRenderer.send('set-current-file', filePath);
|
ipcRenderer.send('set-current-file', filePath);
|
||||||
@@ -1036,6 +1048,66 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
render: (container) => { container.innerHTML = '<p style="color:#888;padding:8px;">Templates coming soon...</p>'; }
|
render: (container) => { container.innerHTML = '<p style="color:#888;padding:8px;">Templates coming soon...</p>'; }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialize command palette
|
||||||
|
const commandPalette = new CommandPalette();
|
||||||
|
|
||||||
|
// Register commands
|
||||||
|
commandPalette.register('New File', 'Ctrl+N', () => tabManager.createNewTab());
|
||||||
|
commandPalette.register('Open File', 'Ctrl+O', () => ipcRenderer.send('menu-open'));
|
||||||
|
commandPalette.register('Save', 'Ctrl+S', () => {
|
||||||
|
const content = tabManager.getCurrentContent();
|
||||||
|
const filePath = tabManager.getCurrentFilePath();
|
||||||
|
if (filePath) {
|
||||||
|
ipcRenderer.send('save-current-file', content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
commandPalette.register('Toggle Preview', '', () => {
|
||||||
|
tabManager.isPreviewVisible = !tabManager.isPreviewVisible;
|
||||||
|
tabManager.updatePreviewVisibility();
|
||||||
|
});
|
||||||
|
commandPalette.register('Toggle Line Numbers', '', () => {
|
||||||
|
tabManager.showLineNumbers = !tabManager.showLineNumbers;
|
||||||
|
tabManager.updateLineNumbers();
|
||||||
|
});
|
||||||
|
commandPalette.register('Bold', 'Ctrl+B', () => tabManager.wrapSelection('**', '**'));
|
||||||
|
commandPalette.register('Italic', 'Ctrl+I', () => tabManager.wrapSelection('*', '*'));
|
||||||
|
commandPalette.register('Insert Table', '', () => tabManager.insertTable());
|
||||||
|
commandPalette.register('Insert Code Block', '', () => tabManager.insertCodeBlock());
|
||||||
|
commandPalette.register('Insert Horizontal Rule', '', () => tabManager.insertHorizontalRule());
|
||||||
|
commandPalette.register('Find & Replace', 'Ctrl+F', () => {
|
||||||
|
const findDialog = document.getElementById('find-dialog');
|
||||||
|
if (findDialog) {
|
||||||
|
findDialog.classList.toggle('hidden');
|
||||||
|
if (!findDialog.classList.contains('hidden')) {
|
||||||
|
document.getElementById('find-input').focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
commandPalette.register('Toggle Sidebar: Explorer', 'Ctrl+Shift+E', () => sidebarManager.togglePanel('explorer'));
|
||||||
|
commandPalette.register('Toggle Sidebar: Git', 'Ctrl+Shift+G', () => sidebarManager.togglePanel('git'));
|
||||||
|
commandPalette.register('Toggle Sidebar: Snippets', '', () => sidebarManager.togglePanel('snippets'));
|
||||||
|
commandPalette.register('Toggle Sidebar: Templates', '', () => sidebarManager.togglePanel('templates'));
|
||||||
|
commandPalette.register('Export to PDF', '', () => ipcRenderer.send('export', 'pdf'));
|
||||||
|
commandPalette.register('Export to DOCX', '', () => ipcRenderer.send('export', 'docx'));
|
||||||
|
commandPalette.register('Export to HTML', '', () => ipcRenderer.send('export', 'html'));
|
||||||
|
commandPalette.register('New Tab', 'Ctrl+T', () => tabManager.createNewTab());
|
||||||
|
commandPalette.register('Close Tab', 'Ctrl+W', () => tabManager.closeTab(tabManager.activeTabId));
|
||||||
|
commandPalette.register('Undo', 'Ctrl+Z', () => { const tab = tabManager.tabs.get(tabManager.activeTabId); if (tab?.editorView) undo(tab.editorView); });
|
||||||
|
commandPalette.register('Redo', 'Ctrl+Shift+Z', () => { const tab = tabManager.tabs.get(tabManager.activeTabId); if (tab?.editorView) redo(tab.editorView); });
|
||||||
|
commandPalette.register('Heading 1', '', () => tabManager.insertAtLineStart('# '));
|
||||||
|
commandPalette.register('Heading 2', '', () => tabManager.insertAtLineStart('## '));
|
||||||
|
commandPalette.register('Heading 3', '', () => tabManager.insertAtLineStart('### '));
|
||||||
|
commandPalette.register('Insert Link', '', () => tabManager.wrapSelection('[', '](url)'));
|
||||||
|
commandPalette.register('Insert Image', '', () => tabManager.wrapSelection(''));
|
||||||
|
|
||||||
|
// Ctrl+Shift+P keyboard shortcut for command palette
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.ctrlKey && e.shiftKey && e.key === 'P') {
|
||||||
|
e.preventDefault();
|
||||||
|
commandPalette.open();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Initialize CodeMirror for the initial tab (tab 1)
|
// Initialize CodeMirror for the initial tab (tab 1)
|
||||||
const editorContainer = document.getElementById('editor-cm-1');
|
const editorContainer = document.getElementById('editor-cm-1');
|
||||||
if (editorContainer) {
|
if (editorContainer) {
|
||||||
@@ -3046,157 +3118,8 @@ window.openHeaderFooterDialog = openHeaderFooterDialog;
|
|||||||
ipcRenderer.on('open-header-footer-dialog', () => {
|
ipcRenderer.on('open-header-footer-dialog', () => {
|
||||||
openHeaderFooterDialog();
|
openHeaderFooterDialog();
|
||||||
});
|
});
|
||||||
// Command Palette Implementation
|
// Command Palette - initialized via CommandPalette class
|
||||||
const commands = [
|
// (see DOMContentLoaded handler for registration of commands)
|
||||||
{ name: 'New File', action: () => tabManager.createTab(), shortcut: 'Ctrl+N' },
|
|
||||||
{ name: 'Open File', action: () => ipcRenderer.send('open-file'), shortcut: 'Ctrl+O' },
|
|
||||||
{ name: 'Save File', action: () => ipcRenderer.send('save-file'), shortcut: 'Ctrl+S' },
|
|
||||||
{ name: 'Save As', action: () => ipcRenderer.send('save-file-as'), shortcut: 'Ctrl+Shift+S' },
|
|
||||||
{ name: 'Export to PDF', action: () => ipcRenderer.send('export', 'pdf'), shortcut: '' },
|
|
||||||
{ name: 'Export to DOCX', action: () => ipcRenderer.send('export', 'docx'), shortcut: '' },
|
|
||||||
{ name: 'Export to HTML', action: () => ipcRenderer.send('export', 'html'), shortcut: '' },
|
|
||||||
{ name: 'Toggle Preview', action: () => tabManager.togglePreview(), shortcut: 'Ctrl+Shift+P' },
|
|
||||||
{ name: 'Toggle Line Numbers', action: () => tabManager.toggleLineNumbers(), shortcut: '' },
|
|
||||||
{ name: 'Find & Replace', action: () => showFindDialog(), shortcut: 'Ctrl+F' },
|
|
||||||
{ name: 'New Tab', action: () => tabManager.createTab(), shortcut: 'Ctrl+T' },
|
|
||||||
{ name: 'Close Tab', action: () => tabManager.closeTab(tabManager.activeTabId), shortcut: 'Ctrl+W' },
|
|
||||||
{ name: 'Undo', action: () => { const tab = tabManager.tabs.get(tabManager.activeTabId); if (tab?.editorView) undo(tab.editorView); }, shortcut: 'Ctrl+Z' },
|
|
||||||
{ name: 'Redo', action: () => { const tab = tabManager.tabs.get(tabManager.activeTabId); if (tab?.editorView) redo(tab.editorView); }, shortcut: 'Ctrl+Shift+Z' },
|
|
||||||
{ name: 'Bold', action: () => insertMarkdown('**', '**'), shortcut: 'Ctrl+B' },
|
|
||||||
{ name: 'Italic', action: () => insertMarkdown('*', '*'), shortcut: 'Ctrl+I' },
|
|
||||||
{ name: 'Heading 1', action: () => insertMarkdown('# ', ''), shortcut: '' },
|
|
||||||
{ name: 'Heading 2', action: () => insertMarkdown('## ', ''), shortcut: '' },
|
|
||||||
{ name: 'Heading 3', action: () => insertMarkdown('### ', ''), shortcut: '' },
|
|
||||||
{ name: 'Insert Link', action: () => insertMarkdown('[', '](url)'), shortcut: '' },
|
|
||||||
{ name: 'Insert Image', action: () => insertMarkdown(''), shortcut: '' },
|
|
||||||
{ name: 'Insert Code Block', action: () => insertMarkdown('```\n', '\n```'), shortcut: '' },
|
|
||||||
{ name: 'Insert Table', action: () => insertMarkdown('\n| Header 1 | Header 2 |\n|----------|----------|\n| Cell 1 | Cell 2 |\n', ''), shortcut: '' },
|
|
||||||
{ name: 'Table Generator', action: () => showTableGenerator(), shortcut: '' },
|
|
||||||
{ name: 'ASCII Art Generator', action: () => showASCIIGenerator(), shortcut: '' },
|
|
||||||
{ name: 'Increase Font Size', action: () => ipcRenderer.send('adjust-font-size', 'increase'), shortcut: 'Ctrl+Shift++' },
|
|
||||||
{ name: 'Decrease Font Size', action: () => ipcRenderer.send('adjust-font-size', 'decrease'), shortcut: 'Ctrl+Shift+-' }
|
|
||||||
];
|
|
||||||
|
|
||||||
let commandPaletteSelectedIndex = 0;
|
|
||||||
let filteredCommands = [...commands];
|
|
||||||
|
|
||||||
function showCommandPalette() {
|
|
||||||
const palette = document.getElementById('command-palette');
|
|
||||||
const searchInput = document.getElementById('command-search');
|
|
||||||
|
|
||||||
palette.classList.remove('hidden');
|
|
||||||
searchInput.value = '';
|
|
||||||
searchInput.focus();
|
|
||||||
|
|
||||||
filteredCommands = [...commands];
|
|
||||||
commandPaletteSelectedIndex = 0;
|
|
||||||
renderCommandList();
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideCommandPalette() {
|
|
||||||
const palette = document.getElementById('command-palette');
|
|
||||||
palette.classList.add('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderCommandList() {
|
|
||||||
const list = document.getElementById('command-list');
|
|
||||||
list.innerHTML = '';
|
|
||||||
|
|
||||||
filteredCommands.forEach((cmd, index) => {
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'command-item';
|
|
||||||
if (index === commandPaletteSelectedIndex) {
|
|
||||||
item.classList.add('selected');
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = document.createElement('span');
|
|
||||||
name.className = 'command-name';
|
|
||||||
name.textContent = cmd.name;
|
|
||||||
|
|
||||||
const shortcut = document.createElement('span');
|
|
||||||
shortcut.className = 'command-shortcut';
|
|
||||||
shortcut.textContent = cmd.shortcut || '';
|
|
||||||
|
|
||||||
item.appendChild(name);
|
|
||||||
if (cmd.shortcut) {
|
|
||||||
item.appendChild(shortcut);
|
|
||||||
}
|
|
||||||
|
|
||||||
item.addEventListener('click', () => {
|
|
||||||
executeCommand(cmd);
|
|
||||||
});
|
|
||||||
|
|
||||||
list.appendChild(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterCommands(query) {
|
|
||||||
const lowerQuery = query.toLowerCase();
|
|
||||||
filteredCommands = commands.filter(cmd =>
|
|
||||||
cmd.name.toLowerCase().includes(lowerQuery)
|
|
||||||
);
|
|
||||||
commandPaletteSelectedIndex = 0;
|
|
||||||
renderCommandList();
|
|
||||||
}
|
|
||||||
|
|
||||||
function executeCommand(cmd) {
|
|
||||||
hideCommandPalette();
|
|
||||||
if (cmd && cmd.action) {
|
|
||||||
cmd.action();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function navigateCommandPalette(direction) {
|
|
||||||
commandPaletteSelectedIndex += direction;
|
|
||||||
if (commandPaletteSelectedIndex < 0) {
|
|
||||||
commandPaletteSelectedIndex = filteredCommands.length - 1;
|
|
||||||
}
|
|
||||||
if (commandPaletteSelectedIndex >= filteredCommands.length) {
|
|
||||||
commandPaletteSelectedIndex = 0;
|
|
||||||
}
|
|
||||||
renderCommandList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command Palette Event Listeners
|
|
||||||
document.addEventListener('keydown', (e) => {
|
|
||||||
// Ctrl+Shift+P to open command palette
|
|
||||||
if (e.ctrlKey && e.shiftKey && e.key === 'P') {
|
|
||||||
e.preventDefault();
|
|
||||||
showCommandPalette();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle command palette navigation when open
|
|
||||||
const palette = document.getElementById('command-palette');
|
|
||||||
if (!palette.classList.contains('hidden')) {
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
e.preventDefault();
|
|
||||||
hideCommandPalette();
|
|
||||||
} else if (e.key === 'ArrowDown') {
|
|
||||||
e.preventDefault();
|
|
||||||
navigateCommandPalette(1);
|
|
||||||
} else if (e.key === 'ArrowUp') {
|
|
||||||
e.preventDefault();
|
|
||||||
navigateCommandPalette(-1);
|
|
||||||
} else if (e.key === 'Enter') {
|
|
||||||
e.preventDefault();
|
|
||||||
if (filteredCommands[commandPaletteSelectedIndex]) {
|
|
||||||
executeCommand(filteredCommands[commandPaletteSelectedIndex]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('command-search').addEventListener('input', (e) => {
|
|
||||||
filterCommands(e.target.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close on backdrop click
|
|
||||||
document.getElementById('command-palette').addEventListener('click', (e) => {
|
|
||||||
if (e.target === document.getElementById('command-palette')) {
|
|
||||||
hideCommandPalette();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// TABLE GENERATOR
|
// TABLE GENERATOR
|
||||||
|
|||||||
@@ -2127,3 +2127,101 @@ body.theme-rosepine-dawn .batch-dialog-footer {
|
|||||||
background: #f2e9e1;
|
background: #f2e9e1;
|
||||||
border-top-color: #dfdad9;
|
border-top-color: #dfdad9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
Command Palette (Ctrl+Shift+P)
|
||||||
|
======================================== */
|
||||||
|
.command-palette-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 80px;
|
||||||
|
}
|
||||||
|
.command-palette-overlay.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.command-palette {
|
||||||
|
width: 560px;
|
||||||
|
max-height: 400px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.2);
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.command-palette-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px 18px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
font-size: 15px;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
.command-palette-results {
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 340px;
|
||||||
|
}
|
||||||
|
.command-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.command-item:hover,
|
||||||
|
.command-item.selected {
|
||||||
|
background: #f3f4f6;
|
||||||
|
}
|
||||||
|
.command-label strong {
|
||||||
|
color: #5661b3;
|
||||||
|
}
|
||||||
|
.command-shortcut {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #9ca3af;
|
||||||
|
background: #f3f4f6;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme command palette */
|
||||||
|
body[class*="dark"] .command-palette {
|
||||||
|
background: #1e1e1e;
|
||||||
|
border: 1px solid #333;
|
||||||
|
}
|
||||||
|
body[class*="dark"] .command-palette-input {
|
||||||
|
background: #1e1e1e;
|
||||||
|
color: #eee;
|
||||||
|
border-color: #333;
|
||||||
|
}
|
||||||
|
body[class*="dark"] .command-item:hover,
|
||||||
|
body[class*="dark"] .command-item.selected {
|
||||||
|
background: #2d2d2d;
|
||||||
|
}
|
||||||
|
body[class*="dark"] .command-shortcut {
|
||||||
|
background: #333;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
Breadcrumb Bar
|
||||||
|
======================================== */
|
||||||
|
.breadcrumb-bar {
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--gray-500, #6b7280);
|
||||||
|
border-bottom: 1px solid var(--gray-200, #e5e7eb);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|||||||
+1
-104
@@ -4010,107 +4010,4 @@ body.theme-github .mermaid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Command Palette Styles */
|
/* Command Palette Styles */
|
||||||
.command-palette {
|
/* Command Palette styles moved to styles-modern.css */
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.7);
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: center;
|
|
||||||
padding-top: 100px;
|
|
||||||
z-index: 10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-palette.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-palette-content {
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 12px;
|
|
||||||
width: 600px;
|
|
||||||
max-width: 90%;
|
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#command-search {
|
|
||||||
width: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
font-size: 18px;
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid #e0e0e0;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-list {
|
|
||||||
max-height: 400px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-item {
|
|
||||||
padding: 12px 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
transition: background 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-item:hover,
|
|
||||||
.command-item.selected {
|
|
||||||
background: #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-name {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-shortcut {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 3px 8px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-palette-footer {
|
|
||||||
padding: 12px 20px;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-top: 1px solid #e0e0e0;
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark theme support for command palette */
|
|
||||||
body.theme-dark .command-palette-content {
|
|
||||||
background: #2d2d2d;
|
|
||||||
color: #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.theme-dark #command-search {
|
|
||||||
background: #2d2d2d;
|
|
||||||
color: #f0f0f0;
|
|
||||||
border-color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.theme-dark .command-item:hover,
|
|
||||||
body.theme-dark .command-item.selected {
|
|
||||||
background: #3d3d3d;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.theme-dark .command-shortcut {
|
|
||||||
background: #3d3d3d;
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.theme-dark .command-palette-footer {
|
|
||||||
background: #252525;
|
|
||||||
border-color: #444;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user