feat: implement elegant slide-in floating control panel for edit mode
Replaced the intrusive blue status bar with a sleek slide-in control panel: UI/UX Improvements: - Minimized ribbon (📝 icon) on top-right that slides out to full panel - Beautiful gradient design with backdrop blur effects - Smooth CSS transitions with cubic-bezier easing - Auto-expand on load, then minimize after 2 seconds - Click outside to close, click ribbon to toggle Features Combined: - Status indicators with dynamic icons (⏳ loading, ✅ success, ❌ error) - Save & Download and Preview buttons in clean grid layout - Version information in panel header - Error reporting with expandable details - Responsive design for mobile devices Technical Changes: - Replaced old floating-header with integrated control panel - Enhanced status update function with visual state management - Added toggle functionality with click-outside-to-close - Improved typography and spacing throughout - Updated test to match new element ID structure This provides a much cleaner editing experience with better space utilization while maintaining all previous functionality and adding visual polish. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -396,35 +396,245 @@ class DocumentManager:
|
||||
body_classes = ' class="markitect-edit-mode"'
|
||||
editor_css = """
|
||||
<style>
|
||||
.markitect-floating-header {
|
||||
/* Floating Control Panel - Slide-in from right */
|
||||
.markitect-control-panel {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
top: 20px;
|
||||
right: -320px;
|
||||
width: 320px;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 12px 0 0 12px;
|
||||
box-shadow: -4px 0 20px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(5px);
|
||||
backdrop-filter: blur(10px);
|
||||
transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
.markitect-control-panel.expanded {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* Control ribbon - always visible */
|
||||
.markitect-control-ribbon {
|
||||
position: absolute;
|
||||
left: -40px;
|
||||
top: 0;
|
||||
width: 40px;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #2196f3, #1976d2);
|
||||
border-radius: 8px 0 0 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.markitect-control-ribbon:hover {
|
||||
background: linear-gradient(135deg, #1976d2, #1565c0);
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
|
||||
/* Panel content */
|
||||
.markitect-panel-header {
|
||||
background: linear-gradient(135deg, #2196f3, #1976d2);
|
||||
color: white;
|
||||
padding: 16px 20px;
|
||||
border-radius: 12px 0 0 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.markitect-panel-title {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.markitect-panel-version {
|
||||
font-size: 12px;
|
||||
opacity: 0.9;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markitect-panel-close {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 16px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.markitect-panel-close:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.markitect-panel-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.markitect-status-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.markitect-status-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
border-left: 4px solid #4caf50;
|
||||
}
|
||||
|
||||
.markitect-status-indicator.loading {
|
||||
border-left-color: #ff9800;
|
||||
}
|
||||
|
||||
.markitect-status-indicator.error {
|
||||
border-left-color: #f44336;
|
||||
background: #ffebee;
|
||||
}
|
||||
|
||||
.markitect-status-icon {
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markitect-status-text {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markitect-controls-section {
|
||||
border-top: 1px solid #e0e0e0;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.markitect-controls-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.markitect-control-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12px 16px;
|
||||
background: #2196f3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markitect-control-btn:hover {
|
||||
background: #1976d2;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.markitect-control-btn.secondary {
|
||||
background: #757575;
|
||||
}
|
||||
|
||||
.markitect-control-btn.secondary:hover {
|
||||
background: #616161;
|
||||
}
|
||||
|
||||
.markitect-control-btn .icon {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.markitect-save-info {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
background: #f5f5f5;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.markitect-error-details {
|
||||
display: none;
|
||||
background: #ffebee;
|
||||
border: 1px solid #f44336;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.markitect-error-title {
|
||||
font-weight: bold;
|
||||
color: #c62828;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.markitect-error-text {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.markitect-error-help {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* Content editing styles */
|
||||
.markitect-section-editable {
|
||||
border: 1px dashed transparent;
|
||||
padding: 8px;
|
||||
margin: 4px 0;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.markitect-section-editable:hover {
|
||||
border-color: #007acc;
|
||||
background: rgba(0, 122, 204, 0.05);
|
||||
}
|
||||
|
||||
.edit-mode textarea {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
font-family: monospace;
|
||||
font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Roboto Mono', monospace;
|
||||
border: 2px solid #007acc;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.edit-mode textarea:focus {
|
||||
outline: none;
|
||||
border-color: #1976d2;
|
||||
box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1);
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.markitect-control-panel {
|
||||
width: 280px;
|
||||
right: -280px;
|
||||
}
|
||||
}
|
||||
</style>"""
|
||||
|
||||
@@ -445,19 +655,19 @@ class DocumentManager:
|
||||
}
|
||||
|
||||
initializeEditor() {
|
||||
const header = document.createElement('div');
|
||||
header.className = 'markitect-floating-header';
|
||||
header.innerHTML = `
|
||||
<button onclick="markitectEditor.save()" title="Download edited file with timestamp">💾 Save & Download</button>
|
||||
<button onclick="markitectEditor.togglePreview()" title="Toggle preview mode">👁️ Preview</button>
|
||||
<span id="save-status" style="margin-left: 15px; font-size: 0.9em;">Ready</span>
|
||||
<span style="margin-left: 15px; font-size: 0.8em; color: #666;">
|
||||
Saves as: filename-edited-YYYY-MM-DD-HH-MM-SS.md
|
||||
</span>
|
||||
`;
|
||||
document.body.insertBefore(header, document.body.firstChild);
|
||||
|
||||
// Control panel is already in HTML, just make content editable
|
||||
this.makeContentEditable();
|
||||
|
||||
// Auto-expand control panel briefly to show it's available
|
||||
setTimeout(() => {
|
||||
const panel = document.getElementById('markitect-control-panel');
|
||||
if (panel) {
|
||||
panel.classList.add('expanded');
|
||||
setTimeout(() => {
|
||||
panel.classList.remove('expanded');
|
||||
}, 2000); // Show for 2 seconds then minimize
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
makeContentEditable() {
|
||||
@@ -613,7 +823,25 @@ class DocumentManager:
|
||||
}
|
||||
}
|
||||
|
||||
let markitectEditor;"""
|
||||
let markitectEditor;
|
||||
|
||||
// Control panel toggle functionality
|
||||
function toggleControlPanel() {
|
||||
const panel = document.getElementById('markitect-control-panel');
|
||||
if (panel) {
|
||||
panel.classList.toggle('expanded');
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-close panel when clicking outside
|
||||
document.addEventListener('click', function(event) {
|
||||
const panel = document.getElementById('markitect-control-panel');
|
||||
if (panel && panel.classList.contains('expanded')) {
|
||||
if (!panel.contains(event.target)) {
|
||||
panel.classList.remove('expanded');
|
||||
}
|
||||
}
|
||||
});"""
|
||||
|
||||
# Edit mode status and error reporting section
|
||||
edit_mode_html = ""
|
||||
@@ -692,20 +920,58 @@ class DocumentManager:
|
||||
version_info = "0.3.0"
|
||||
|
||||
edit_mode_html = f"""
|
||||
<div id="markitect-status" style="background: #e3f2fd; border-left: 4px solid #2196f3; padding: 12px; margin-bottom: 20px; font-family: monospace; font-size: 14px;">
|
||||
<div style="font-weight: bold; color: #1976d2;">📝 Markitect Edit Mode <span style="font-weight: normal; color: #666;">v{version_info}</span></div>
|
||||
<div id="status-message" style="margin-top: 8px;">Loading edit capabilities...</div>
|
||||
<div id="error-details" style="display: none; background: #ffebee; border: 1px solid #f44336; padding: 8px; margin-top: 8px; border-radius: 4px;">
|
||||
<div style="font-weight: bold; color: #c62828;">❌ Edit Mode Failed</div>
|
||||
<div id="error-text" style="margin-top: 4px; color: #666;"></div>
|
||||
<details style="margin-top: 8px;">
|
||||
<summary style="cursor: pointer; color: #1976d2;">🐛 Help us fix this issue</summary>
|
||||
<div style="margin-top: 8px; font-size: 12px; color: #666;">
|
||||
Please report this error with your browser info:
|
||||
<br>📋 Browser: <span id="browser-info"></span>
|
||||
<br>🔗 Create issue: <a href="https://github.com/anthropics/markitect/issues/new" target="_blank" style="color: #1976d2;">GitHub Issues</a>
|
||||
<!-- Floating Control Panel -->
|
||||
<div id="markitect-control-panel" class="markitect-control-panel">
|
||||
<!-- Control Ribbon - Always Visible -->
|
||||
<div class="markitect-control-ribbon" onclick="toggleControlPanel()" title="MarkiTect Editor Controls">
|
||||
📝
|
||||
</div>
|
||||
|
||||
<!-- Panel Header -->
|
||||
<div class="markitect-panel-header">
|
||||
<h3 class="markitect-panel-title">📝 MarkiTect Editor</h3>
|
||||
<p class="markitect-panel-version">v{version_info}</p>
|
||||
<button class="markitect-panel-close" onclick="toggleControlPanel()" title="Close panel">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Panel Body -->
|
||||
<div class="markitect-panel-body">
|
||||
<!-- Status Section -->
|
||||
<div class="markitect-status-section">
|
||||
<div id="status-indicator" class="markitect-status-indicator loading">
|
||||
<span class="markitect-status-icon">⏳</span>
|
||||
<div class="markitect-status-text" id="status-message">Loading edit capabilities...</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Error Details (hidden by default) -->
|
||||
<div id="error-details" class="markitect-error-details">
|
||||
<div class="markitect-error-title">❌ Edit Mode Failed</div>
|
||||
<div class="markitect-error-text" id="error-text"></div>
|
||||
<div class="markitect-error-help">
|
||||
📋 Browser: <span id="browser-info"></span><br>
|
||||
🔗 <a href="https://github.com/anthropics/markitect/issues/new" target="_blank" style="color: #1976d2;">Report Issue</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Controls Section -->
|
||||
<div class="markitect-controls-section">
|
||||
<div class="markitect-controls-grid">
|
||||
<button class="markitect-control-btn" onclick="markitectEditor.save()" title="Download edited content">
|
||||
<span class="icon">💾</span>
|
||||
Save & Download
|
||||
</button>
|
||||
<button class="markitect-control-btn secondary" onclick="markitectEditor.togglePreview()" title="Toggle preview mode">
|
||||
<span class="icon">👁️</span>
|
||||
Preview
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="markitect-save-info">
|
||||
<div id="save-status">Ready to save</div>
|
||||
<div style="margin-top: 4px; opacity: 0.8;">Saves as: filename-edited-YYYY-MM-DD-HH-MM-SS.md</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>"""
|
||||
|
||||
@@ -786,9 +1052,27 @@ class DocumentManager:
|
||||
// Status update utility
|
||||
function updateStatus(message, isError = false) {{
|
||||
const statusMsg = document.getElementById('status-message');
|
||||
const statusIndicator = document.getElementById('status-indicator');
|
||||
const statusIcon = document.querySelector('.markitect-status-icon');
|
||||
|
||||
if (statusMsg) {{
|
||||
statusMsg.textContent = message;
|
||||
statusMsg.style.color = isError ? '#c62828' : '#1976d2';
|
||||
}}
|
||||
|
||||
if (statusIndicator) {{
|
||||
// Remove all status classes
|
||||
statusIndicator.classList.remove('loading', 'error');
|
||||
|
||||
if (isError) {{
|
||||
statusIndicator.classList.add('error');
|
||||
if (statusIcon) statusIcon.textContent = '❌';
|
||||
}} else if (message.includes('Loading') || message.includes('Initializing')) {{
|
||||
statusIndicator.classList.add('loading');
|
||||
if (statusIcon) statusIcon.textContent = '⏳';
|
||||
}} else {{
|
||||
// Success state
|
||||
if (statusIcon) statusIcon.textContent = '✅';
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ class TestEditModeRegression:
|
||||
)
|
||||
|
||||
# Should contain error handling elements
|
||||
assert 'id="markitect-status"' in html_content
|
||||
assert 'id="markitect-control-panel"' in html_content
|
||||
assert 'id="status-message"' in html_content
|
||||
assert 'id="error-details"' in html_content
|
||||
assert 'reportEditModeError' in html_content
|
||||
|
||||
Reference in New Issue
Block a user