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
56 changes: 24 additions & 32 deletions frontend/src/ide/chat-ai/Body.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useState, useCallback, useEffect, useRef } from "react";
import { useState, useCallback, useEffect } from "react";
import PropTypes from "prop-types";

import { useSocketService } from "../../service/socket-service";
import { ExistingChat } from "./ExistingChat";
import { NewChat } from "./NewChat";
import { useChatAIService } from "./services";
import { useProjectStore } from "../../store/project-store";
import { useExplorerStore } from "../../store/explorer-store";
import { useNotificationService } from "../../service/notification-service";
import { explorerService } from "../explorer/explorer-service";

// Cloud-only: fetch token balance (unavailable in OSS — import fails gracefully)
let getTokenBalance = null;
Expand Down Expand Up @@ -71,8 +71,9 @@ const Body = function Body({
seedsData: {},
dbData: {},
});
const explorerSvc = useRef(explorerService()).current;
const { projectId } = useProjectStore();
const explorerData = useExplorerStore((state) => state.explorerData);
const dbExplorerData = useExplorerStore((state) => state.dbExplorerData);

const { postChatPrompt, getChatIntents, getChatLlmModels } =
useChatAIService();
Expand Down Expand Up @@ -202,37 +203,28 @@ const Body = function Body({
});
}, [selectedChatId, chatMessages.length, realTokenBalance, notify]);

// Autocomplete data is mirrored passively from useExplorerStore, which is
// populated exclusively by explorer-component.jsx. This component deliberately
// does NOT fetch on its own — the IDE layout mounts the explorer before the
// chat drawer opens, so the store is populated by the time autocomplete is
// triggered. If that invariant ever breaks (lazy-loaded explorer, standalone
// chat route, explorer fetch failure), add a fetch fallback here or a
// loading/unavailable state in the InputPrompt autocomplete UI.
useEffect(() => {
if (!projectId || !isChatDrawerOpen) return;

// fetch database schemas -> update immediately when ready
explorerSvc
.getDbExplorer(projectId)
.then((res) => {
setPromptAutoComplete((prev) => ({
...prev,
dbData: res?.data || {},
}));
})
.catch(() => {
console.error("Failed to fetch database schemas");
});
const children = explorerData || [];
setPromptAutoComplete((prev) => ({
...prev,
modelsData: children[0] || {},
seedsData: children[1] || {},
}));
}, [explorerData]);

// fetch models & seeds -> update as soon as ready
explorerSvc
.getExplorer(projectId)
.then((res) => {
const children = res?.data?.children || [];
setPromptAutoComplete((prev) => ({
...prev,
modelsData: children[0] || {},
seedsData: children[1] || {},
}));
})
.catch(() => {
console.error("Failed to fetch models and seeds");
});
}, [projectId, isChatDrawerOpen, explorerSvc]);
useEffect(() => {
setPromptAutoComplete((prev) => ({
...prev,
dbData: dbExplorerData || {},
}));
}, [dbExplorerData]);
Comment thread
greptile-apps[bot] marked this conversation as resolved.

