Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
5 changes: 2 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,8 @@ FALKORDB_URL=redis://localhost:6379/0 # REQUIRED - change to your FalkorDB URL
# GITHUB_CLIENT_ID=your_github_client_id
# GITHUB_CLIENT_SECRET=your_github_client_secret

# If your OAuth app uses a different base URL than the request base (e.g., using 127.0.0.1 vs localhost)
# you can override the base used for building callback URLs. Example:
# OAUTH_BASE_URL=http://localhost:5000
# FalkorDB configuration
FALKORDB_URL=redis://localhost:6379
Comment thread
Anchel123 marked this conversation as resolved.
Resolved

# -----------------------------
# Email Configuration (optional - for sending invitation emails)
Expand Down
55 changes: 47 additions & 8 deletions app/public/css/menu.css
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,24 @@
border-color: var(--text-secondary);
}

#query-final-result-table {
border-collapse: collapse;
}

#query-final-result-table th,
#query-final-result-table td {
padding: 4px 8px;
}

#query-final-result-table th:not(:first-child),
#query-final-result-table td:not(:first-child) {
border-left: 1px solid var(--text-primary);
}

#query-final-result-table td {
border-top: 1px solid var(--text-primary);
}

#graph-select {
height: 100%;
padding: 8px 12px;
Expand All @@ -135,19 +153,40 @@
cursor: pointer;
}

/* Graph canvas container inside schema sidebar */
#schema-graph {
width: 100%;
height: 100%;
min-height: 300px;
background: transparent;
height: 100%;
}

#schema-content {
position: relative;
width: 100%;
height: 100%;
}

#schema-controls {
position: absolute;
padding: 10px;
bottom: 15px;
left: 0px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 10px;
z-index: 10;
pointer-events: none;
}
Comment thread
Anchel123 marked this conversation as resolved.
Resolved

/* Ensure canvas inside ForceGraph fills the container */
.force-graph-container, #schema-graph > canvas {
width: 100% !important;
height: 100% !important;
#schema-controls button {
display: flex;
align-items: center;
justify-content: center;
color: var(--text-primary);
background-color: transparent;
pointer-events: auto;
border: none;
cursor: pointer;
}

#graph-select:focus {
Expand Down
2 changes: 1 addition & 1 deletion app/templates/base.j2
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<!-- End Google Tag Manager -->
{% endif %}
<script src="https://unpkg.com/d3"></script>
<script src="https://unpkg.com/force-graph"></script>
<script src="https://cdn.jsdelivr.net/npm/force-graph@1.50.1"></script>
</head>
<body>
{% if google_tag_manager_id %}
Expand Down
31 changes: 31 additions & 0 deletions app/templates/components/sidebar_schema.j2
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,35 @@
<div id="schema-content">
<div id="schema-graph"></div>
</div>
<div id="schema-controls">
<button id="schema-controls-zoom-in">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-zoom-in-icon lucide-zoom-in">
<circle cx="11" cy="11" r="8" />
<line x1="21" x2="16.65" y1="21" y2="16.65" />
<line x1="11" x2="11" y1="8" y2="14" />
<line x1="8" x2="14" y1="11" y2="11" />
</svg>
</button>
<button id="schema-controls-zoom-out">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-zoom-out-icon lucide-zoom-out">
<circle cx="11" cy="11" r="8" />
<line x1="21" x2="16.65" y1="21" y2="16.65" />
<line x1="8" x2="14" y1="11" y2="11" />
</svg>
</button>
<button id="schema-controls-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-shrink-icon lucide-shrink">
<path d="m15 15 6 6m-6-6v4.8m0-4.8h4.8" />
<path d="M9 19.8V15m0 0H4.2M9 15l-6 6" />
<path d="M15 4.2V9m0 0h4.8M15 9l6-6" />
<path d="M9 4.2V9m0 0H4.2M9 9 3 3" />
</svg>
</button>
</div>
</div>
7 changes: 5 additions & 2 deletions app/ts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { setupAuthenticationModal, setupDatabaseModal } from './modules/modals';
import { showGraph } from './modules/schema';
import { setupTokenManagement } from './modules/tokens';
import { initLeftToolbar } from './modules/left_toolbar';
import { resizeGraph } from './modules/schema';

async function loadAndShowGraph(selected: string | undefined) {
if (!selected) return;
Expand Down Expand Up @@ -68,7 +69,8 @@ function setupEventListeners() {
toggleContainer(DOM.schemaContainer as HTMLElement, async () => {
const selected = getSelectedGraph();
if (!selected) return;
await loadAndShowGraph(selected);
loadAndShowGraph(selected);
setTimeout(resizeGraph, 450);
});
});

Expand All @@ -92,7 +94,8 @@ function setupEventListeners() {
const selected = getSelectedGraph();
if (!selected) return;
if (DOM.schemaContainer && DOM.schemaContainer.classList.contains('open')) {
await loadAndShowGraph(selected);
loadAndShowGraph(selected);
setTimeout(resizeGraph, 450);
}
});

