mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 18:10:18 +05:30
feat: add sidebar panel system with icon strip and panel toggle
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
<link rel="stylesheet" href="styles-modern.css">
|
<link rel="stylesheet" href="styles-modern.css">
|
||||||
<link rel="stylesheet" href="styles-concreteinfo.css">
|
<link rel="stylesheet" href="styles-concreteinfo.css">
|
||||||
|
<link rel="stylesheet" href="styles-sidebar.css">
|
||||||
<!-- Load external resources asynchronously -->
|
<!-- Load external resources asynchronously -->
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" media="print" onload="this.media='all'">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" media="print" onload="this.media='all'">
|
||||||
<script defer src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
<script defer src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||||||
@@ -1293,6 +1294,47 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="main-content" id="main-content">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<div class="sidebar collapsed" id="sidebar">
|
||||||
|
<div class="sidebar-icons">
|
||||||
|
<button class="sidebar-icon" data-panel="explorer" title="File Explorer (Ctrl+Shift+E)">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="sidebar-icon" data-panel="git" title="Git (Ctrl+Shift+G)">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<line x1="6" y1="3" x2="6" y2="15"/>
|
||||||
|
<circle cx="18" cy="6" r="3"/>
|
||||||
|
<circle cx="6" cy="6" r="3"/>
|
||||||
|
<path d="M18 9a9 9 0 01-9 9"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="sidebar-icon" data-panel="snippets" title="Snippets (Ctrl+Shift+S)">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<polyline points="16 18 22 12 16 6"/>
|
||||||
|
<polyline points="8 6 2 12 8 18"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="sidebar-icon" data-panel="templates" title="Templates">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||||
|
<line x1="3" y1="9" x2="21" y2="9"/>
|
||||||
|
<line x1="9" y1="21" x2="9" y2="9"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-panel" id="sidebar-panel">
|
||||||
|
<div class="sidebar-panel-header">
|
||||||
|
<span class="sidebar-panel-title"></span>
|
||||||
|
<button class="sidebar-panel-close" title="Close panel">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-panel-content" id="sidebar-panel-content"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Editor Container -->
|
||||||
<div class="editor-container">
|
<div class="editor-container">
|
||||||
<div class="tab-content active" id="tab-content-1" data-tab-id="1">
|
<div class="tab-content active" id="tab-content-1" data-tab-id="1">
|
||||||
<div id="editor-pane-1" class="pane">
|
<div id="editor-pane-1" class="pane">
|
||||||
@@ -1404,6 +1446,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="status-bar">
|
<div class="status-bar">
|
||||||
<span id="status-text">Ready</span>
|
<span id="status-text">Ready</span>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const DOMPurify = require('dompurify');
|
|||||||
const hljs = require('highlight.js');
|
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');
|
||||||
|
|
||||||
// Configure marked with highlight extension
|
// Configure marked with highlight extension
|
||||||
marked.use(markedHighlight({
|
marked.use(markedHighlight({
|
||||||
@@ -1006,6 +1007,25 @@ let tabManager;
|
|||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
tabManager = new TabManager();
|
tabManager = new TabManager();
|
||||||
|
|
||||||
|
// Initialize sidebar
|
||||||
|
const sidebarManager = new SidebarManager();
|
||||||
|
sidebarManager.registerPanel('explorer', {
|
||||||
|
title: 'Explorer',
|
||||||
|
render: (container) => { container.innerHTML = '<p style="color:#888;padding:8px;">File explorer coming soon...</p>'; }
|
||||||
|
});
|
||||||
|
sidebarManager.registerPanel('git', {
|
||||||
|
title: 'Git',
|
||||||
|
render: (container) => { container.innerHTML = '<p style="color:#888;padding:8px;">Git panel coming soon...</p>'; }
|
||||||
|
});
|
||||||
|
sidebarManager.registerPanel('snippets', {
|
||||||
|
title: 'Snippets',
|
||||||
|
render: (container) => { container.innerHTML = '<p style="color:#888;padding:8px;">Snippets coming soon...</p>'; }
|
||||||
|
});
|
||||||
|
sidebarManager.registerPanel('templates', {
|
||||||
|
title: 'Templates',
|
||||||
|
render: (container) => { container.innerHTML = '<p style="color:#888;padding:8px;">Templates coming soon...</p>'; }
|
||||||
|
});
|
||||||
|
|
||||||
// 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) {
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
class SidebarManager {
|
||||||
|
constructor() {
|
||||||
|
this.sidebar = document.getElementById('sidebar');
|
||||||
|
this.panelContent = document.getElementById('sidebar-panel-content');
|
||||||
|
this.panelTitle = document.querySelector('.sidebar-panel-title');
|
||||||
|
this.activePanel = null;
|
||||||
|
this.panels = new Map();
|
||||||
|
this.setupEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEventListeners() {
|
||||||
|
document.querySelectorAll('.sidebar-icon').forEach(btn => {
|
||||||
|
btn.addEventListener('click', () => this.togglePanel(btn.dataset.panel));
|
||||||
|
});
|
||||||
|
document.querySelector('.sidebar-panel-close')?.addEventListener('click', () => this.collapse());
|
||||||
|
}
|
||||||
|
|
||||||
|
registerPanel(name, { title, render }) {
|
||||||
|
this.panels.set(name, { title, render });
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePanel(name) {
|
||||||
|
if (this.activePanel === name) {
|
||||||
|
this.collapse();
|
||||||
|
} else {
|
||||||
|
this.expand(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expand(name) {
|
||||||
|
const panel = this.panels.get(name);
|
||||||
|
if (!panel) return;
|
||||||
|
this.sidebar.classList.remove('collapsed');
|
||||||
|
this.panelTitle.textContent = panel.title;
|
||||||
|
this.panelContent.innerHTML = '';
|
||||||
|
panel.render(this.panelContent);
|
||||||
|
this.activePanel = name;
|
||||||
|
document.querySelectorAll('.sidebar-icon').forEach(btn => {
|
||||||
|
btn.classList.toggle('active', btn.dataset.panel === name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
collapse() {
|
||||||
|
this.sidebar.classList.add('collapsed');
|
||||||
|
this.activePanel = null;
|
||||||
|
document.querySelectorAll('.sidebar-icon').forEach(btn => btn.classList.remove('active'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { SidebarManager };
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
/* Sidebar */
|
||||||
|
.main-content {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 100%;
|
||||||
|
transition: width 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed {
|
||||||
|
width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar:not(.collapsed) {
|
||||||
|
width: 328px; /* 48px icons + 280px panel */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-icons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 48px;
|
||||||
|
background: var(--gray-100, #f3f4f6);
|
||||||
|
border-right: 1px solid var(--gray-200, #e5e7eb);
|
||||||
|
padding: 8px 0;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--gray-500, #6b7280);
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-icon:hover {
|
||||||
|
background: var(--gray-200, #e5e7eb);
|
||||||
|
color: var(--gray-700, #374151);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-icon.active {
|
||||||
|
background: var(--gray-200, #e5e7eb);
|
||||||
|
color: var(--primary-dark, #5661b3);
|
||||||
|
box-shadow: inset 3px 0 0 var(--primary-dark, #5661b3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-panel {
|
||||||
|
width: 280px;
|
||||||
|
background: var(--gray-50, #f9fafb);
|
||||||
|
border-right: 1px solid var(--gray-200, #e5e7eb);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-panel {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-panel-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid var(--gray-200, #e5e7eb);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 13px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: var(--gray-600, #4b5563);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-panel-close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--gray-400, #9ca3af);
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-panel-close:hover {
|
||||||
|
background: var(--gray-200, #e5e7eb);
|
||||||
|
color: var(--gray-600, #4b5563);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-panel-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme support */
|
||||||
|
body[class*="dark"] .sidebar-icons,
|
||||||
|
body[class*="dark"] .sidebar-panel {
|
||||||
|
background: #1e1e1e;
|
||||||
|
border-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[class*="dark"] .sidebar-icon {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[class*="dark"] .sidebar-icon:hover {
|
||||||
|
background: #333;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[class*="dark"] .sidebar-icon.active {
|
||||||
|
background: #333;
|
||||||
|
color: #8b9aff;
|
||||||
|
box-shadow: inset 3px 0 0 #8b9aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[class*="dark"] .sidebar-panel-header {
|
||||||
|
border-color: #333;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user