Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion neuron-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,8 @@ module.exports = {
css: [
require("path").resolve(__dirname, "neuron/theme/header-balance.css"),
require("path").resolve(__dirname, "neuron/theme/template-browser.css"),
require("path").resolve(__dirname, "neuron/theme/featurebase-left.css")
require("path").resolve(__dirname, "neuron/theme/featurebase-left.css"),
require("path").resolve(__dirname, "neuron/theme/chat-widget-layout.css")
],
scripts: [

Expand Down
106 changes: 106 additions & 0 deletions neuron/theme/chat-widget-layout.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/* Clean Sidebar Layout - Simple Approach */

/* Chat widget positioning */
#neuron-chat-widget {
position: fixed !important;
top: 48px !important;
right: 0px !important;
width: 380px !important;
height: calc(100vh - 48px) !important;
z-index: 10000 !important;
background: #1D1D1D !important;
border: 1px solid #333 !important;
}

/* Chat bubble positioning - above sidebar */
#neuron-chat-bubble {
z-index: 10003 !important; /* Above sidebar (10001) and separator (10002) */
}

/* SCENARIO 1: Chat open - Move sidebar left by 380px */
body.neuron-chat-open #red-ui-sidebar {
right: 380px !important;
z-index: 10001 !important;
/* Let Node-RED manage width naturally */
}

/* SCENARIO 2: Chat closed - Sidebar at normal position (right edge) */
body.neuron-chat-closed #red-ui-sidebar {
right: 0px !important;
z-index: 10001 !important;
/* Let Node-RED manage width naturally */
}

/* Sidebar separator (resize bar) positioning */
/* When chat open: separator = sidebar_width + 380px (chat width) */
body.neuron-chat-open #red-ui-sidebar-separator {
z-index: 10002 !important;
/* Position will be calculated by JavaScript based on actual sidebar width */
}

/* When chat closed: separator = sidebar_width */
body.neuron-chat-closed #red-ui-sidebar-separator {
z-index: 10002 !important;
/* Position will be calculated by JavaScript based on actual sidebar width */
}

/* Canvas (workspace) positioning */
/* Canvas positioning when sidebar is open - handled by JavaScript */
/* These rules are intentionally empty as positioning is calculated dynamically */

/* When sidebar is closed */
body.red-ui-sidebar-closed.neuron-chat-open #red-ui-workspace {
right: 380px !important; /* Just chat width */
}

body.red-ui-sidebar-closed.neuron-chat-closed #red-ui-workspace {
right: 0px !important; /* Full width */
}

body.red-ui-sidebar-closed #red-ui-editor-stack {
right: 1px !important;
}

/* Override Node-RED's sidebar separator positioning with maximum specificity */
#red-ui-main-container.red-ui-sidebar-closed body.neuron-chat-open #red-ui-sidebar-separator,
body.neuron-chat-open #red-ui-main-container.red-ui-sidebar-closed #red-ui-sidebar-separator,
html body.neuron-chat-open .red-ui-sidebar-closed #red-ui-sidebar-separator,
html #red-ui-main-container.red-ui-sidebar-closed body.neuron-chat-open #red-ui-sidebar-separator {
right: 380px !important; /* Force separator to chat's left edge when sidebar closed */
}

/* Additional override with style attribute specificity simulation */
body.neuron-chat-open #red-ui-sidebar-separator[style] {
right: 380px !important;
}

#red-ui-main-container.red-ui-sidebar-closed body.neuron-chat-closed #red-ui-sidebar-separator,
body.neuron-chat-closed #red-ui-main-container.red-ui-sidebar-closed #red-ui-sidebar-separator,
html body.neuron-chat-closed .red-ui-sidebar-closed #red-ui-sidebar-separator {
right: 0px !important; /* Force separator to right edge when both closed */
}