Expand Down
41 changes: 21 additions & 20 deletions app/ts/modules/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ export async function sendMessage() {

const selectedValue = getSelectedGraph() || '';
if (!selectedValue) {
addMessage('Please select a graph from the dropdown before sending a message.', false, true);
addMessage('Please select a graph from the dropdown before sending a message.', "followup");
return;
}

if (state.currentRequestController) {
state.currentRequestController.abort();
}

addMessage(message, true, false, false, false, (window as any).currentUser || null);
addMessage(message, "user", (window as any).currentUser || null);
if (DOM.messageInput) DOM.messageInput.value = '';

// Show typing indicator
DOM.inputContainer?.classList.add('loading');
if (DOM.submitButton) DOM.submitButton.style.display = 'none';
if (DOM.pauseButton) DOM.pauseButton.style.display = 'block';
if (DOM.newChatButton) DOM.newChatButton.disabled = true;
addMessage('', false, false, false, true);
addMessage('', "loading");

[DOM.confValue, DOM.expValue, DOM.missValue, DOM.ambValue].forEach((element) => {
if (element) element.innerHTML = '';
Expand Down Expand Up @@ -62,7 +62,7 @@ export async function sendMessage() {
} else {
console.error('Error:', error);
resetUIState();
addMessage('Sorry, there was an error processing your message: ' + (error.message || String(error)), false);
addMessage('Sorry, there was an error processing your message: ' + (error.message || String(error)));
}
state.currentRequestController = null;
}
Expand All @@ -80,9 +80,9 @@ async function processStreamingResponse(response: Response) {
if (buffer.trim()) {
try {
const step = JSON.parse(buffer);
addMessage(step.message || JSON.stringify(step), false);
addMessage(step.message || JSON.stringify(step));
} catch {
addMessage(buffer, false);
addMessage(buffer);
}
}
break;
Expand All @@ -102,7 +102,7 @@ async function processStreamingResponse(response: Response) {
const step = JSON.parse(message);
handleStreamMessage(step);
} catch {
addMessage('Failed: ' + message, false);
addMessage('Failed: ' + message);
}
}
}
Expand All @@ -115,7 +115,7 @@ function handleStreamMessage(step: any) {
}

if (step.type === 'reasoning_step') {
addMessage(step.message, false);
addMessage(step.message);
moveLoadingMessageToBottom();
} else if (step.type === 'final_result') {
handleFinalResult(step);
Expand All @@ -124,13 +124,13 @@ function handleStreamMessage(step: any) {
} else if (step.type === 'query_result') {
handleQueryResult(step);
} else if (step.type === 'ai_response') {
addMessage(step.message, false, false, true);
addMessage(step.message, "final-result");
} else if (step.type === 'destructive_confirmation') {
addDestructiveConfirmationMessage(step);
} else if (step.type === 'operation_cancelled') {
addMessage(step.message, false, true);
addMessage(step.message, "followup");
} else {
addMessage(step.message || JSON.stringify(step), false);
addMessage(step.message || JSON.stringify(step));
}

if (step.type !== 'reasoning_step') {
Expand Down Expand Up @@ -166,9 +166,9 @@ function handleFinalResult(step: any) {

const message = step.message || JSON.stringify(step.data, null, 2);
if (step.is_valid) {
addMessage(message, false, false, true);
addMessage(message, "final-result");
} else {
addMessage("Sorry, we couldn't generate a valid SQL query. Please try rephrasing your question or add more details. For help, check the explanation window.", false, true);
addMessage("Sorry, we couldn't generate a valid SQL query. Please try rephrasing your question or add more details. For help, check the explanation window.", "followup");
}
}

Expand All @@ -177,14 +177,15 @@ function handleFollowupQuestions(step: any) {
if (DOM.confValue) DOM.confValue.textContent = 'N/A';
if (DOM.missValue) DOM.missValue.textContent = 'N/A';
if (DOM.ambValue) DOM.ambValue.textContent = 'N/A';
addMessage(step.message, false, true);
addMessage(step.message, "followup");
}

function handleQueryResult(step: any) {
if (step.data) {
addMessage(`Query Result: ${JSON.stringify(step.data)}`, false, false, true);
console.log(step.data);
addMessage("Query Result", "query-final-result", null, step.data);
} else {
addMessage('No results found for the query.', false);
addMessage('No results found for the query.');
}
}
Comment thread
Anchel123 marked this conversation as resolved.
Resolved

Expand All @@ -202,7 +203,7 @@ export function pauseRequest() {
state.currentRequestController = null;

resetUIState();
addMessage('Request was paused by user.', false, true);
addMessage('Request was paused by user.', "followup");
}
}

Expand Down Expand Up @@ -255,10 +256,10 @@ export async function handleDestructiveConfirmation(confirmation: string, sqlQue
if (DOM.messageInput) DOM.messageInput.disabled = false;
if (DOM.submitButton) DOM.submitButton.disabled = false;

addMessage(`User choice: ${confirmation}`, true, false, false, false, (window as any).currentUser || null);
addMessage(`User choice: ${confirmation}`, "user", (window as any).currentUser || null);

if (confirmation === 'CANCEL') {
addMessage('Operation cancelled. The destructive SQL query was not executed.', false, true);
addMessage('Operation cancelled. The destructive SQL query was not executed.', "followup");
return;
}

Expand All @@ -282,7 +283,7 @@ export async function handleDestructiveConfirmation(confirmation: string, sqlQue
await processStreamingResponse(response);
} catch (error: any) {
console.error('Error:', error);
addMessage('Sorry, there was an error processing the confirmation: ' + (error.message || String(error)), false);
addMessage('Sorry, there was an error processing the confirmation: ' + (error.message || String(error)));
}
}

Expand Down
Loading