diff --git a/src/components/shared/FavoriteComponentToggle.tsx b/src/components/shared/FavoriteComponentToggle.tsx
index 2867f5fc9..d072a28ac 100644
--- a/src/components/shared/FavoriteComponentToggle.tsx
+++ b/src/components/shared/FavoriteComponentToggle.tsx
@@ -14,7 +14,6 @@ import { Spinner } from "@/components/ui/spinner";
import { useGuaranteedHydrateComponentReference } from "@/hooks/useHydrateComponentReference";
import { cn } from "@/lib/utils";
import { useComponentLibrary } from "@/providers/ComponentLibraryProvider";
-import { flattenFolders } from "@/providers/ComponentLibraryProvider/componentLibrary";
import { hydrateComponentReference } from "@/services/componentService";
import { type ComponentReference } from "@/utils/componentSpec";
import { MINUTES } from "@/utils/constants";
@@ -143,18 +142,14 @@ const FavoriteToggleButton = withSuspenseWrapper(
);
const useComponentFlags = (component: ComponentReference) => {
- const { checkIfUserComponent, componentLibrary } = useComponentLibrary();
+ const { checkIfUserComponent, getComponentLibrary } = useComponentLibrary();
+ const componentLibrary = getComponentLibrary("standard_components");
const isUserComponent = useMemo(
() => checkIfUserComponent(component),
[component, checkIfUserComponent],
);
- const flatComponentList = useMemo(
- () => (componentLibrary ? flattenFolders(componentLibrary) : []),
- [componentLibrary],
- );
-
const { data: isInLibrary } = useSuspenseQuery({
queryKey: ["component", "flags", component.digest],
queryFn: async () => {
@@ -162,24 +157,7 @@ const useComponentFlags = (component: ComponentReference) => {
if (isUserComponent) return true;
- for (const c of flatComponentList) {
- if (component.name === "Chicago Taxi Trips dataset") {
- console.log(c.name, c.digest, component.digest);
- }
-
- if (c.name && c.name !== component.name) {
- // micro optimization to skip components with different names
- continue;
- }
-
- const digest = c.digest ?? (await hydrateComponentReference(c))?.digest;
-
- if (digest === component.digest) {
- return true;
- }
- }
-
- return false;
+ return componentLibrary.hasComponent(component);
},
staleTime: 10 * MINUTES,
});
diff --git a/src/components/shared/ReactFlow/FlowSidebar/components/LibraryStates.tsx b/src/components/shared/ReactFlow/FlowSidebar/components/LibraryStates.tsx
index dfd57e9e6..1bce52517 100644
--- a/src/components/shared/ReactFlow/FlowSidebar/components/LibraryStates.tsx
+++ b/src/components/shared/ReactFlow/FlowSidebar/components/LibraryStates.tsx
@@ -12,7 +12,3 @@ export const LoadingState = () => (
export const ErrorState = ({ message }: { message: string }) => (
Error: {message}
);
-
-export const EmptyState = () => (
- No components found
-);
diff --git a/src/components/shared/ReactFlow/FlowSidebar/components/index.ts b/src/components/shared/ReactFlow/FlowSidebar/components/index.ts
index 8879d72e8..c789d0e4a 100644
--- a/src/components/shared/ReactFlow/FlowSidebar/components/index.ts
+++ b/src/components/shared/ReactFlow/FlowSidebar/components/index.ts
@@ -1,5 +1,5 @@
export { default as FolderItem } from "./FolderItem";
export { default as ImportComponent } from "./ImportComponent";
-export { EmptyState, ErrorState, LoadingState } from "./LibraryStates";
+export { ErrorState, LoadingState } from "./LibraryStates";
export { default as SearchInput } from "./SearchInput";
export { default as SearchResults } from "./SearchResults";
diff --git a/src/components/shared/ReactFlow/FlowSidebar/sections/GraphComponents.tsx b/src/components/shared/ReactFlow/FlowSidebar/sections/GraphComponents.tsx
index 12a70727c..75b3d2671 100644
--- a/src/components/shared/ReactFlow/FlowSidebar/sections/GraphComponents.tsx
+++ b/src/components/shared/ReactFlow/FlowSidebar/sections/GraphComponents.tsx
@@ -22,7 +22,6 @@ import { useForcedSearchContext } from "@/providers/ComponentLibraryProvider/For
import type { UIComponentFolder } from "@/types/componentLibrary";
import {
- EmptyState,
ErrorState,
FolderItem,
ImportComponent,
@@ -131,7 +130,6 @@ function ComponentLibrarySection() {
const { updateSearchFilter } = useForcedSearchContext();
const {
- componentLibrary,
usedComponentsFolder,
userComponentsFolder,
isLoading,
@@ -139,6 +137,8 @@ function ComponentLibrarySection() {
searchResult,
} = useComponentLibrary();
+ const standardComponentsLibrary = getComponentLibrary("standard_components");
+
const handleFiltersChange = (filters: string[]) => {
updateSearchFilter({
filters,
@@ -147,7 +147,6 @@ function ComponentLibrarySection() {
if (isLoading) return ;
if (error) return ;
- if (!componentLibrary) return ;
if (!remoteComponentLibrarySearchEnabled && searchResult) {
// If there's a search result, use the SearchResults component
@@ -210,15 +209,9 @@ function ComponentLibrarySection() {
icon="Cable"
/>
-
{githubComponentLibraryEnabled && (
diff --git a/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.test.tsx b/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.test.tsx
index ec0342159..10271fe91 100644
--- a/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.test.tsx
+++ b/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.test.tsx
@@ -41,15 +41,12 @@ vi.mock("./componentLibrary");
// Import mocked modules
import * as componentLibraryUtils from "@/providers/ComponentLibraryProvider/componentLibrary";
-import * as componentService from "@/services/componentService";
import * as componentStore from "@/utils/componentStore";
import * as getComponentName from "@/utils/getComponentName";
import * as localforage from "@/utils/localforage";
// Mock implementations
-const mockFetchAndStoreComponentLibrary = vi.mocked(
- componentService.fetchAndStoreComponentLibrary,
-);
+
const mockFetchUserComponents = vi.mocked(
componentLibraryUtils.fetchUserComponents,
);
@@ -134,7 +131,6 @@ describe("ComponentLibraryProvider - Component Management", () => {
componentDuplicateDialogProps.handleImportComponent = undefined;
// Setup default mock implementations
- mockFetchAndStoreComponentLibrary.mockResolvedValue(mockComponentLibrary);
mockFetchUserComponents.mockResolvedValue(mockUserComponentsFolder);
mockFetchUsedComponents.mockReturnValue({
name: "Used Components",
diff --git a/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.tsx b/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.tsx
index 7f60876a1..da76399e9 100644
--- a/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.tsx
+++ b/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.tsx
@@ -15,14 +15,10 @@ import {
isYamlLibraryConfiguration,
} from "@/components/shared/GitHubLibrary/types";
import {
- fetchAndStoreComponentLibrary,
+ COMPONENT_LIBRARY_URL,
hydrateComponentReference,
} from "@/services/componentService";
-import type {
- ComponentFolder,
- ComponentLibrary,
- SearchResult,
-} from "@/types/componentLibrary";
+import type { ComponentFolder, SearchResult } from "@/types/componentLibrary";
import type {
ComponentReference,
HydratedComponentReference,
@@ -69,7 +65,6 @@ type AvailableComponentLibraries =
| string;
type ComponentLibraryContextType = {
- componentLibrary: ComponentLibrary | undefined;
userComponentsFolder: ComponentFolder | undefined;
usedComponentsFolder: ComponentFolder;
isLoading: boolean;
@@ -136,6 +131,10 @@ function useComponentLibraryRegistry() {
/**
* In future we will have other library types, including "standard_library", "favorite_components", "used_components", etc.
*/
+ [
+ "standard_components",
+ new YamlFileLibrary("Standard library", COMPONENT_LIBRARY_URL),
+ ],
]),
[queryClient],
);
@@ -187,7 +186,6 @@ export const ComponentLibraryProvider = ({
const { getComponentLibraryObject, existingComponentLibraries } =
useComponentLibraryRegistry();
- const [componentLibrary, setComponentLibrary] = useState();
const [userComponentsFolder, setUserComponentsFolder] =
useState();
@@ -196,17 +194,6 @@ export const ComponentLibraryProvider = ({
const [newComponent, setNewComponent] =
useState(null);
- // Fetch main component library
- const {
- data: rawComponentLibrary,
- isLoading: isLibraryLoading,
- error: libraryError,
- refetch: refetchLibrary,
- } = useQuery({
- queryKey: ["componentLibrary"],
- queryFn: fetchAndStoreComponentLibrary,
- });
-
// Fetch user components
const {
data: rawUserComponentsFolder,
@@ -227,14 +214,6 @@ export const ComponentLibraryProvider = ({
);
// Methods
- const refreshComponentLibrary = useCallback(async () => {
- const { data: updatedLibrary } = await refetchLibrary();
-
- if (updatedLibrary) {
- setComponentLibrary(updatedLibrary);
- }
- }, [refetchLibrary]);
-
const refreshUserComponents = useCallback(async () => {
const { data: updatedUserComponents } = await refetchUserComponents();
@@ -273,15 +252,7 @@ export const ComponentLibraryProvider = ({
},
};
- if (componentLibrary) {
- const uniqueComponents = filterToUniqueByDigest(
- flattenFolders(componentLibrary),
- );
-
- result.components.standard = uniqueComponents.filter(
- (c) => c.spec && componentMatchesSearch(c.spec, search, filters),
- );
- }
+ // classic search is not supported for now
if (userComponentsFolder) {
const uniqueComponents = filterToUniqueByDigest(
@@ -303,13 +274,13 @@ export const ComponentLibraryProvider = ({
return result;
},
- [componentLibrary, userComponentsFolder, usedComponentsFolder],
+ [userComponentsFolder, usedComponentsFolder],
);
const internalAddComponentToLibrary = useCallback(
async (hydratedComponent: HydratedComponentReference) => {
await importComponent(hydratedComponent);
- await refreshComponentLibrary();
+
await refreshUserComponents();
setNewComponent(null);
setExistingComponent(null);
@@ -322,7 +293,7 @@ export const ComponentLibraryProvider = ({
}),
);
},
- [refreshComponentLibrary, refreshUserComponents, importComponent],
+ [refreshUserComponents, importComponent],
);
const handleImportComponent = useCallback(
@@ -341,12 +312,7 @@ export const ComponentLibraryProvider = ({
console.error("Error importing component:", error);
}
},
- [
- newComponent,
- refreshComponentLibrary,
- refreshUserComponents,
- importComponent,
- ],
+ [newComponent, refreshUserComponents, importComponent],
);
const addToComponentLibraryWithDuplicateCheck = useCallback(
@@ -376,12 +342,7 @@ export const ComponentLibraryProvider = ({
console.error("Error adding component to library:", error);
}
},
- [
- userComponentsFolder,
- refreshComponentLibrary,
- refreshUserComponents,
- importComponent,
- ],
+ [userComponentsFolder, refreshUserComponents, importComponent],
);
const addToComponentLibrary = useCallback(
@@ -420,7 +381,6 @@ export const ComponentLibraryProvider = ({
USER_COMPONENTS_LIST_NAME,
component.name,
).then(async () => {
- await refreshComponentLibrary();
await refreshUserComponents();
});
} else {
@@ -432,7 +392,7 @@ export const ComponentLibraryProvider = ({
console.error("Error deleting component:", error);
}
},
- [refreshComponentLibrary, refreshUserComponents],
+ [refreshUserComponents],
);
const handleCloseDuplicationDialog = useCallback(() => {
@@ -451,14 +411,6 @@ export const ComponentLibraryProvider = ({
[currentSearchFilter, searchComponentLibrary],
);
- useEffect(() => {
- if (!rawComponentLibrary) {
- setComponentLibrary(undefined);
- return;
- }
- setComponentLibrary(rawComponentLibrary);
- }, [rawComponentLibrary]);
-
useEffect(() => {
if (!rawUserComponentsFolder) {
setUserComponentsFolder(undefined);
@@ -476,12 +428,11 @@ export const ComponentLibraryProvider = ({
[],
);
- const isLoading = isLibraryLoading || isUserComponentsLoading;
- const error = libraryError || userComponentsError;
+ const isLoading = isUserComponentsLoading;
+ const error = userComponentsError;
const value = useMemo(
() => ({
- componentLibrary,
userComponentsFolder,
usedComponentsFolder,
isLoading,
@@ -495,7 +446,6 @@ export const ComponentLibraryProvider = ({
checkIfUserComponent,
}),
[
- componentLibrary,
userComponentsFolder,
usedComponentsFolder,
isLoading,
diff --git a/src/services/componentService.test.ts b/src/services/componentService.test.ts
index 4815948a7..57ae0f60c 100644
--- a/src/services/componentService.test.ts
+++ b/src/services/componentService.test.ts
@@ -1,7 +1,6 @@
import yaml from "js-yaml";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
-import type { ComponentLibrary } from "@/types/componentLibrary";
import type { ComponentReference, ComponentSpec } from "@/utils/componentSpec";
import { generateDigest } from "@/utils/componentStore";
import * as localforage from "@/utils/localforage";
@@ -9,7 +8,6 @@ import * as localforage from "@/utils/localforage";
import {
fetchAndStoreComponent,
fetchAndStoreComponentByUrl,
- fetchAndStoreComponentLibrary,
getExistingAndNewUserComponent,
inputsWithInvalidArguments,
parseComponentData,
@@ -489,68 +487,4 @@ describe("componentService", () => {
expect(result).toEqual(["required-missing"]);
});
});
-
- describe("fetchAndStoreComponentLibrary", () => {
- const mockLibrary: ComponentLibrary = {
- folders: [
- {
- name: "test-folder",
- components: [
- { name: "component1", url: "https://example.com/comp1.yaml" },
- ],
- folders: [],
- },
- ],
- };
-
- it("should fetch and store component library successfully", async () => {
- const { loadObjectFromYamlData } = await import("@/utils/cache");
- const componentYaml = yaml.dump({
- name: "component1",
- implementation: { container: { image: "test" } },
- });
-
- // First mock for fetching the library
- mockFetch.mockResolvedValueOnce({
- ok: true,
- headers: new Headers(),
- arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)),
- } as Response);
-
- // Mock for fetching individual component from library
- mockFetch.mockResolvedValueOnce({
- ok: true,
- headers: new Headers(),
- text: () => Promise.resolve(componentYaml),
- } as Response);
-
- vi.mocked(loadObjectFromYamlData).mockReturnValue(mockLibrary);
- vi.mocked(localforage.getComponentByUrl).mockResolvedValue(null);
-
- const result = await fetchAndStoreComponentLibrary();
-
- expect(result).toEqual(mockLibrary);
- expect(localforage.saveComponent).toHaveBeenCalledWith({
- id: expect.stringMatching(/^library-\d+$/),
- url: "/component-library.yaml",
- data: JSON.stringify(mockLibrary),
- createdAt: expect.any(Number),
- updatedAt: expect.any(Number),
- });
- });
-
- it("should handle fetch errors and fallback to local storage", async () => {
- mockFetch.mockResolvedValue({
- ok: false,
- headers: new Headers(),
- statusText: "Not Found",
- } as Response);
-
- vi.mocked(localforage.componentExistsByUrl).mockResolvedValue(false);
-
- await expect(fetchAndStoreComponentLibrary()).rejects.toThrow(
- "Failed to load component library: Not Found",
- );
- });
- });
});
diff --git a/src/services/componentService.ts b/src/services/componentService.ts
index 4604c4faa..95163e858 100644
--- a/src/services/componentService.ts
+++ b/src/services/componentService.ts
@@ -1,9 +1,4 @@
import { getAppSettings } from "@/appSettings";
-import {
- type ComponentLibrary,
- isValidComponentLibrary,
-} from "@/types/componentLibrary";
-import { loadObjectFromYamlData } from "@/utils/cache";
import {
type ComponentReference,
type ComponentSpec,
@@ -42,77 +37,7 @@ interface ExistingAndNewComponent {
newComponent: HydratedComponentReference | undefined;
}
-const COMPONENT_LIBRARY_URL = getAppSettings().componentLibraryUrl;
-
-/**
- * Fetches the component library from local storage
- */
-const loadComponentLibraryFromLocalStorage =
- async (): Promise => {
- const libraryExists = await componentExistsByUrl(COMPONENT_LIBRARY_URL);
-
- if (libraryExists) {
- const storedLibrary = await getComponentByUrl(COMPONENT_LIBRARY_URL);
- if (storedLibrary) {
- try {
- const parsedLibrary = JSON.parse(storedLibrary.data);
- if (isValidComponentLibrary(parsedLibrary)) {
- return parsedLibrary;
- }
- } catch (error) {
- console.error("Error parsing stored component library:", error);
- }
- }
- }
-
- return null;
- };
-
-/**
- * Fetches the component library and stores all components in local storage
- */
-export const fetchAndStoreComponentLibrary =
- async (): Promise => {
- // Try fetch from the URL
- const response = await fetch(COMPONENT_LIBRARY_URL);
- if (!response.ok) {
- // Fallback to local storage
- await loadComponentLibraryFromLocalStorage().then((library) => {
- if (library) {
- return library;
- }
- });
-
- throw new Error(
- `Failed to load component library: ${response.statusText}`,
- );
- }
-
- const arrayBuffer = await response.arrayBuffer();
- const obj = loadObjectFromYamlData(arrayBuffer);
-
- if (!isValidComponentLibrary(obj)) {
- // Fallback to local storage
- await loadComponentLibraryFromLocalStorage().then((library) => {
- if (library) {
- return library;
- }
- });
-
- throw new Error("Invalid component library structure");
- }
-
- // Store the fetched library in local storage
- await saveComponent({
- id: `library-${Date.now()}`,
- url: COMPONENT_LIBRARY_URL,
- data: JSON.stringify(obj),
- createdAt: Date.now(),
- updatedAt: Date.now(),
- });
-
- return obj;
- };
+export const COMPONENT_LIBRARY_URL = getAppSettings().componentLibraryUrl;
/**
* Fetch and store a single component by URL