/* Ensure UI elements appear above everything */
.red-ui-menu-dropdown,
#template-browser-dialog,
.red-ui-dialog,
.ui-dialog,
.red-ui-popover,
.popover,
.red-ui-tourGuide,
.red-ui-notifications,
.red-ui-menu,
.dropdown-menu,
.context-menu,
.red-ui-editor-tray,
#red-ui-header .red-ui-menu-dropdown {
z-index: 11000 !important;
}

/* Smooth transitions */
#red-ui-workspace,
#red-ui-editor-stack,
#red-ui-sidebar,
#red-ui-sidebar-separator {
transition: right 0.3s ease !important;
}
111 changes: 73 additions & 38 deletions neuron/theme/dedicated-chat-window.html
Original file line number Diff line number Diff line change
Expand Up @@ -974,52 +974,78 @@
}
}

// Use updated flow context if available, otherwise fall back to localStorage
let flowContextToUse = null;
let rawFlowJsonToUse = null;

if (window.currentFlowContext) {
flowContextToUse = window.currentFlowContext;
rawFlowJsonToUse = window.currentFlowContext.rawFlowJson;
console.log('🔄 [FLOW SYNC] Using real-time flow context from main window');
} else {
console.log('⚠️ [FLOW SYNC] No real-time context, checking localStorage...');
// Fall back to localStorage
const chatData = getUserData('chat');
if (chatData?.flowContext) {
flowContextToUse = chatData.flowContext;
rawFlowJsonToUse = chatData.rawFlowJson;
console.log('🔄 [FLOW SYNC] Using flow context from localStorage');
}
}

const requestBody = {
message: message,
context: 'Neuron software context',
flowContext: null, // Will be populated if available
rawFlowJson: null // Will be populated if available
flowContext: flowContextToUse,
rawFlowJson: rawFlowJsonToUse
};

