Loading...
@@ -1379,7 +1383,7 @@ class CleanDocumentManager:
this.setupDragHandlers(icon);
}}
- const title = header.querySelector('h3');
+ const title = header.querySelector('.control-title');
if (title) {{
title.style.cssText = `
margin: 0;
@@ -1387,7 +1391,14 @@ class CleanDocumentManager:
font-weight: 600;
flex-grow: 1;
line-height: 1;
+ cursor: pointer;
+ user-select: none;
`;
+
+ // Add click handler to toggle header-only mode
+ title.addEventListener('click', () => {{
+ this.toggleHeaderOnly();
+ }});
}}
const closeBtn = header.querySelector('.control-close');
@@ -1397,13 +1408,14 @@ class CleanDocumentManager:
border: none;
font-size: 14px;
cursor: pointer;
- color: #666;
+ color: #6c757d;
padding: 0;
width: 20px;
height: 20px;
- display: flex;
+ display: none;
align-items: center;
justify-content: center;
+ transition: color 0.2s ease;
`;
}}
}},
@@ -1459,14 +1471,23 @@ class CleanDocumentManager:
this.styleHeader();
this.styleContent();
+ this.addResizeHandle();
}},
collapse: function() {{
this.isExpanded = false;
+ this.isHeaderOnly = false; // Reset header-only state
const panel = this.element.querySelector('.control-panel');
const toggleBtn = this.element.querySelector('.control-toggle');
panel.style.display = 'none';
+
+ // Reset size to default
this.element.style.width = '40px';
+ this.element.style.height = 'auto';
+
+ // Remove resize handle
+ this.removeResizeHandle();
+
toggleBtn.style.display = 'block';
// Reset position to original compass location
@@ -1477,6 +1498,28 @@ class CleanDocumentManager:
this.element.style.transform = this.originalPosition.transform;
}},
+ toggleHeaderOnly: function() {{
+ if (!this.isExpanded) {{
+ // If collapsed, first expand normally
+ this.buildContent();
+ return;
+ }}
+
+ const content = this.element.querySelector('.control-content');
+
+ if (this.isHeaderOnly) {{
+ // Show content area (go to full expanded mode)
+ this.isHeaderOnly = false;
+ content.style.display = 'block';
+ console.log(`🎛️ ${{this.config.title}} expanded to full view`);
+ }} else {{
+ // Hide content area (go to header-only mode)
+ this.isHeaderOnly = true;
+ content.style.display = 'none';
+ console.log(`🎛️ ${{this.config.title}} collapsed to header only`);
+ }}
+ }},
+
setupDragHandlers: function(dragElement) {{
dragElement.addEventListener('mousedown', (e) => {{
this.isDragging = true;
@@ -1515,6 +1558,128 @@ class CleanDocumentManager:
dragElement.style.cursor = 'grab';
}}
}});
+ }},
+
+ // Add resize handle to expanded control
+ addResizeHandle: function() {{
+ // Remove existing resize handle if any
+ this.removeResizeHandle();
+
+ const resizeHandle = document.createElement('div');
+ resizeHandle.className = 'control-resize-handle';
+ // Create small circle for resize handle
+ resizeHandle.innerHTML = '';
+ resizeHandle.style.cssText = `
+ position: absolute;
+ bottom: 2px;
+ right: 2px;
+ width: 8px;
+ height: 8px;
+ cursor: nw-resize;
+ display: none;
+ user-select: none;
+ z-index: 1001;
+ background: #6c757d;
+ border-radius: 50%;
+ opacity: 0.6;
+ transition: opacity 0.2s ease;
+ `;
+
+ this.element.appendChild(resizeHandle);
+ this.setupResizeHandlers(resizeHandle);
+ this.setupHoverBehavior();
+ }},
+
+ // Setup hover behavior for resize handle and close button
+ setupHoverBehavior: function() {{
+ const resizeHandle = this.element.querySelector('.control-resize-handle');
+ const closeBtn = this.element.querySelector('.control-close');
+
+ if (resizeHandle && closeBtn) {{
+ // Show/hide on control hover
+ this.element.addEventListener('mouseenter', () => {{
+ resizeHandle.style.display = 'flex';
+ closeBtn.style.display = 'block';
+ }});
+
+ this.element.addEventListener('mouseleave', () => {{
+ resizeHandle.style.display = 'none';
+ closeBtn.style.display = 'none';
+ }});
+ }}
+ }},
+
+ // Remove resize handle
+ removeResizeHandle: function() {{
+ const existingHandle = this.element.querySelector('.control-resize-handle');
+ if (existingHandle) {{
+ existingHandle.remove();
+ }}
+ }},
+
+ // Set up resize event handlers
+ setupResizeHandlers: function(resizeHandle) {{
+ resizeHandle.addEventListener('mousedown', (e) => {{
+ this.isResizing = true;
+ const rect = this.element.getBoundingClientRect();
+ this.resizeStartSize = {{
+ width: rect.width,
+ height: rect.height,
+ startX: e.clientX,
+ startY: e.clientY
+ }};
+
+ resizeHandle.style.cursor = 'nw-resize';
+ resizeHandle.style.color = '#28a745';
+
+ e.preventDefault();
+ e.stopPropagation(); // Prevent triggering drag
+ }});
+
+ document.addEventListener('mousemove', (e) => {{
+ if (!this.isResizing || !this.isExpanded) return;
+
+ const deltaX = e.clientX - this.resizeStartSize.startX;
+ const deltaY = e.clientY - this.resizeStartSize.startY;
+
+ const newWidth = Math.max(this.defaultSize.minWidth, this.resizeStartSize.width + deltaX);
+ const newHeight = Math.max(this.defaultSize.minHeight, this.resizeStartSize.height + deltaY);
+
+ // Check viewport bounds
+ const maxWidth = window.innerWidth - this.element.offsetLeft;
+ const maxHeight = window.innerHeight - this.element.offsetTop;
+
+ const boundedWidth = Math.min(newWidth, maxWidth - 20);
+ const boundedHeight = Math.min(newHeight, maxHeight - 20);
+
+ this.element.style.width = boundedWidth + 'px';
+ this.element.style.height = boundedHeight + 'px';
+
+ // Ensure content areas resize properly
+ this.updateContentSize();
+ }});
+
+ document.addEventListener('mouseup', () => {{
+ if (this.isResizing) {{
+ this.isResizing = false;
+ resizeHandle.style.cursor = 'nw-resize';
+ resizeHandle.style.color = '#6c757d';
+ }}
+ }});
+ }},
+
+ // Update content area sizes during resize
+ updateContentSize: function() {{
+ const content = this.element.querySelector('.control-content');
+ if (content) {{
+ // Adjust content height to fit the resized control
+ const headerHeight = 40; // Header is 40px
+ const padding = 16; // Account for padding
+ const controlHeight = this.element.offsetHeight;
+ const availableHeight = controlHeight - headerHeight - padding;
+
+ content.style.maxHeight = Math.max(100, availableHeight) + 'px';
+ }}
}}
}};
@@ -1649,9 +1814,13 @@ document.addEventListener('DOMContentLoaded', function() {
// Abstract control properties
element: null,
isExpanded: false,
+ isHeaderOnly: false, // New state for header-only mode
isDragging: false,
+ isResizing: false, // New state for resizing mode
dragOffset: { x: 0, y: 0 },
+ resizeStartSize: { width: 280, height: 'auto' },
originalPosition: { top: '80px', left: '20px' },
+ defaultSize: { width: 280, minWidth: 200, minHeight: 150 },
// Configuration properties (to be overridden by subclasses)
config: {
@@ -1733,7 +1902,7 @@ document.addEventListener('DOMContentLoaded', function() {
Loading...
@@ -1831,7 +2000,7 @@ document.addEventListener('DOMContentLoaded', function() {
this.setupDragHandlers(icon);
}
- const title = header.querySelector('h3');
+ const title = header.querySelector('.control-title');
if (title) {
title.style.cssText = `
margin: 0;
@@ -1839,7 +2008,14 @@ document.addEventListener('DOMContentLoaded', function() {
font-weight: 600;
flex-grow: 1;
line-height: 1;
+ cursor: pointer;
+ user-select: none;
`;
+
+ // Add click handler to toggle header-only mode
+ title.addEventListener('click', () => {
+ this.toggleHeaderOnly();
+ });
}
const closeBtn = header.querySelector('.control-close');
@@ -1849,13 +2025,14 @@ document.addEventListener('DOMContentLoaded', function() {
border: none;
font-size: 14px;
cursor: pointer;
- color: #666;
+ color: #6c757d;
padding: 0;
width: 20px;
height: 20px;
- display: flex;
+ display: none;
align-items: center;
justify-content: center;
+ transition: color 0.2s ease;
`;
}
},
@@ -1911,14 +2088,23 @@ document.addEventListener('DOMContentLoaded', function() {
this.styleHeader();
this.styleContent();
+ this.addResizeHandle();
},
collapse: function() {
this.isExpanded = false;
+ this.isHeaderOnly = false; // Reset header-only state
const panel = this.element.querySelector('.control-panel');
const toggleBtn = this.element.querySelector('.control-toggle');
panel.style.display = 'none';
+
+ // Reset size to default
this.element.style.width = '40px';
+ this.element.style.height = 'auto';
+
+ // Remove resize handle
+ this.removeResizeHandle();
+
toggleBtn.style.display = 'block';
// Reset position to original compass location
@@ -1929,6 +2115,28 @@ document.addEventListener('DOMContentLoaded', function() {
this.element.style.transform = this.originalPosition.transform;
},
+ toggleHeaderOnly: function() {
+ if (!this.isExpanded) {
+ // If collapsed, first expand normally
+ this.buildContent();
+ return;
+ }
+
+ const content = this.element.querySelector('.control-content');
+
+ if (this.isHeaderOnly) {
+ // Show content area (go to full expanded mode)
+ this.isHeaderOnly = false;
+ content.style.display = 'block';
+ console.log(`🎛️ ${this.config.title} expanded to full view`);
+ } else {
+ // Hide content area (go to header-only mode)
+ this.isHeaderOnly = true;
+ content.style.display = 'none';
+ console.log(`🎛️ ${this.config.title} collapsed to header only`);
+ }
+ },
+
setupDragHandlers: function(dragElement) {
dragElement.addEventListener('mousedown', (e) => {
this.isDragging = true;
@@ -1967,6 +2175,128 @@ document.addEventListener('DOMContentLoaded', function() {
dragElement.style.cursor = 'grab';
}
});
+ },
+
+ // Add resize handle to expanded control (same as viewing mode)
+ addResizeHandle: function() {
+ // Remove existing resize handle if any
+ this.removeResizeHandle();
+
+ const resizeHandle = document.createElement('div');
+ resizeHandle.className = 'control-resize-handle';
+ // Create small circle for resize handle
+ resizeHandle.innerHTML = '';
+ resizeHandle.style.cssText = `
+ position: absolute;
+ bottom: 2px;
+ right: 2px;
+ width: 8px;
+ height: 8px;
+ cursor: nw-resize;
+ display: none;
+ user-select: none;
+ z-index: 1001;
+ background: #6c757d;
+ border-radius: 50%;
+ opacity: 0.6;
+ transition: opacity 0.2s ease;
+ `;
+
+ this.element.appendChild(resizeHandle);
+ this.setupResizeHandlers(resizeHandle);
+ this.setupHoverBehavior();
+ },
+
+ // Remove resize handle
+ removeResizeHandle: function() {
+ const existingHandle = this.element.querySelector('.control-resize-handle');
+ if (existingHandle) {
+ existingHandle.remove();
+ }
+ },
+
+ // Set up resize event handlers
+ setupResizeHandlers: function(resizeHandle) {
+ resizeHandle.addEventListener('mousedown', (e) => {
+ this.isResizing = true;
+ const rect = this.element.getBoundingClientRect();
+ this.resizeStartSize = {
+ width: rect.width,
+ height: rect.height,
+ startX: e.clientX,
+ startY: e.clientY
+ };
+
+ resizeHandle.style.cursor = 'nw-resize';
+ resizeHandle.style.background = 'rgba(40, 167, 69, 0.9)';
+
+ e.preventDefault();
+ e.stopPropagation(); // Prevent triggering drag
+ });
+
+ document.addEventListener('mousemove', (e) => {
+ if (!this.isResizing || !this.isExpanded) return;
+
+ const deltaX = e.clientX - this.resizeStartSize.startX;
+ const deltaY = e.clientY - this.resizeStartSize.startY;
+
+ const newWidth = Math.max(this.defaultSize.minWidth, this.resizeStartSize.width + deltaX);
+ const newHeight = Math.max(this.defaultSize.minHeight, this.resizeStartSize.height + deltaY);
+
+ // Check viewport bounds
+ const maxWidth = window.innerWidth - this.element.offsetLeft;
+ const maxHeight = window.innerHeight - this.element.offsetTop;
+
+ const boundedWidth = Math.min(newWidth, maxWidth - 20);
+ const boundedHeight = Math.min(newHeight, maxHeight - 20);
+
+ this.element.style.width = boundedWidth + 'px';
+ this.element.style.height = boundedHeight + 'px';
+
+ // Ensure content areas resize properly
+ this.updateContentSize();
+ });
+
+ document.addEventListener('mouseup', () => {
+ if (this.isResizing) {
+ this.isResizing = false;
+ resizeHandle.style.cursor = 'nw-resize';
+ resizeHandle.style.color = '#6c757d';
+ }
+ });
+ },
+
+ // Update content area sizes during resize
+ updateContentSize: function() {
+ const content = this.element.querySelector('.control-content');
+ if (content) {
+ // Adjust content height to fit the resized control
+ const headerHeight = 40; // Header is 40px
+ const padding = 16; // Account for padding
+ const controlHeight = this.element.offsetHeight;
+ const availableHeight = controlHeight - headerHeight - padding;
+
+ content.style.maxHeight = Math.max(100, availableHeight) + 'px';
+ }
+ },
+
+ // Setup hover behavior for resize handle and close button
+ setupHoverBehavior: function() {
+ const resizeHandle = this.element.querySelector('.control-resize-handle');
+ const closeBtn = this.element.querySelector('.control-close');
+
+ if (resizeHandle && closeBtn) {
+ // Show/hide on control hover
+ this.element.addEventListener('mouseenter', () => {
+ resizeHandle.style.display = 'flex';
+ closeBtn.style.display = 'block';
+ });
+
+ this.element.addEventListener('mouseleave', () => {
+ resizeHandle.style.display = 'none';
+ closeBtn.style.display = 'none';
+ });
+ }
}
};
@@ -2035,6 +2365,559 @@ document.addEventListener('DOMContentLoaded', function() {
console.error("ContentsControl failed to initialize:", error);
}
+ // Step 7: Initialize Independent Debug System
+ try {
+ // Create independent debug system using IndexedDB for persistence
+ window.MarkitectDebugSystem = {
+ db: null,
+ messages: [],
+ maxMessages: 1000,
+ isEnabled: true,
+ subscribers: [],
+
+ // Selection and filtering system
+ selectionCriteria: {
+ includeDocumentEvents: true,
+ includeSystemEvents: false,
+ includeControlEvents: true,
+ includeEditingEvents: true,
+ includeNavigationEvents: false,
+ includedHeadings: new Set(), // Track which document headings to monitor
+ excludedSources: new Set(['ContentsControl', 'DocumentNavigator'])
+ },
+
+ // Initialize IndexedDB for persistence
+ async init() {
+ return new Promise((resolve, reject) => {
+ const request = indexedDB.open('MarkitectDebugDB', 1);
+
+ request.onerror = () => reject(request.error);
+ request.onsuccess = () => {
+ this.db = request.result;
+ this.loadMessages().then(resolve);
+ };
+
+ request.onupgradeneeded = (e) => {
+ const db = e.target.result;
+ if (!db.objectStoreNames.contains('messages')) {
+ const store = db.createObjectStore('messages', { keyPath: 'id', autoIncrement: true });
+ store.createIndex('timestamp', 'timestamp', { unique: false });
+ store.createIndex('category', 'category', { unique: false });
+ }
+ };
+ });
+ },
+
+ // Add a debug message with selection filtering
+ async addMessage(message, category = 'INFO', source = 'System', context = {}) {
+ // Check if this message should be included based on selection criteria
+ if (!this.shouldIncludeMessage(message, category, source, context)) {
+ return null;
+ }
+
+ const messageObj = {
+ message: String(message),
+ category: category.toUpperCase(),
+ source: source,
+ context: context,
+ timestamp: new Date().toISOString(),
+ displayTime: new Date().toLocaleTimeString()
+ };
+
+ // Add to memory
+ this.messages.push(messageObj);
+
+ // Keep only last maxMessages in memory
+ if (this.messages.length > this.maxMessages) {
+ this.messages = this.messages.slice(-this.maxMessages);
+ }
+
+ // Persist to IndexedDB
+ if (this.db) {
+ try {
+ const transaction = this.db.transaction(['messages'], 'readwrite');
+ const store = transaction.objectStore('messages');
+ await store.add(messageObj);
+ } catch (e) {
+ console.warn('Failed to persist debug message:', e);
+ }
+ }
+
+ // Notify subscribers
+ this.subscribers.forEach(callback => {
+ try { callback(messageObj); } catch (e) { console.error('Debug subscriber error:', e); }
+ });
+
+ return messageObj;
+ },
+
+ // Selection filtering logic
+ shouldIncludeMessage(message, category, source, context) {
+ if (!this.isEnabled) return false;
+
+ // Check excluded sources
+ if (this.selectionCriteria.excludedSources.has(source)) {
+ return false;
+ }
+
+ // Category-based filtering
+ const categoryChecks = {
+ 'DOCUMENT': () => this.selectionCriteria.includeDocumentEvents,
+ 'SYSTEM': () => this.selectionCriteria.includeSystemEvents,
+ 'CONTROL': () => this.selectionCriteria.includeControlEvents,
+ 'EDITING': () => this.selectionCriteria.includeEditingEvents,
+ 'NAVIGATION': () => this.selectionCriteria.includeNavigationEvents
+ };
+
+ // Check if we have a specific category filter
+ if (context.eventType && categoryChecks[context.eventType]) {
+ return categoryChecks[context.eventType]();
+ }
+
+ // Document heading specific filtering
+ if (context.headingId) {
+ return this.selectionCriteria.includedHeadings.has(context.headingId) ||
+ this.selectionCriteria.includedHeadings.size === 0; // Include all if none specifically selected
+ }
+
+ // Default: include based on general settings
+ return category === 'ERROR' || // Always include errors
+ this.selectionCriteria.includeSystemEvents;
+ },
+
+ // Document structure awareness
+ scanDocumentStructure() {
+ const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
+ const documentStructure = {
+ headings: [],
+ totalSections: 0
+ };
+
+ headings.forEach((heading, index) => {
+ // Skip control headings (like Contents, Debug, etc.)
+ const text = heading.textContent.trim().toLowerCase();
+ if (text.includes('contents') || text.includes('debug') ||
+ text.includes('control') || text.includes('navigation')) {
+ return;
+ }
+
+ if (!heading.id) {
+ heading.id = `document-heading-${index + 1}`;
+ }
+
+ documentStructure.headings.push({
+ id: heading.id,
+ text: heading.textContent.trim(),
+ level: parseInt(heading.tagName.substring(1)),
+ element: heading
+ });
+ });
+
+ documentStructure.totalSections = documentStructure.headings.length;
+ this.documentStructure = documentStructure;
+
+ // Auto-include all document headings for monitoring
+ this.selectionCriteria.includedHeadings.clear();
+ documentStructure.headings.forEach(heading => {
+ this.selectionCriteria.includedHeadings.add(heading.id);
+ });
+
+ this.addMessage(`Document structure scanned: ${documentStructure.totalSections} sections found`,
+ 'INFO', 'DocumentScanner', { eventType: 'DOCUMENT', headings: documentStructure.headings });
+
+ return documentStructure;
+ },
+
+ // Selection management methods
+ includeHeading(headingId) {
+ this.selectionCriteria.includedHeadings.add(headingId);
+ },
+
+ excludeHeading(headingId) {
+ this.selectionCriteria.includedHeadings.delete(headingId);
+ },
+
+ includeSource(source) {
+ this.selectionCriteria.excludedSources.delete(source);
+ },
+
+ excludeSource(source) {
+ this.selectionCriteria.excludedSources.add(source);
+ },
+
+ // Get selection criteria for UI
+ getSelectionCriteria() {
+ return {
+ ...this.selectionCriteria,
+ includedHeadings: Array.from(this.selectionCriteria.includedHeadings),
+ excludedSources: Array.from(this.selectionCriteria.excludedSources)
+ };
+ },
+
+ // Load messages from IndexedDB
+ async loadMessages() {
+ if (!this.db) return;
+
+ try {
+ const transaction = this.db.transaction(['messages'], 'readonly');
+ const store = transaction.objectStore('messages');
+ const request = store.getAll();
+
+ request.onsuccess = () => {
+ this.messages = request.result.slice(-this.maxMessages);
+ console.log(`📊 Loaded ${this.messages.length} debug messages from IndexedDB`);
+ };
+ } catch (e) {
+ console.warn('Failed to load debug messages:', e);
+ }
+ },
+
+ // Clear all messages
+ async clearMessages() {
+ this.messages = [];
+
+ if (this.db) {
+ try {
+ const transaction = this.db.transaction(['messages'], 'readwrite');
+ const store = transaction.objectStore('messages');
+ await store.clear();
+ } catch (e) {
+ console.warn('Failed to clear debug messages from DB:', e);
+ }
+ }
+
+ this.subscribers.forEach(callback => {
+ try { callback({ type: 'clear' }); } catch (e) { console.error('Debug subscriber error:', e); }
+ });
+ },
+
+ // Subscribe to debug updates
+ subscribe(callback) {
+ this.subscribers.push(callback);
+ return () => {
+ const index = this.subscribers.indexOf(callback);
+ if (index > -1) this.subscribers.splice(index, 1);
+ };
+ },
+
+ // Get messages (with filtering)
+ getMessages(category = null, limit = null) {
+ let filtered = this.messages;
+
+ if (category) {
+ filtered = filtered.filter(msg => msg.category === category.toUpperCase());
+ }
+
+ if (limit) {
+ filtered = filtered.slice(-limit);
+ }
+
+ return filtered;
+ },
+
+ // Get recent messages
+ getRecentMessages(count = 50) {
+ return this.messages.slice(-count);
+ },
+
+ // Export messages as JSON
+ exportMessages() {
+ return JSON.stringify(this.messages, null, 2);
+ }
+ };
+
+ // Initialize the debug system
+ window.MarkitectDebugSystem.init().then(() => {
+ console.log('📊 Markitect Debug System initialized');
+
+ // Add initial message
+ window.MarkitectDebugSystem.addMessage('Markitect Debug System initialized', 'INFO', 'DebugSystem', {eventType: 'SYSTEM'});
+
+ }).catch(error => {
+ console.warn('📊 Debug System initialization failed, using memory only:', error);
+ window.MarkitectDebugSystem.addMessage('Debug System initialized (memory only)', 'WARNING', 'DebugSystem', {eventType: 'SYSTEM'});
+ });
+
+ } catch (error) {
+ console.error("Debug System initialization failed:", error);
+ }
+
+ // Step 8: Initialize DebugControl (new implementation based on Control class)
+ try {
+ const debugControl = Object.create(Control);
+
+ // Configure for debug functionality
+ debugControl.config = {
+ icon: '🪲',
+ title: 'Debug',
+ className: 'debug-control',
+ defaultContent: 'Debug panel controls',
+ ariaLabel: 'Debug Control',
+ position: 'ese' // East-south-east positioning
+ };
+
+ // Override buildContent method for debug functionality
+ debugControl.buildContent = function() {
+ console.log("🪲 Building debug control content...");
+
+ try {
+ const content = this.element.querySelector('.control-content');
+ if (!content) {
+ console.error("🪲 Debug control content element not found");
+ return;
+ }
+
+ // Build debug control panel with selection and filtering
+ content.innerHTML = `
+
+
+
+
Debug Messages
+
+
+
+
+
+
+
+
+
+
+
+ Debug System:
+
+
+
+
+
+
+ Click 📊 Scan to analyze document structure
+
+
+
+
+
+
+
+
+
+ `;
+
+ console.log("🪲 Debug control HTML content built successfully");
+
+ // Note: Debug switch state is set directly in HTML (checked attribute)
+ // No need to call updateDebugStatus() since checkbox manages its own state
+
+ // Set up periodic update of debug messages
+ try {
+ this.setupMessageUpdates();
+ } catch (e) {
+ console.log("🪲 Message updates setup failed:", e);
+ }
+
+ // Show panel and call expand
+ console.log("🪲 Calling expand...");
+ this.expand();
+ console.log("🪲 Expand called successfully");
+
+ } catch (error) {
+ console.error("🪲 Error in buildContent:", error);
+ // Fallback simple content
+ const content = this.element.querySelector('.control-content');
+ if (content) {
+ content.innerHTML = `
Debug control error: ${error.message}
`;
+ this.expand();
+ }
+ }
+ };
+
+ // Add method to update debug switch status (updated for new debug system)
+ debugControl.updateDebugStatus = function() {
+ const checkbox = this.element.querySelector('#debugToggle');
+ const statusText = this.element.querySelector('#debugStatus');
+
+ if (checkbox && statusText) {
+ try {
+ if (window.MarkitectDebugSystem) {
+ // Use the new debug system's enabled state
+ const isEnabled = window.MarkitectDebugSystem.isEnabled;
+ checkbox.checked = isEnabled;
+ statusText.textContent = isEnabled ? 'ON' : 'OFF';
+ statusText.style.color = isEnabled ? '#28a745' : '#dc3545';
+ } else {
+ // Fallback when debug system is not available
+ checkbox.checked = false;
+ statusText.textContent = 'UNAVAILABLE';
+ statusText.style.color = '#dc3545';
+ }
+ } catch (e) {
+ console.log("🪲 Error updating debug status:", e);
+ checkbox.checked = false;
+ statusText.textContent = 'ERROR';
+ statusText.style.color = '#dc3545';
+ }
+ }
+ };
+
+ // Add method to setup message updates
+ debugControl.setupMessageUpdates = function() {
+ try {
+ // Update messages every 500ms when debug control is open
+ this.messageUpdateInterval = setInterval(() => {
+ if (this.isExpanded && !this.isHeaderOnly) {
+ this.updateMessages();
+ // Note: updateDebugStatus() removed - checkbox manages its own state
+ }
+ }, 500);
+ console.log("🪲 Message update interval set up successfully");
+ } catch (e) {
+ console.log("🪲 Failed to set up message updates:", e);
+ }
+ };
+
+ // Add method to update debug messages display using new system
+ debugControl.updateMessages = function() {
+ const container = this.element.querySelector('#debugMessagesContainer');
+ if (!container) return;
+
+ try {
+ if (window.MarkitectDebugSystem) {
+ const messages = window.MarkitectDebugSystem.getRecentMessages(50);
+ if (messages && messages.length > 0) {
+ // Show messages in reverse order (newest first)
+ const reversedMessages = [...messages].reverse();
+ container.innerHTML = `
+
+ ${messages.length} messages (newest first) |
+ Add Test
+
+ ${reversedMessages.map(msg => `
+
+
+
+ [${msg.displayTime}]
+ ${msg.category}:
+ ${msg.message}
+
+ ${msg.source ? `
${msg.source}` : ''}
+
+ ${msg.context && msg.context.headingId ? `
+
+ 📍 Section: ${msg.context.headingId}
+
+ ` : ''}
+
+ `).join('')}
+ `;
+ } else {
+ container.innerHTML = `
+
+ No debug messages yet
+
+
+ `;
+ }
+ } else {
+ container.innerHTML = `
+
+ Debug system not initialized
+
+ `;
+ }
+ } catch (e) {
+ console.log("🪲 Error updating messages:", e);
+ container.innerHTML = `
+
+ Error loading messages: ${e.message}
+
+ `;
+ }
+ };
+
+ // Add method to get message colors by type
+ debugControl.getMessageColor = function(type) {
+ const colors = {
+ 'ERROR': '#dc3545',
+ 'WARNING': '#fd7e14',
+ 'SUCCESS': '#28a745',
+ 'INFO': '#17a2b8',
+ 'DEBUG': '#6f42c1'
+ };
+ return colors[type] || '#495057';
+ };
+
+ // Override collapse to clean up intervals
+ const originalCollapse = debugControl.collapse;
+ debugControl.collapse = function() {
+ if (this.messageUpdateInterval) {
+ clearInterval(this.messageUpdateInterval);
+ this.messageUpdateInterval = null;
+ }
+ originalCollapse.call(this);
+ };
+
+ // Create and show the debug control
+ console.log("🪲 Creating debug control...");
+ debugControl.createControl();
+ console.log("🪲 Debug control created, element:", debugControl.element);
+
+ // Make debug control globally accessible
+ window.debugControl = debugControl;
+ console.log("🪲 Debug control setup complete and globally accessible");
+ } catch (error) {
+ console.error("DebugControl failed to initialize:", error);
+ }
+
// Wire up event handlers
documentControls.setEventHandlers({
@@ -2063,12 +2946,12 @@ document.addEventListener('DOMContentLoaded', function() {
document.body.removeChild(a);
URL.revokeObjectURL(url);
- // Log success to debug panel
- debugPanel.addMessage(`Document saved as: ${editedFilename}`, 'SUCCESS');
+ // Log success to debug system
+ window.MarkitectDebugSystem?.addMessage(`Document saved as: ${editedFilename}`, 'SUCCESS', 'DocumentSaver', {eventType: 'DOCUMENT'});
console.log(`Document successfully saved as: ${editedFilename}`);
} catch (error) {
- debugPanel.addMessage(`Save failed: ${error.message}`, 'ERROR');
+ window.MarkitectDebugSystem?.addMessage(`Save failed: ${error.message}`, 'ERROR', 'DocumentSaver', {eventType: 'DOCUMENT'});
console.error('Save error:', error);
}
},
@@ -2083,28 +2966,21 @@ document.addEventListener('DOMContentLoaded', function() {
});
// Re-render all sections
domRenderer.renderAllSections(allSections);
- debugPanel.addMessage(`Reset all sections to original state`, 'INFO');
- },
- 'show-status': () => {
- const status = sectionManager.getDocumentStatus();
- alert(`Document Status:\\nTotal Sections: ${status.totalSections}\\nEditing Sections: ${status.editingSections}`);
- },
- 'toggle-debug': () => {
- debugPanel.toggle();
+ window.MarkitectDebugSystem?.addMessage(`Reset all sections to original state`, 'INFO', 'SectionManager', {eventType: 'EDITING'});
}
});
- // Set up debug panel integration
+ // Set up debug system integration with section-aware logging
sectionManager.on('sections-created', (data) => {
- debugPanel.addMessage(`Created ${data.count} sections`, 'INFO');
+ window.MarkitectDebugSystem?.addMessage(`Created ${data.count} sections`, 'INFO', 'SectionManager', {eventType: 'DOCUMENT'});
});
sectionManager.on('edit-started', (data) => {
- debugPanel.addMessage(`Edit started for section: ${data.sectionId}`, 'DEBUG');
+ window.MarkitectDebugSystem?.addMessage(`Edit started for section: ${data.sectionId}`, 'DEBUG', 'SectionManager', {eventType: 'EDITING', sectionId: data.sectionId});
});
sectionManager.on('changes-accepted', (data) => {
- debugPanel.addMessage(`Changes accepted for section: ${data.sectionId}`, 'SUCCESS');
+ window.MarkitectDebugSystem?.addMessage(`Changes accepted for section: ${data.sectionId}`, 'SUCCESS', 'SectionManager', {eventType: 'EDITING', sectionId: data.sectionId});
// Re-render the section to show updated content
const section = sectionManager.sections.get(data.sectionId);
if (section) {
@@ -2112,13 +2988,13 @@ document.addEventListener('DOMContentLoaded', function() {
if (sectionElement) {
const newElement = domRenderer.renderSection(section);
sectionElement.parentNode.replaceChild(newElement, sectionElement);
- debugPanel.addMessage(`DOM updated for section: ${data.sectionId}`, 'INFO');
+ window.MarkitectDebugSystem?.addMessage(`DOM updated for section: ${data.sectionId}`, 'INFO', 'DOMRenderer', {eventType: 'EDITING', sectionId: data.sectionId});
}
}
});
sectionManager.on('changes-cancelled', (data) => {
- debugPanel.addMessage(`Changes cancelled for section: ${data.sectionId}`, 'WARNING');
+ window.MarkitectDebugSystem?.addMessage(`Changes cancelled for section: ${data.sectionId}`, 'WARNING', 'SectionManager', {eventType: 'EDITING', sectionId: data.sectionId});
});
// Initialize with markdown content
@@ -2126,9 +3002,9 @@ document.addEventListener('DOMContentLoaded', function() {
if (markdownToRender.trim()) {
const sections = sectionManager.createSectionsFromMarkdown(markdownToRender);
domRenderer.renderAllSections(sections);
- debugPanel.addMessage(`Initialized with ${sections.length} sections`, 'INFO');
+ window.MarkitectDebugSystem?.addMessage(`Initialized with ${sections.length} sections`, 'INFO', 'DocumentRenderer', {eventType: 'DOCUMENT'});
} else {
- debugPanel.addMessage('No markdown content to initialize', 'WARNING');
+ window.MarkitectDebugSystem?.addMessage('No markdown content to initialize', 'WARNING', 'DocumentRenderer', {eventType: 'DOCUMENT'});
}
// Make components globally available for debugging