feat: refactor control panel architecture and fix layout issues

Base class architecture improvements:
- Centralize all panel layout, styling, and behavior in ControlBase
- Implement consistent generateContent() pattern for subclasses
- Add proper flexbox layout with fixed header and scrollable content
- Standardize title styling, positioning, and scroll behavior

Panel layout fixes:
- Fix content positioning to appear inside panels instead of floating above
- Implement proper height management (expands with content up to browser height)
- Add correct scroll boundaries with only content area scrolling
- Position resize handle outside scroll area to avoid scrollbar interference

Visual improvements:
- Fix rounded border appearance with proper overflow handling
- Ensure header respects panel corner radius
- Add proper content margins and padding
- Improve resize handle positioning and visibility

Architecture standardization:
- All panels now follow same base class pattern
- Individual panels only provide configuration and content generation
- Eliminate duplicate styling and layout code across controls
- Consistent behavior across all panel types

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-14 21:55:06 +01:00
parent 512085d283
commit f788ccdfd3
10 changed files with 348 additions and 184 deletions

View File

@@ -256,33 +256,41 @@ class ContentsControl extends ControlBase {
* Build the control content * Build the control content
* Override of base class method to provide contents-specific functionality * Override of base class method to provide contents-specific functionality
*/ */
buildContent() {
/**
* Generate contents control content (called by base class buildContent)
*/
generateContent() {
// Extract headings first
this.extractHeadings();
return this.safeOperation(() => { return this.safeOperation(() => {
// Extract headings on first build return this.generateContentsHTML();
this.extractHeadings(); }, 'Error generating contents', 'generateContent');
}
// Generate and set content /**
const content = this.element?.querySelector('.control-content'); * Override buildContent to add control reference and auto-refresh
if (content) { */
content.innerHTML = this.generateContentsHTML(); buildContent() {
super.buildContent();
// Store reference to this control for onclick handlers // Store reference to this control for onclick handlers
this.element.contentsControl = this; if (this.element) {
this.element.contentsControl = this;
}
// Set up auto-refresh for dynamic content
if (this.updateInterval) {
clearInterval(this.updateInterval);
}
this.updateInterval = setInterval(() => {
const currentHeadingCount = document.querySelectorAll('h1, h2, h3, h4, h5, h6').length;
if (currentHeadingCount !== this.headings.length) {
this.refreshContents();
} }
}, 5000); // Check every 5 seconds
// Set up auto-refresh for dynamic content
if (this.updateInterval) {
clearInterval(this.updateInterval);
}
this.updateInterval = setInterval(() => {
const currentHeadingCount = document.querySelectorAll('h1, h2, h3, h4, h5, h6').length;
if (currentHeadingCount !== this.headings.length) {
this.refreshContents();
}
}, 5000); // Check every 5 seconds
}, null, 'buildContent');
} }
/** /**

View File

@@ -260,6 +260,9 @@ class ControlBase {
// Style expanded panel // Style expanded panel
panel.style.cssText = ` panel.style.cssText = `
position: relative;
display: flex;
flex-direction: column;
background: rgba(248, 249, 250, 0.95); background: rgba(248, 249, 250, 0.95);
border: 1px solid #dee2e6; border: 1px solid #dee2e6;
border-radius: 8px; border-radius: 8px;
@@ -267,6 +270,10 @@ class ControlBase {
backdrop-filter: blur(8px); backdrop-filter: blur(8px);
min-width: 300px; min-width: 300px;
min-height: 200px; min-height: 200px;
max-height: calc(100vh - 40px);
width: auto;
height: auto;
overflow: hidden;
`; `;
// Style header // Style header
@@ -281,6 +288,22 @@ class ControlBase {
border-bottom: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6;
cursor: move; cursor: move;
user-select: none; user-select: none;
flex-shrink: 0;
min-height: 40px;
border-radius: 7px 7px 0 0;
margin: -1px -1px 0 -1px;
`;
}
// Style content area container
const contentArea = this.element.querySelector('.control-content');
if (contentArea) {
contentArea.style.cssText = `
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
`; `;
} }
@@ -512,15 +535,16 @@ class ControlBase {
resizeHandle.style.cssText = ` resizeHandle.style.cssText = `
position: absolute; position: absolute;
bottom: 4px; bottom: 4px;
right: 4px; right: 20px;
width: 8px; width: 12px;
height: 8px; height: 12px;
cursor: se-resize; cursor: se-resize;
font-size: 8px; font-size: 10px;
line-height: 1; line-height: 1;
user-select: none; user-select: none;
color: #999; color: #999;
background: transparent; background: transparent;
z-index: 10;
`; `;
// Add to the expanded panel // Add to the expanded panel
@@ -636,14 +660,55 @@ class ControlBase {
/** /**
* Build the control content (to be overridden by subclasses) * Build the control content (to be overridden by subclasses)
*/ */
/**
* Build content with consistent styling - calls subclass generateContent()
*/
buildContent() { buildContent() {
// Default implementation - subclasses should override this
const content = this.element?.querySelector('.control-content'); const content = this.element?.querySelector('.control-content');
if (content) { if (content) {
content.innerHTML = this.config.defaultContent; // Get content from subclass
const innerContent = this.generateContent ? this.generateContent() : this.config.defaultContent;
// 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;
font-size: 0.8rem;
box-sizing: border-box;
min-height: 0;
border-radius: 0 0 6px 6px;
">
<div class="control-content-body" style="
padding: 0;
margin-bottom: 25px;
">
${innerContent}
</div>
</div>
`;
} }
} }
/**
* Generate content - subclasses should override this method
* @returns {string} HTML content for the panel body
*/
generateContent() {
return this.config.defaultContent || `<p>Panel content goes here...</p>`;
}
/** /**
* Show the control * Show the control
*/ */

View File

@@ -423,35 +423,36 @@ class DebugControl extends ControlBase {
} }
/** /**
* Build the control content * Generate debug control content (called by base class buildContent)
* Override of base class method to provide debug-specific functionality */
generateContent() {
return this.safeOperation(() => {
return `
${this.generateSystemInfoHTML()}
${this.generatePerformanceHTML()}
${this.generateFilterControlsHTML()}
${this.generateMessagesHTML()}
${this.generateControlButtonsHTML()}
<div style="margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid #eee; font-size: 0.7rem; color: #666; text-align: center;">
Recording: ${this.isRecording ? '🟢 Active' : '🔴 Paused'} |
Filter: ${this.messageFilter.toUpperCase()} |
Messages: ${this.getFilteredMessages().length}/${this.messages.length}
</div>
`;
}, 'Error generating debug content', 'generateContent');
}
/**
* Override buildContent to add control reference
*/ */
buildContent() { buildContent() {
return this.safeOperation(() => { super.buildContent();
const content = this.element?.querySelector('.control-content');
if (content) {
content.innerHTML = `
<div style="padding: 1rem; font-size: 0.8rem;">
<div style="margin-top: 0; margin-bottom: 1rem; font-weight: 600; font-size: 1.1em; color: #333;">Debug Information</div>
${this.generateSystemInfoHTML()} // Store reference to this control for onclick handlers
${this.generatePerformanceHTML()} if (this.element) {
${this.generateFilterControlsHTML()} this.element.debugControl = this;
${this.generateMessagesHTML()} }
${this.generateControlButtonsHTML()}
<div style="margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid #eee; font-size: 0.7rem; color: #666; text-align: center;">
Recording: ${this.isRecording ? '🟢 Active' : '🔴 Paused'} |
Filter: ${this.messageFilter.toUpperCase()} |
Messages: ${this.getFilteredMessages().length}/${this.messages.length}
</div>
</div>
`;
// Store reference to this control for onclick handlers
this.element.debugControl = this;
}
}, null, 'buildContent');
} }
/** /**

View File

@@ -67,10 +67,7 @@ class EditControl extends ControlBase {
generateEditToolsHTML() { generateEditToolsHTML() {
return this.safeOperation(() => { return this.safeOperation(() => {
return ` return `
<div style="padding: 1rem; font-size: 0.8rem;"> <!-- Document Actions -->
<div style="margin-top: 0; margin-bottom: 1rem; font-weight: 600; font-size: 1.1em; color: #333;">Edit Tools</div>
<!-- Document Actions -->
<div class="action-section" style="margin-bottom: 1rem;"> <div class="action-section" style="margin-bottom: 1rem;">
<div style="margin: 0 0 0.5rem 0; font-size: 0.9em; color: #666; font-weight: 600;">Document Actions</div> <div style="margin: 0 0 0.5rem 0; font-size: 0.9em; color: #666; font-weight: 600;">Document Actions</div>
@@ -180,7 +177,6 @@ class EditControl extends ControlBase {
${this.unsavedChanges ? '<div style="color: #dc3545;">⚠️ Unsaved changes</div>' : ''} ${this.unsavedChanges ? '<div style="color: #dc3545;">⚠️ Unsaved changes</div>' : ''}
</div> </div>
</div> </div>
</div>
`; `;
}, '<p>Error generating edit tools</p>', 'generateEditToolsHTML'); }, '<p>Error generating edit tools</p>', 'generateEditToolsHTML');
@@ -539,16 +535,25 @@ class EditControl extends ControlBase {
* Build the control content * Build the control content
* Override of base class method to provide edit-specific functionality * Override of base class method to provide edit-specific functionality
*/ */
buildContent() { /**
* Generate edit control content (called by base class buildContent)
*/
generateContent() {
return this.safeOperation(() => { return this.safeOperation(() => {
const content = this.element?.querySelector('.control-content'); return this.generateEditToolsHTML();
if (content) { }, 'Error generating edit content', 'generateContent');
content.innerHTML = this.generateEditToolsHTML(); }
// Store reference to this control for onclick handlers /**
this.element.editControl = this; * Override buildContent to add control reference
} */
}, null, 'buildContent'); buildContent() {
super.buildContent();
// Store reference to this control for onclick handlers
if (this.element) {
this.element.editControl = this;
}
} }
/** /**

View File

@@ -131,10 +131,7 @@ class StatusControl extends ControlBase {
const formatNumber = (num) => num.toLocaleString(); const formatNumber = (num) => num.toLocaleString();
return ` return `
<div style="padding: 1rem; font-size: 0.8rem;"> <div class="stats-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-bottom: 1rem;">
<div style="margin-top: 0; margin-bottom: 1rem; font-weight: 600; font-size: 1.1em; color: #333;">Document Statistics</div>
<div class="stats-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-bottom: 1rem;">
<div class="stat-item"> <div class="stat-item">
<strong>Words:</strong><br> <strong>Words:</strong><br>
<span style="font-size: 1.1em; color: #007bff;">${formatNumber(this.stats.words)}</span> <span style="font-size: 1.1em; color: #007bff;">${formatNumber(this.stats.words)}</span>
@@ -206,7 +203,6 @@ class StatusControl extends ControlBase {
Updated: ${new Date(this.lastUpdateTime).toLocaleTimeString()} Updated: ${new Date(this.lastUpdateTime).toLocaleTimeString()}
</div> </div>
` : ''} ` : ''}
</div>
`; `;
}, '<p>Error displaying statistics</p>', 'formatStatistics'); }, '<p>Error displaying statistics</p>', 'formatStatistics');
@@ -322,30 +318,37 @@ class StatusControl extends ControlBase {
* Build the control content * Build the control content
* Override of base class method to provide status-specific functionality * Override of base class method to provide status-specific functionality
*/ */
buildContent() { /**
* Generate status control content (called by base class buildContent)
*/
generateContent() {
// Analyze document first
this.analyzeDocument();
return this.safeOperation(() => { return this.safeOperation(() => {
// Analyze document first return this.formatStatistics();
this.analyzeDocument(); }, 'Error generating status content', 'generateContent');
}
// Generate and set content /**
const content = this.element?.querySelector('.control-content'); * Override buildContent to add control reference and auto-refresh
if (content) { */
content.innerHTML = this.formatStatistics(); buildContent() {
super.buildContent();
// Store reference to this control for onclick handlers // Store reference to this control for onclick handlers
this.element.statusControl = this; if (this.element) {
} this.element.statusControl = this;
}
// Set up auto-refresh for dynamic content // Set up auto-refresh for dynamic content
if (this.updateInterval) { if (this.updateInterval) {
clearInterval(this.updateInterval); clearInterval(this.updateInterval);
} }
this.updateInterval = setInterval(() => { this.updateInterval = setInterval(() => {
this.refreshStats(); this.refreshStats();
}, 10000); // Update every 10 seconds }, 10000); // Update every 10 seconds
}, null, 'buildContent');
} }
/** /**

View File

@@ -256,33 +256,41 @@ class ContentsControl extends ControlBase {
* Build the control content * Build the control content
* Override of base class method to provide contents-specific functionality * Override of base class method to provide contents-specific functionality
*/ */
buildContent() {
/**
* Generate contents control content (called by base class buildContent)
*/
generateContent() {
// Extract headings first
this.extractHeadings();
return this.safeOperation(() => { return this.safeOperation(() => {
// Extract headings on first build return this.generateContentsHTML();
this.extractHeadings(); }, 'Error generating contents', 'generateContent');
}
// Generate and set content /**
const content = this.element?.querySelector('.control-content'); * Override buildContent to add control reference and auto-refresh
if (content) { */
content.innerHTML = this.generateContentsHTML(); buildContent() {
super.buildContent();
// Store reference to this control for onclick handlers // Store reference to this control for onclick handlers
this.element.contentsControl = this; if (this.element) {
this.element.contentsControl = this;
}
// Set up auto-refresh for dynamic content
if (this.updateInterval) {
clearInterval(this.updateInterval);
}
this.updateInterval = setInterval(() => {
const currentHeadingCount = document.querySelectorAll('h1, h2, h3, h4, h5, h6').length;
if (currentHeadingCount !== this.headings.length) {
this.refreshContents();
} }
}, 5000); // Check every 5 seconds
// Set up auto-refresh for dynamic content
if (this.updateInterval) {
clearInterval(this.updateInterval);
}
this.updateInterval = setInterval(() => {
const currentHeadingCount = document.querySelectorAll('h1, h2, h3, h4, h5, h6').length;
if (currentHeadingCount !== this.headings.length) {
this.refreshContents();
}
}, 5000); // Check every 5 seconds
}, null, 'buildContent');
} }
/** /**

View File

@@ -260,6 +260,9 @@ class ControlBase {
// Style expanded panel // Style expanded panel
panel.style.cssText = ` panel.style.cssText = `
position: relative;
display: flex;
flex-direction: column;
background: rgba(248, 249, 250, 0.95); background: rgba(248, 249, 250, 0.95);
border: 1px solid #dee2e6; border: 1px solid #dee2e6;
border-radius: 8px; border-radius: 8px;
@@ -267,6 +270,10 @@ class ControlBase {
backdrop-filter: blur(8px); backdrop-filter: blur(8px);
min-width: 300px; min-width: 300px;
min-height: 200px; min-height: 200px;
max-height: calc(100vh - 40px);
width: auto;
height: auto;
overflow: hidden;
`; `;
// Style header // Style header
@@ -281,6 +288,22 @@ class ControlBase {
border-bottom: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6;
cursor: move; cursor: move;
user-select: none; user-select: none;
flex-shrink: 0;
min-height: 40px;
border-radius: 7px 7px 0 0;
margin: -1px -1px 0 -1px;
`;
}
// Style content area container
const contentArea = this.element.querySelector('.control-content');
if (contentArea) {
contentArea.style.cssText = `
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
`; `;
} }
@@ -512,15 +535,16 @@ class ControlBase {
resizeHandle.style.cssText = ` resizeHandle.style.cssText = `
position: absolute; position: absolute;
bottom: 4px; bottom: 4px;
right: 4px; right: 20px;
width: 8px; width: 12px;
height: 8px; height: 12px;
cursor: se-resize; cursor: se-resize;
font-size: 8px; font-size: 10px;
line-height: 1; line-height: 1;
user-select: none; user-select: none;
color: #999; color: #999;
background: transparent; background: transparent;
z-index: 10;
`; `;
// Add to the expanded panel // Add to the expanded panel
@@ -636,14 +660,55 @@ class ControlBase {
/** /**
* Build the control content (to be overridden by subclasses) * Build the control content (to be overridden by subclasses)
*/ */
/**
* Build content with consistent styling - calls subclass generateContent()
*/
buildContent() { buildContent() {
// Default implementation - subclasses should override this
const content = this.element?.querySelector('.control-content'); const content = this.element?.querySelector('.control-content');
if (content) { if (content) {
content.innerHTML = this.config.defaultContent; // Get content from subclass
const innerContent = this.generateContent ? this.generateContent() : this.config.defaultContent;
// 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;
font-size: 0.8rem;
box-sizing: border-box;
min-height: 0;
border-radius: 0 0 6px 6px;
">
<div class="control-content-body" style="
padding: 0;
margin-bottom: 25px;
">
${innerContent}
</div>
</div>
`;
} }
} }
/**
* Generate content - subclasses should override this method
* @returns {string} HTML content for the panel body
*/
generateContent() {
return this.config.defaultContent || `<p>Panel content goes here...</p>`;
}
/** /**
* Show the control * Show the control
*/ */