// Try to get flow context from localStorage if available
try {
const chatData = getUserData('chat');
console.log('📱 [SERVER DEBUG] Retrieved chat data for flow context:', chatData);

if (chatData && chatData.flowContext) {
requestBody.flowContext = chatData.flowContext;
console.log('📱 [SERVER DEBUG] Using flow context from localStorage:', chatData.flowContext.flowName);
}

if (chatData && chatData.rawFlowJson) {
requestBody.rawFlowJson = chatData.rawFlowJson;
console.log('📱 [SERVER DEBUG] Using raw flow JSON from localStorage, nodes:', chatData.rawFlowJson.length);
}

// If we don't have raw flow JSON, try to get it from the parent window
if (!requestBody.rawFlowJson && window.opener && !window.opener.closed) {
try {
console.log('📱 [SERVER DEBUG] Attempting to get raw flow JSON from parent window...');
// Try to access the parent window's RED object
if (window.opener.RED && window.opener.RED.nodes && typeof window.opener.RED.nodes.createCompleteNodeSet === 'function') {
requestBody.rawFlowJson = window.opener.RED.nodes.createCompleteNodeSet();
console.log('📱 [SERVER DEBUG] Successfully got raw flow JSON from parent window, nodes:', requestBody.rawFlowJson.length);
}
} catch (error) {
console.warn('📱 [SERVER DEBUG] Could not get raw flow JSON from parent window:', error);
// Log flow context usage with detailed debugging
console.log('🔍 [DEBUG] Checking flow context availability...');
console.log('🔍 [DEBUG] window.currentFlowContext exists:', !!window.currentFlowContext);
console.log('🔍 [DEBUG] window.currentFlowContext value:', window.currentFlowContext);
console.log('🔍 [DEBUG] window.lastFlowUpdate:', window.lastFlowUpdate);

if (window.currentFlowContext) {
console.log('🔄 [FLOW SYNC] Using updated flow context in dedicated window');
console.log('🔄 [FLOW SYNC] Flow name:', window.currentFlowContext.flowName);
console.log('🔄 [FLOW SYNC] Node count:', window.currentFlowContext.nodeCount);
console.log('🔄 [FLOW SYNC] Last update:', new Date(window.lastFlowUpdate || 0).toLocaleTimeString());
} else {
console.log('⚠️ [FLOW SYNC] No current flow context available in dedicated window');
console.log('⚠️ [FLOW SYNC] Falling back to localStorage flow context');
}

// Log what we're actually sending (removed localStorage override)
console.log('📱 [SERVER DEBUG] Final flow context being sent:', requestBody.flowContext);
if (requestBody.flowContext) {
console.log('📱 [SERVER DEBUG] Flow name:', requestBody.flowContext.flowName);
console.log('📱 [SERVER DEBUG] Node count:', requestBody.flowContext.nodeCount);
console.log('📱 [SERVER DEBUG] Node types:', requestBody.flowContext.nodeTypes);
} else {
console.log('⚠️ [SERVER DEBUG] No flow context available for AI request');
}

// If we don't have raw flow JSON, try to get it from the parent window
if (!requestBody.rawFlowJson && window.opener && !window.opener.closed) {
try {
console.log('📱 [SERVER DEBUG] Attempting to get raw flow JSON from parent window...');
// Try to access the parent window's RED object
if (window.opener.RED && window.opener.RED.nodes && typeof window.opener.RED.nodes.createCompleteNodeSet === 'function') {
requestBody.rawFlowJson = window.opener.RED.nodes.createCompleteNodeSet();
console.log('📱 [SERVER DEBUG] Successfully got raw flow JSON from parent window, nodes:', requestBody.rawFlowJson.length);
}
} catch (error) {
console.warn('📱 [SERVER DEBUG] Could not get raw flow JSON from parent window:', error);
}

console.log('📱 [SERVER DEBUG] Final request body flow context:', {
hasFlowContext: !!requestBody.flowContext,
hasRawFlowJson: !!requestBody.rawFlowJson,
flowContextKeys: requestBody.flowContext ? Object.keys(requestBody.flowContext) : [],
rawFlowJsonNodeCount: requestBody.rawFlowJson ? requestBody.rawFlowJson.length : 0
});

} catch (error) {
console.warn('📱 [SERVER DEBUG] Could not get flow context from localStorage:', error);
}

console.log('📱 [SERVER DEBUG] Final request body flow context:', {
hasFlowContext: !!requestBody.flowContext,
hasRawFlowJson: !!requestBody.rawFlowJson,
flowContextKeys: requestBody.flowContext ? Object.keys(requestBody.flowContext) : [],
rawFlowJsonNodeCount: requestBody.rawFlowJson ? requestBody.rawFlowJson.length : 0
});

// Get authentication token if available
const authToken = localStorage.getItem('chat-token');
Expand Down Expand Up @@ -1159,6 +1185,15 @@
console.log('📱 [DEDICATED CHAT] Reloading chat data...');
loadChatData();
}, 100);
} else if (event.data && event.data.type === 'FLOW_STATE_UPDATE') {
console.log('🔄 [FLOW SYNC] Received flow state update from main window');
console.log('🔄 [FLOW SYNC] New flow context:', event.data.flowContext);

// Store the updated flow context for use in chat messages
window.currentFlowContext = event.data.flowContext;
window.lastFlowUpdate = event.data.timestamp;

console.log('🔄 [FLOW SYNC] Flow context updated in dedicated window');
}
});

Expand Down
55 changes: 44 additions & 11 deletions neuron/theme/menu-customizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,28 @@
newMenuItem.style.cssText = 'margin: 0; padding: 0;';

newMenuItem.innerHTML = `
<a href="https://docs.neuron.world" target="_blank" rel="noopener noreferrer">
<i class="fa fa-external-link"></i>
<span class="red-ui-menu-label">Neuron AI Node Builder</span>
<a href="https://docs.neuron.world" target="_blank" rel="noopener noreferrer"
style="display: block !important; white-space: nowrap !important; padding: 3px 10px 3px 30px !important; color: inherit !important; text-decoration: none !important;">
<i class="fa fa-external-link" style="margin-right: 10px !important; width: 16px !important; display: inline-block !important; vertical-align: middle !important;"></i>
<span class="red-ui-menu-label" style="white-space: nowrap !important; vertical-align: middle !important;">Neuron documentation</span>
</a>
`;

