feat: comprehensive control panel UI improvements

- Fix version information display with actual Markitect version
- Implement auto-resize functionality with double-click on resize dot
- Add viewport repositioning to keep panels visible during auto-resize
- Reduce title bar height by 25% for more compact appearance
- Remove duplicate content titles below titlebars across all panels
- Optimize scrollbar positioning to right border with proper spacing
- Reposition resize dot to optimal corner location (bottom: 0px, right: -4px)
- Set default panel height to 1/3 of window height
- Fix Debug panel title formatting consistency
- Remove duplicate initialization warnings
- Clean up panel layout with proper margin management (10px bottom margin)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-14 22:51:25 +01:00
parent f788ccdfd3
commit 4e3f112987
9 changed files with 236 additions and 77 deletions

View File

@@ -59,7 +59,10 @@ class ControlBase {
this.isDragging = false;
this.isResizing = false;
this.position = { x: 0, y: 0 };
this.size = { width: 300, height: 200 };
this.size = {
width: 300,
height: Math.floor(window.innerHeight / 3)
};
this.originalPosition = null; // Store original position for collapse
// Event handlers storage
@@ -258,6 +261,9 @@ class ControlBase {
panel.style.display = 'block';
toggleBtn.style.display = 'none';
// Calculate default height as 1/3 of window height
const defaultHeight = Math.floor(window.innerHeight / 3);
// Style expanded panel
panel.style.cssText = `
position: relative;
@@ -272,7 +278,7 @@ class ControlBase {
min-height: 200px;
max-height: calc(100vh - 40px);
width: auto;
height: auto;
height: ${defaultHeight}px;
overflow: hidden;
`;
@@ -283,13 +289,13 @@ class ControlBase {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
padding: 4px 12px;
background: rgba(0,0,0,0.05);
border-bottom: 1px solid #dee2e6;
cursor: move;
user-select: none;
flex-shrink: 0;
min-height: 40px;
min-height: 24px;
border-radius: 7px 7px 0 0;
margin: -1px -1px 0 -1px;
`;
@@ -365,7 +371,7 @@ class ControlBase {
// Reset internal size tracking
this.size.width = 300;
this.size.height = 200;
this.size.height = Math.floor(window.innerHeight / 3);
this.storedWidth = null;
// Remove resize handle
@@ -534,8 +540,8 @@ class ControlBase {
resizeHandle.innerHTML = '●'; // Dot resize indicator
resizeHandle.style.cssText = `
position: absolute;
bottom: 4px;
right: 20px;
bottom: 0px;
right: -4px;
width: 12px;
height: 12px;
cursor: se-resize;
@@ -554,6 +560,7 @@ class ControlBase {
// Set up resize handlers
this.addEventListener(resizeHandle, 'mousedown', (e) => this.startResize(e));
this.addEventListener(resizeHandle, 'dblclick', (e) => this.autoResizeToContent(e));
}
}
@@ -647,6 +654,89 @@ class ControlBase {
}
}
/**
* Auto-resize panel to fit content size with viewport repositioning
*/
autoResizeToContent(event) {
return this.safeOperation(() => {
event.preventDefault();
event.stopPropagation();
if (!this.isExpanded) return;
const panel = this.element?.querySelector('.control-panel-expanded');
const contentBody = this.element?.querySelector('.control-content-body');
if (!panel || !contentBody) return;
// Get current panel position
const rect = panel.getBoundingClientRect();
const currentLeft = rect.left;
const currentTop = rect.top;
// Measure content size by temporarily allowing natural sizing
const originalOverflow = contentBody.style.overflow;
const originalMaxHeight = panel.style.maxHeight;
const originalHeight = panel.style.height;
const originalWidth = panel.style.width;
// Temporarily remove constraints to measure natural size
contentBody.style.overflow = 'visible';
panel.style.maxHeight = 'none';
panel.style.height = 'auto';
panel.style.width = 'auto';
// Force reflow and measure
panel.offsetHeight; // Force reflow
const contentRect = contentBody.getBoundingClientRect();
const headerHeight = this.element.querySelector('.control-header')?.offsetHeight || 24;
// Calculate ideal size with padding and margins
const idealWidth = Math.max(300, Math.min(window.innerWidth - 40, contentRect.width + 40));
const idealHeight = Math.max(200, Math.min(window.innerHeight - 40, contentRect.height + headerHeight + 40));
// Restore original constraints
contentBody.style.overflow = originalOverflow;
panel.style.maxHeight = originalMaxHeight;
// Calculate new position to keep panel in viewport
let newLeft = currentLeft;
let newTop = currentTop;
// Adjust position if panel would go outside viewport
if (currentLeft + idealWidth > window.innerWidth) {
newLeft = window.innerWidth - idealWidth - 20;
}
if (newLeft < 20) {
newLeft = 20;
}
if (currentTop + idealHeight > window.innerHeight) {
newTop = window.innerHeight - idealHeight - 20;
}
if (newTop < 20) {
newTop = 20;
}
// Apply new size and position
panel.style.width = `${idealWidth}px`;
panel.style.height = `${idealHeight}px`;
// Update position if it changed
if (newLeft !== currentLeft || newTop !== currentTop) {
this.element.style.left = `${newLeft}px`;
this.element.style.top = `${newTop}px`;
this.position.x = newLeft;
this.position.y = newTop;
}
// Update internal size tracking
this.size.width = idealWidth;
this.size.height = idealHeight;
}, null, 'autoResizeToContent');
}
/**
* Position the control based on compass position (used by show method)
*/
@@ -671,20 +761,11 @@ class ControlBase {
// Apply consistent container styling
content.innerHTML = `
<div class="control-content-title" style="
margin: 0 0 1rem 0;
font-weight: 600;
font-size: 1.1em;
color: #333;
padding: 1rem 1rem 0 1rem;
flex-shrink: 0;
">${this.config.title}</div>
<div class="control-content-container" style="
flex: 1;
overflow-y: auto;
margin: 0 1rem;
padding: 0 0 1rem 0;
margin: 0 0 10px 1rem;
padding: 0.75rem 1rem 1rem 0;
font-size: 0.8rem;
box-sizing: border-box;
min-height: 0;
@@ -692,7 +773,7 @@ class ControlBase {
">
<div class="control-content-body" style="
padding: 0;
margin-bottom: 25px;
margin-bottom: 0;
">
${innerContent}
</div>