const triggerGetChatMessagesApi = useCallback(() => {
setIsGetChatMessages(true);
Expand Down
40 changes: 34 additions & 6 deletions frontend/src/ide/explorer/explorer-component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import "../ide-layout.css";
import { useNotificationService } from "../../service/notification-service.js";
import { SpinnerLoader } from "../../widgets/spinner_loader/index.js";
import { useRefreshModelsStore } from "../../store/refresh-models-store.js";
import { useExplorerStore } from "../../store/explorer-store.js";
import { LinearScale } from "../../base/icons";

// Static sort options for model explorer
Expand Down Expand Up @@ -176,6 +177,13 @@ const IdeExplorer = ({
const currentSchema = useProjectStore((state) => state.currentSchema);
const setCurrentSchema = useProjectStore((state) => state.setCurrentSchema);
const setSchemaList = useProjectStore((state) => state.setSchemaList);
const setExplorerData = useExplorerStore((state) => state.setExplorerData);
const setDbExplorerData = useExplorerStore(
(state) => state.setDbExplorerData
);
const clearExplorerData = useExplorerStore(
(state) => state.clearExplorerData
);

// Reset currentSchema on unmount to prevent stale data
useEffect(() => {
Expand All @@ -194,7 +202,7 @@ const IdeExplorer = ({
const [openNameModal, setOpenNameModal] = useState(false);
const [newSchemaName, setNewSchemaName] = useState("");
const [isSchemaModalOpen, setIsSchemaModalOpen] = useState(false);
const [schemaMenu, setSchemaMenu] = useState([]);
const [schemaMenu, setSchemaMenu] = useState(null);
const [dbExplorer, setDBExplorer] = useState([]);
const [activeMenu, setActiveMenu] = useState("");
const [dbLoading, setDbLoading] = useState(false);
Expand Down Expand Up @@ -976,7 +984,7 @@ const IdeExplorer = ({
<>
<Dropdown
menu={{
items: schemaMenu.map((el) => ({
items: (schemaMenu || []).map((el) => ({
...el,
label:
el.key === "add-new-schema" ? (
Expand Down Expand Up @@ -1014,7 +1022,7 @@ const IdeExplorer = ({
? "Run Seed Disabled - Please select a schema"
: previewTimeTravel
? "Run Seed Disabled - Time travel mode active"
: schemaMenu.length <= 1
: (schemaMenu || []).length <= 1
? "Run Seed Disabled - No schemas available"
: "Run Seed"
}
Expand All @@ -1035,7 +1043,7 @@ const IdeExplorer = ({
disabled={
previewTimeTravel ||
!currentSchema ||
schemaMenu.length <= 1
(schemaMenu || []).length <= 1
}
>
<PlayCircleOutlined />
Expand Down Expand Up @@ -1118,7 +1126,7 @@ const IdeExplorer = ({
if (
!previewTimeTravel &&
currentSchema &&
schemaMenu.length > 1
(schemaMenu || []).length > 1
) {
handleSeedIconClick(event, child.title);
}
Expand All @@ -1127,7 +1135,7 @@ const IdeExplorer = ({
previewTimeTravel ||
seedRunningRef.current ||
!currentSchema ||
schemaMenu.length <= 1
(schemaMenu || []).length <= 1
? "seed-icon-disabled"
: ""
}`}
Expand Down Expand Up @@ -1244,12 +1252,28 @@ const IdeExplorer = ({
[modelSortBy, handleModelSort]
);

// schemaMenu starts as null; becomes an array (possibly empty) after
Comment thread
tahierhussain marked this conversation as resolved.
// getSchemas resolves. Gating on truthiness skips the redundant mount-time
// fetch while still firing for projects whose schema list is legitimately
// empty.
useEffect(() => {
if (schemaMenu) {
getExplorer(projectId);
}
Comment thread
tahierhussain marked this conversation as resolved.
}, [schemaMenu, currentSchema]);

// Clear shared explorer data on project switch so other consumers
// (e.g. chat autocomplete) don't momentarily read the previous project's tree.
// Ref-gated so the clear does NOT fire on initial mount / remount within the
// same project — only when projectId actually changes.
const prevProjectIdRef = useRef(projectId);
useEffect(() => {
if (prevProjectIdRef.current !== projectId) {
clearExplorerData();
prevProjectIdRef.current = projectId;
}
}, [projectId, clearExplorerData]);

function getExplorer(projectId) {
if (!projectId) return;
setLoading(true);
Expand All @@ -1258,6 +1282,9 @@ const IdeExplorer = ({
.then((res) => {
const treeData = res.data.children;
rawTreeDataRef.current = JSON.parse(JSON.stringify(treeData));
// Publish the raw (pre-mutation) shape to the shared store so
// consumers like chat-ai/Body.jsx get the untransformed children.
setExplorerData(rawTreeDataRef.current);
// Apply sort and decorations to no_code models BEFORE transformTree
// so that _isChild flag is set when className is assigned
treeData.forEach((node) => {
Expand Down Expand Up @@ -1299,6 +1326,7 @@ const IdeExplorer = ({
const treeData = res.data;
const mappedData = mapIconsToTreeData([treeData]);
setDBExplorer(mappedData);
setDbExplorerData(treeData);
setCachedLists((prev) => ({
...prev,
2: generateList([treeData]), // Correct key
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/store/explorer-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { create } from "zustand";

/**
* Explorer Store
* Holds the shared responses of explorerSvc.getExplorer(projectId) and
* explorerSvc.getDbExplorer(projectId) so multiple consumers (explorer
* tree, chat autocomplete) don't refetch.
* Owner of writes: frontend/src/ide/explorer/explorer-component.jsx
*/
const useExplorerStore = create((set) => ({
// res.data.children from /explorer API — array where [0]=models, [1]=seeds
explorerData: null,
// res.data from /db_explorer API — single DB tree object
dbExplorerData: null,
setExplorerData: (data) => set({ explorerData: data }),
setDbExplorerData: (data) => set({ dbExplorerData: data }),
clearExplorerData: () => set({ explorerData: null, dbExplorerData: null }),
}));

export { useExplorerStore };
Loading