feat: add help modal with keyboard shortcuts documentation

This commit is contained in:
2025-10-16 02:23:58 +03:00
parent a3af753f42
commit f0a2eb664e
5 changed files with 179 additions and 13 deletions

View File

@@ -38,3 +38,4 @@ Instructions for Claude Code when working on this project.
- On-demand URL preview loading with caching
See `docs/dev-plan.md` for complete roadmap and technical details.
- when updating documentation, do not record intermediate changes - write them always as a matter-of-fact information

View File

@@ -303,18 +303,6 @@ Astrolabe is a focused tool for managing, editing, and previewing Vega-Lite visu
### **Phase 12: Advanced Dataset Features** ✅ **COMPLETE**
**Goal**: Enhanced dataset workflows
- [x] Detect inline data in Vega-Lite specs
- [x] "Extract to dataset" feature for inline data
- [x] Update snippet UI to show linked datasets
- [x] Dataset usage tracking (which snippets reference which datasets)
- [x] Bidirectional linking between snippets and datasets
- [x] Usage count badges on dataset list items
- [x] "New Snippet" button to create snippet from dataset
- [x] Import datasets from file upload
- [x] Export individual datasets
- [x] Dataset preview with table view
- [x] Column type detection and display
**Deliverables**:
- Recursive dataset reference extraction from Vega-Lite specs
- Extract to Dataset modal with automatic spec transformation

View File

@@ -321,6 +321,42 @@
</div>
</div>
<!-- Help Modal -->
<div id="help-modal" class="modal" style="display: none;">
<div class="modal-content" style="max-width: 600px; height: auto;">
<div class="modal-header">
<span class="modal-title">Help</span>
<button class="btn btn-icon" id="help-modal-close">×</button>
</div>
<div class="modal-body">
<div style="padding: 20px;">
<h3 style="margin-top: 0; margin-bottom: 16px; font-size: 16px; font-weight: bold;">Keyboard Shortcuts</h3>
<table class="help-shortcuts-table">
<tbody>
<tr>
<td class="shortcut-key">Cmd/Ctrl+Shift+N</td>
<td class="shortcut-desc">Create new snippet</td>
</tr>
<tr>
<td class="shortcut-key">Cmd/Ctrl+K</td>
<td class="shortcut-desc">Toggle dataset manager</td>
</tr>
<tr>
<td class="shortcut-key">Cmd/Ctrl+S</td>
<td class="shortcut-desc">Publish draft (save)</td>
</tr>
<tr>
<td class="shortcut-key">Escape</td>
<td class="shortcut-desc">Close any open modal</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="src/js/config.js"></script>
<script src="src/js/snippet-manager.js"></script>
<script src="src/js/dataset-manager.js"></script>

View File