View File

@@ -423,35 +423,36 @@ class DebugControl extends ControlBase {
} }
/** /**
* Build the control content * Generate debug control content (called by base class buildContent)
* Override of base class method to provide debug-specific functionality */
generateContent() {
return this.safeOperation(() => {
return `
${this.generateSystemInfoHTML()}
${this.generatePerformanceHTML()}
${this.generateFilterControlsHTML()}
${this.generateMessagesHTML()}
${this.generateControlButtonsHTML()}
<div style="margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid #eee; font-size: 0.7rem; color: #666; text-align: center;">
Recording: ${this.isRecording ? '🟢 Active' : '🔴 Paused'} |
Filter: ${this.messageFilter.toUpperCase()} |
Messages: ${this.getFilteredMessages().length}/${this.messages.length}
</div>
`;
}, 'Error generating debug content', 'generateContent');
}
/**
* Override buildContent to add control reference
*/ */
buildContent() { buildContent() {
return this.safeOperation(() => { super.buildContent();
const content = this.element?.querySelector('.control-content');
if (content) {
content.innerHTML = `
<div style="padding: 1rem; font-size: 0.8rem;">
<div style="margin-top: 0; margin-bottom: 1rem; font-weight: 600; font-size: 1.1em; color: #333;">Debug Information</div>
${this.generateSystemInfoHTML()} // Store reference to this control for onclick handlers
${this.generatePerformanceHTML()} if (this.element) {
${this.generateFilterControlsHTML()} this.element.debugControl = this;
${this.generateMessagesHTML()} }
${this.generateControlButtonsHTML()}
<div style="margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid #eee; font-size: 0.7rem; color: #666; text-align: center;">
Recording: ${this.isRecording ? '🟢 Active' : '🔴 Paused'} |
Filter: ${this.messageFilter.toUpperCase()} |
Messages: ${this.getFilteredMessages().length}/${this.messages.length}
</div>
</div>
`;
// Store reference to this control for onclick handlers
this.element.debugControl = this;
}
}, null, 'buildContent');
} }
/** /**

View File

@@ -67,10 +67,7 @@ class EditControl extends ControlBase {
generateEditToolsHTML() { generateEditToolsHTML() {
return this.safeOperation(() => { return this.safeOperation(() => {
return ` return `
<div style="padding: 1rem; font-size: 0.8rem;"> <!-- Document Actions -->
<div style="margin-top: 0; margin-bottom: 1rem; font-weight: 600; font-size: 1.1em; color: #333;">Edit Tools</div>
<!-- Document Actions -->
<div class="action-section" style="margin-bottom: 1rem;"> <div class="action-section" style="margin-bottom: 1rem;">
<div style="margin: 0 0 0.5rem 0; font-size: 0.9em; color: #666; font-weight: 600;">Document Actions</div> <div style="margin: 0 0 0.5rem 0; font-size: 0.9em; color: #666; font-weight: 600;">Document Actions</div>
@@ -180,7 +177,6 @@ class EditControl extends ControlBase {
${this.unsavedChanges ? '<div style="color: #dc3545;">⚠️ Unsaved changes</div>' : ''} ${this.unsavedChanges ? '<div style="color: #dc3545;">⚠️ Unsaved changes</div>' : ''}
</div> </div>
</div> </div>
</div>
`; `;
}, '<p>Error generating edit tools</p>', 'generateEditToolsHTML'); }, '<p>Error generating edit tools</p>', 'generateEditToolsHTML');
@@ -539,16 +535,25 @@ class EditControl extends ControlBase {
* Build the control content * Build the control content
* Override of base class method to provide edit-specific functionality * Override of base class method to provide edit-specific functionality
*/ */
buildContent() { /**
* Generate edit control content (called by base class buildContent)
*/
generateContent() {
return this.safeOperation(() => { return this.safeOperation(() => {
const content = this.element?.querySelector('.control-content'); return this.generateEditToolsHTML();
if (content) { }, 'Error generating edit content', 'generateContent');
content.innerHTML = this.generateEditToolsHTML(); }
// Store reference to this control for onclick handlers /**
this.element.editControl = this; * Override buildContent to add control reference
} */
}, null, 'buildContent'); buildContent() {
super.buildContent();
// Store reference to this control for onclick handlers
if (this.element) {
this.element.editControl = this;
}
} }
/** /**

View File

@@ -131,10 +131,7 @@ class StatusControl extends ControlBase {
const formatNumber = (num) => num.toLocaleString(); const formatNumber = (num) => num.toLocaleString();
return ` return `
<div style="padding: 1rem; font-size: 0.8rem;"> <div class="stats-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-bottom: 1rem;">
<div style="margin-top: 0; margin-bottom: 1rem; font-weight: 600; font-size: 1.1em; color: #333;">Document Statistics</div>
<div class="stats-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-bottom: 1rem;">
<div class="stat-item"> <div class="stat-item">
<strong>Words:</strong><br> <strong>Words:</strong><br>
<span style="font-size: 1.1em; color: #007bff;">${formatNumber(this.stats.words)}</span> <span style="font-size: 1.1em; color: #007bff;">${formatNumber(this.stats.words)}</span>
@@ -206,7 +203,6 @@ class StatusControl extends ControlBase {
Updated: ${new Date(this.lastUpdateTime).toLocaleTimeString()} Updated: ${new Date(this.lastUpdateTime).toLocaleTimeString()}
</div> </div>
` : ''} ` : ''}
</div>
`; `;
}, '<p>Error displaying statistics</p>', 'formatStatistics'); }, '<p>Error displaying statistics</p>', 'formatStatistics');
@@ -322,30 +318,37 @@ class StatusControl extends ControlBase {
* Build the control content * Build the control content
* Override of base class method to provide status-specific functionality * Override of base class method to provide status-specific functionality
*/ */
buildContent() { /**
* Generate status control content (called by base class buildContent)
*/
generateContent() {
// Analyze document first
this.analyzeDocument();
return this.safeOperation(() => { return this.safeOperation(() => {
// Analyze document first return this.formatStatistics();
this.analyzeDocument(); }, 'Error generating status content', 'generateContent');
}
// Generate and set content /**
const content = this.element?.querySelector('.control-content'); * Override buildContent to add control reference and auto-refresh
if (content) { */
content.innerHTML = this.formatStatistics(); buildContent() {
super.buildContent();
// Store reference to this control for onclick handlers // Store reference to this control for onclick handlers
this.element.statusControl = this; if (this.element) {
} this.element.statusControl = this;
}
// Set up auto-refresh for dynamic content // Set up auto-refresh for dynamic content
if (this.updateInterval) { if (this.updateInterval) {
clearInterval(this.updateInterval); clearInterval(this.updateInterval);
} }
this.updateInterval = setInterval(() => { this.updateInterval = setInterval(() => {
this.refreshStats(); this.refreshStats();
}, 10000); // Update every 10 seconds }, 10000); // Update every 10 seconds
}, null, 'buildContent');
} }
/** /**