// Add custom event handlers to prevent interference
// Create the tutorials menu item
const tutorialsMenuItem = document.createElement('li');
tutorialsMenuItem.id = 'menu-item-neuron-tutorials';
tutorialsMenuItem.className = 'neuron-tutorials-item custom-menu-item';
tutorialsMenuItem.style.cssText = 'margin: 0; padding: 0;';

tutorialsMenuItem.innerHTML = `
<a href="https://docs.neuron.world/node-builder-software/" target="_blank" rel="noopener noreferrer"
style="display: block !important; white-space: nowrap !important; padding: 3px 10px 3px 30px !important; color: inherit !important; text-decoration: none !important;">
<i class="fa fa-graduation-cap" style="margin-right: 10px !important; width: 16px !important; display: inline-block !important; vertical-align: middle !important;"></i>
<span class="red-ui-menu-label" style="white-space: nowrap !important; vertical-align: middle !important;">Tutorials</span>
</a>
`;

// Add custom event handlers for documentation menu item
const link = newMenuItem.querySelector('a');
link.addEventListener('mouseenter', function(e) {
e.stopPropagation();
Expand All @@ -38,23 +53,40 @@
this.style.color = 'inherit';
});

// Add custom event handlers for tutorials menu item
const tutorialsLink = tutorialsMenuItem.querySelector('a');
tutorialsLink.addEventListener('mouseenter', function(e) {
e.stopPropagation();
this.style.backgroundColor = '#2a2a2a';
this.style.color = '#ffffff';
});

tutorialsLink.addEventListener('mouseleave', function(e) {
e.stopPropagation();
this.style.backgroundColor = 'transparent';
this.style.color = 'inherit';
});

// Create a separator first
const separator = document.createElement('li');
separator.className = 'red-ui-menu-divider';

// Insert separator and menu item after the version number
// Insert separator and menu items after the version number
const versionItem = document.querySelector('#menu-item-node-red-version');
if (versionItem) {
// Insert separator after the version item
versionItem.parentNode.insertBefore(separator, versionItem.nextSibling);
// Then insert our menu item after the separator
// Then insert documentation menu item after the separator
versionItem.parentNode.insertBefore(newMenuItem, separator.nextSibling);
// Then insert tutorials menu item after the documentation item
versionItem.parentNode.insertBefore(tutorialsMenuItem, newMenuItem.nextSibling);

// Debug: Log the DOM structure
console.log('🔍 DOM Structure after insertion:');
console.log('Version item:', versionItem);
console.log('Separator:', separator);
console.log('Our menu item:', newMenuItem);
console.log('Documentation menu item:', newMenuItem);
console.log('Tutorials menu item:', tutorialsMenuItem);
console.log('Version item next sibling:', versionItem.nextSibling);

// Force the version item to maintain its styling with maximum specificity
Expand Down Expand Up @@ -90,7 +122,7 @@
}
}

console.log('✅ Custom menu item injected successfully!');
console.log('✅ Custom menu items injected successfully! (Documentation + Tutorials)');
} else {
console.log('⚠️ Keyboard shortcuts menu item not found, retrying...');
// Retry after a short delay
Expand All @@ -110,9 +142,10 @@
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList') {
// Check if our custom item exists
const existingItem = document.querySelector('#menu-item-neuron-docs');
if (!existingItem) {
// Check if our custom items exist
const existingDocsItem = document.querySelector('#menu-item-neuron-docs');
const existingTutorialsItem = document.querySelector('#menu-item-neuron-tutorials');
if (!existingDocsItem || !existingTutorialsItem) {
injectCustomMenuItem();
}
}
Expand Down
Loading
Loading