@@ -29,6 +29,9 @@ document.addEventListener('DOMContentLoaded', function () {
// Initialize resize functionality
initializeResize();
// Initialize keyboard shortcuts
initializeKeyboardShortcuts();
// Initialize Monaco Editor
require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.47.0/min/vs' } });
require(['vs/editor/editor.main'], async function () {
@@ -70,6 +73,9 @@ document.addEventListener('DOMContentLoaded', function () {
formatOnType: true
});
// Register custom keyboard shortcuts in Monaco
registerMonacoKeyboardShortcuts();
// Add debounced auto-render on editor change
editor.onDidChangeModelContent(() => {
debouncedRender();
@@ -118,7 +124,7 @@ document.addEventListener('DOMContentLoaded', function () {
if (helpLink) {
helpLink.addEventListener('click', function () {
alert('Coming soon in a future phase!');
openHelpModal();
});
}
@@ -229,6 +235,23 @@ document.addEventListener('DOMContentLoaded', function () {
});
}
// Help Modal
const helpModal = document.getElementById('help-modal');
const helpModalClose = document.getElementById('help-modal-close');
if (helpModalClose) {
helpModalClose.addEventListener('click', closeHelpModal);
}
// Close on overlay click
if (helpModal) {
helpModal.addEventListener('click', function (e) {
if (e.target === helpModal) {
closeHelpModal();
}
});
}
// View mode toggle buttons
document.getElementById('view-draft').addEventListener('click', () => {
switchViewMode('draft');
@@ -315,3 +338,113 @@ function initializeURLStateManagement() {
handleURLStateChange();
}
}
// Keyboard shortcut action handlers (shared between Monaco and document)
const KeyboardActions = {
createNewSnippet: function() {
createNewSnippet();
},
toggleDatasetManager: function() {
const modal = document.getElementById('dataset-modal');
if (modal && modal.style.display === 'flex') {
closeDatasetManager();
} else {
openDatasetManager();
}
},
publishDraft: function() {
if (currentViewMode === 'draft' && window.currentSnippetId) {
publishDraft();
}
},
closeAnyModal: function() {
const helpModal = document.getElementById('help-modal');
const datasetModal = document.getElementById('dataset-modal');
const extractModal = document.getElementById('extract-modal');
if (helpModal && helpModal.style.display === 'flex') {
closeHelpModal();
return true;
}
if (datasetModal && datasetModal.style.display === 'flex') {
closeDatasetManager();
return true;
}
if (extractModal && extractModal.style.display === 'flex') {
hideExtractModal();
return true;
}
return false;
}
};
// Keyboard shortcuts handler (document-level)
function initializeKeyboardShortcuts() {
document.addEventListener('keydown', function(e) {
// Escape: Close any open modal
if (e.key === 'Escape') {
if (KeyboardActions.closeAnyModal()) {
return;
}
}
// Detect modifier key: Cmd on Mac, Ctrl on Windows/Linux
const modifierKey = e.metaKey || e.ctrlKey;
// Cmd/Ctrl+Shift+N: Create new snippet
if (modifierKey && e.shiftKey && e.key.toLowerCase() === 'n') {
e.preventDefault();
KeyboardActions.createNewSnippet();
return;
}
// Cmd/Ctrl+K: Toggle dataset manager
if (modifierKey && e.key.toLowerCase() === 'k') {
e.preventDefault();
KeyboardActions.toggleDatasetManager();
return;
}
// Cmd/Ctrl+S: Publish draft
if (modifierKey && e.key.toLowerCase() === 's') {
e.preventDefault();
KeyboardActions.publishDraft();
return;
}
});
}
// Register keyboard shortcuts in Monaco Editor
function registerMonacoKeyboardShortcuts() {
if (!editor) return;
// Cmd/Ctrl+Shift+N: Create new snippet
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyN,
KeyboardActions.createNewSnippet);
// Cmd/Ctrl+K: Toggle dataset manager
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK,
KeyboardActions.toggleDatasetManager);
// Cmd/Ctrl+S: Publish draft
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS,
KeyboardActions.publishDraft);
}
// Help modal functions
function openHelpModal() {
const modal = document.getElementById('help-modal');
if (modal) {
modal.style.display = 'flex';
}
}
function closeHelpModal() {
const modal = document.getElementById('help-modal');
if (modal) {
modal.style.display = 'none';
}
}

View File

@@ -298,3 +298,11 @@ body { font-family: var(--font-main); height: 100vh; overflow: hidden; backgroun
.detection-preview-label { font-size: 10px; font-weight: bold; margin-bottom: 4px; }
.dataset-form-error { color: #f00; font-size: 11px; margin-bottom: 12px; min-height: 16px; }
.dataset-form-actions { display: flex; gap: 8px; margin-top: 16px; }
/* Help Modal */
.help-shortcuts-table { width: 100%; border-collapse: collapse; }
.help-shortcuts-table tbody tr { border-bottom: 1px solid var(--bg-lighter); }
.help-shortcuts-table tbody tr:last-child { border-bottom: none; }
.help-shortcuts-table td { padding: 12px 8px; font-size: 12px; }
.shortcut-key { font-family: var(--font-mono); font-weight: bold; background: var(--bg-light); border: 1px solid var(--win-gray-dark); padding: 6px 10px; border-radius: 2px; white-space: nowrap; width: 180px; }
.shortcut-desc { color: var(--win-gray-darker); }