From ce81a437521a946d5bc1cac4ab7287e0e316e906 Mon Sep 17 00:00:00 2001 From: Chaibuilder Developer Date: Sun, 22 Feb 2026 16:01:51 +0530 Subject: [PATCH 01/13] refactor: remove custom drag preview from outline tree and simplify with null render --- .../panels/outline/default-drag-preview.tsx | 47 ------------------- .../sidepanels/panels/outline/list-tree.tsx | 3 +- 2 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 src/core/components/sidepanels/panels/outline/default-drag-preview.tsx diff --git a/src/core/components/sidepanels/panels/outline/default-drag-preview.tsx b/src/core/components/sidepanels/panels/outline/default-drag-preview.tsx deleted file mode 100644 index a686afcf4..000000000 --- a/src/core/components/sidepanels/panels/outline/default-drag-preview.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { TypeIcon } from "@/core/components/sidepanels/panels/outline/block-type-icon"; -import { useBlocksStore } from "@/hooks/history/use-blocks-store-undoable-actions"; -import { ChaiBlock } from "@/types/common"; -import { memo, useMemo } from "react"; -import { DragPreviewProps } from "react-arborist"; - -const Overlay = memo(function Overlay({ children, isDragging }: { children: React.ReactNode; isDragging: boolean }) { - if (!isDragging) return null; - - return
{children}
; -}); - -export const DefaultDragPreview = memo(({ id, isDragging, mouse }: Omit) => { - const [allBlocks] = useBlocksStore(); - - const block: ChaiBlock | undefined = useMemo(() => { - return allBlocks.find((block) => block._id === id); - }, [allBlocks, id]); - - const style = useMemo( - () => ({ - transform: `translate(${mouse?.x! - 10}px, ${mouse?.y! - 10}px)`, - }), - [mouse], - ); - - if (!mouse) { - return
; - } - - return ( -
- -
- -
-
-
- ); -}); diff --git a/src/core/components/sidepanels/panels/outline/list-tree.tsx b/src/core/components/sidepanels/panels/outline/list-tree.tsx index 160f4ccec..625d9da90 100644 --- a/src/core/components/sidepanels/panels/outline/list-tree.tsx +++ b/src/core/components/sidepanels/panels/outline/list-tree.tsx @@ -3,7 +3,6 @@ import { treeRefAtom } from "@/atoms/ui"; import { Button } from "@/components/ui/button"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { DefaultCursor } from "@/core/components/sidepanels/panels/outline/default-cursor"; -import { DefaultDragPreview } from "@/core/components/sidepanels/panels/outline/default-drag-preview"; import { close, defaultShortcuts, @@ -336,7 +335,7 @@ const ListTree = () => { childrenAccessor={(d: any) => d.children} width={"100%"} rowHeight={25} - renderDragPreview={DefaultDragPreview} + renderDragPreview={() => null} indent={10} onContextMenu={onContextMenu} disableDrop={debouncedDisableDrop as any} From 7b56f2ee9a9940e08bf6bc4bbd16a6d9eaa7f29a Mon Sep 17 00:00:00 2001 From: Chaibuilder Developer Date: Mon, 23 Feb 2026 08:41:43 +0530 Subject: [PATCH 02/13] refactor: replace SquareIcon with BoxIcon in outline block type icon --- .../components/sidepanels/panels/outline/block-type-icon.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/components/sidepanels/panels/outline/block-type-icon.tsx b/src/core/components/sidepanels/panels/outline/block-type-icon.tsx index b7821125a..6c5a637d5 100644 --- a/src/core/components/sidepanels/panels/outline/block-type-icon.tsx +++ b/src/core/components/sidepanels/panels/outline/block-type-icon.tsx @@ -1,5 +1,5 @@ import { useRegisteredChaiBlocks } from "@/runtime"; -import { SquareIcon } from "@radix-ui/react-icons"; +import { BoxIcon } from "@radix-ui/react-icons"; import { get } from "lodash-es"; import React from "react"; @@ -18,5 +18,5 @@ export const TypeIcon: React.FC = (props) => { } // * Fallback Icon - return ; + return ; }; From d51684f2506d44f15a760fcfa175edda37d67c6d Mon Sep 17 00:00:00 2001 From: Chaibuilder Developer Date: Mon, 23 Feb 2026 08:47:27 +0530 Subject: [PATCH 03/13] refactor: increase outline tree indent from 10 to 13 pixels --- .../sidepanels/panels/outline/list-tree.tsx | 2 +- .../sidepanels/panels/outline/node.tsx | 27 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/core/components/sidepanels/panels/outline/list-tree.tsx b/src/core/components/sidepanels/panels/outline/list-tree.tsx index 625d9da90..424a18cc7 100644 --- a/src/core/components/sidepanels/panels/outline/list-tree.tsx +++ b/src/core/components/sidepanels/panels/outline/list-tree.tsx @@ -336,7 +336,7 @@ const ListTree = () => { width={"100%"} rowHeight={25} renderDragPreview={() => null} - indent={10} + indent={13} onContextMenu={onContextMenu} disableDrop={debouncedDisableDrop as any} idAccessor={"_id"}> diff --git a/src/core/components/sidepanels/panels/outline/node.tsx b/src/core/components/sidepanels/panels/outline/node.tsx index 751582861..0e78d6087 100644 --- a/src/core/components/sidepanels/panels/outline/node.tsx +++ b/src/core/components/sidepanels/panels/outline/node.tsx @@ -176,6 +176,20 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) = } }; + const { librarySite } = useBuilderProp("flags", { librarySite: false }); + const isLibBlock = useMemo(() => { + return ( + librarySite && + has(data, "_libBlockId") && + !isEmpty(data._libBlockId) && + (hasPermission(PERMISSIONS.CREATE_LIBRARY_BLOCK) || hasPermission(PERMISSIONS.EDIT_LIBRARY_BLOCK)) + ); + }, [data, hasPermission, librarySite]); + + const isPartialBlock = useMemo(() => { + return data?._type === "PartialBlock" || data?._type === "GlobalBlock"; + }, [data]); + if (id === ROOT_TEMP_KEY) { return (
@@ -194,19 +208,6 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) =
); } - const { librarySite } = useBuilderProp("flags", { librarySite: false }); - const isLibBlock = useMemo(() => { - return ( - librarySite && - has(data, "_libBlockId") && - !isEmpty(data._libBlockId) && - (hasPermission(PERMISSIONS.CREATE_LIBRARY_BLOCK) || hasPermission(PERMISSIONS.EDIT_LIBRARY_BLOCK)) - ); - }, [data, hasPermission, librarySite]); - - const isPartialBlock = useMemo(() => { - return data?._type === "PartialBlock" || data?._type === "GlobalBlock"; - }, [data]); return (
From ce988605a3572dfd0fea686ee4b4b357e6a025fc Mon Sep 17 00:00:00 2001 From: Chaibuilder Developer Date: Mon, 23 Feb 2026 08:48:29 +0530 Subject: [PATCH 04/13] refactor: add eslint-disable comment for exhaustive-deps in outline node useEffect --- src/core/components/sidepanels/panels/outline/node.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/components/sidepanels/panels/outline/node.tsx b/src/core/components/sidepanels/panels/outline/node.tsx index 0e78d6087..b8fdd8ce7 100644 --- a/src/core/components/sidepanels/panels/outline/node.tsx +++ b/src/core/components/sidepanels/panels/outline/node.tsx @@ -144,6 +144,7 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) = }, 500); return () => clearTimeout(timedToggle); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [willReceiveDrop, node, isDragging]); const setDropAttribute = (id: string, value: string) => { From d6472fd4cc098197b8b651b40c8cfaf8d4612840 Mon Sep 17 00:00:00 2001 From: Chaibuilder Developer Date: Mon, 23 Feb 2026 09:51:15 +0530 Subject: [PATCH 05/13] refactor: increase outline tree indent from 13 to 14 pixels and add visual hierarchy lines --- .../sidepanels/panels/outline/list-tree.tsx | 2 +- .../components/sidepanels/panels/outline/node.tsx | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/core/components/sidepanels/panels/outline/list-tree.tsx b/src/core/components/sidepanels/panels/outline/list-tree.tsx index 424a18cc7..63f754f4a 100644 --- a/src/core/components/sidepanels/panels/outline/list-tree.tsx +++ b/src/core/components/sidepanels/panels/outline/list-tree.tsx @@ -336,7 +336,7 @@ const ListTree = () => { width={"100%"} rowHeight={25} renderDragPreview={() => null} - indent={13} + indent={14} onContextMenu={onContextMenu} disableDrop={debouncedDisableDrop as any} idAccessor={"_id"}> diff --git a/src/core/components/sidepanels/panels/outline/node.tsx b/src/core/components/sidepanels/panels/outline/node.tsx index b8fdd8ce7..93a273239 100644 --- a/src/core/components/sidepanels/panels/outline/node.tsx +++ b/src/core/components/sidepanels/panels/outline/node.tsx @@ -233,6 +233,19 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) = e.preventDefault(); setDropAttribute(id, "no"); }}> + {node.level > 0 && ( +
+ {Array.from({ length: node.level }).map((_, index) => ( +
+ ))} +
+ )} {hasPermission(PERMISSIONS.ADD_BLOCK) && !isDragAndDropEnabled && node?.rowIndex !== null && From 88bfcdaf272fbb07abb39c74ea9ae1af4ee0a84e Mon Sep 17 00:00:00 2001 From: Chaibuilder Developer Date: Mon, 23 Feb 2026 10:01:02 +0530 Subject: [PATCH 06/13] refactor: add parent group hover state to outline tree hierarchy lines --- src/core/components/sidepanels/panels/outline/list-tree.tsx | 6 ++++-- src/core/components/sidepanels/panels/outline/node.tsx | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/components/sidepanels/panels/outline/list-tree.tsx b/src/core/components/sidepanels/panels/outline/list-tree.tsx index 63f754f4a..f4e6c86ab 100644 --- a/src/core/components/sidepanels/panels/outline/list-tree.tsx +++ b/src/core/components/sidepanels/panels/outline/list-tree.tsx @@ -258,7 +258,9 @@ const ListTree = () => { return ( <> -
clearSelection()}> +
clearSelection()}>
{ ref={treeRef} height={window.innerHeight - 160} className="no-scrollbar !h-full max-w-full space-y-1 !overflow-y-auto !overflow-x-hidden" - rowClassName="flex items-center h-full border-b border-transparent" + rowClassName="flex items-center h-full border-b border-transparent tree-group:bg-black" selection={ids[0] || ""} onRename={onRename} openByDefault={false} diff --git a/src/core/components/sidepanels/panels/outline/node.tsx b/src/core/components/sidepanels/panels/outline/node.tsx index 93a273239..5a6c1946c 100644 --- a/src/core/components/sidepanels/panels/outline/node.tsx +++ b/src/core/components/sidepanels/panels/outline/node.tsx @@ -238,7 +238,7 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) = {Array.from({ length: node.level }).map((_, index) => (
Date: Mon, 23 Feb 2026 10:34:21 +0530 Subject: [PATCH 07/13] refactor: update UI libraries fetching to use `useWebsiteData` and adjust related constant names --- src/pages/chaibuilder-pages.tsx | 4 +++- src/pages/constants/ACTIONS.ts | 2 +- src/pages/hooks/project/use-ui-libraries.ts | 14 +++++++++----- src/pages/hooks/use-website-data.ts | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/pages/chaibuilder-pages.tsx b/src/pages/chaibuilder-pages.tsx index 6f44c2ee5..dc5b55fe7 100644 --- a/src/pages/chaibuilder-pages.tsx +++ b/src/pages/chaibuilder-pages.tsx @@ -33,6 +33,7 @@ import { useGetBlockAysncProps } from "./hooks/use-chai-collections"; import { useGotoPage } from "./hooks/use-goto-page"; import { useWebsiteData } from "./hooks/use-website-data"; import { useSiteWideUsage } from "./hooks/use-site-wide-usage"; +import { useUILibraries } from "./hooks/project/use-ui-libraries"; const PageLock = lazy(() => import("./client/components/page-lock/page-lock")); const NoLanguagePageDialog = lazy(() => import("@/pages/client/components/no-language-page/no-language-page-dialog")); @@ -99,7 +100,8 @@ type ChaiBuilderInnerProps = ChaiWebsiteBuilderProps; const ChaiBuilderInner = ({ ...props }: ChaiBuilderInnerProps) => { const { data: websiteData } = useWebsiteData(); const { data: siteWideUsage } = useSiteWideUsage(); - const { libraries: uiLibraries, collections, pageTypes, websiteSettings: websiteConfig } = websiteData; + const {data: uiLibraries} = useUILibraries(); + const { collections, pageTypes, websiteSettings: websiteConfig } = websiteData; const fallbackLang = useMemo(() => websiteConfig?.fallbackLang || "en", [websiteConfig]); const { data: accessData, isFetching: isFetchingAccessData } = useCheckUserAccess(); const roleAndPermissions = accessData || DEFAULT_ROLES_AND_PERMISSIONS; diff --git a/src/pages/constants/ACTIONS.ts b/src/pages/constants/ACTIONS.ts index beffa515f..312d71e34 100644 --- a/src/pages/constants/ACTIONS.ts +++ b/src/pages/constants/ACTIONS.ts @@ -55,7 +55,7 @@ export const ACTIONS = { DELETE_GLOBAL_SCHEMA: "DELETE_GLOBAL_SCHEMA", TOGGLE_PAGE_GLOBAL_SCHEMA: "TOGGLE_PAGE_GLOBAL_SCHEMA", APPLY_SCHEMA_TO_ALL_PAGES: "APPLY_SCHEMA_TO_ALL_PAGES", - UI_LIBRARIES: "uiLibraries", + UI_LIBRARIES: "UI_LIBRARIES", REMOVE_SCHEMA_FROM_ALL_PAGES: "REMOVE_SCHEMA_FROM_ALL_PAGES", // DAM Actions UPLOAD_ASSET: "UPLOAD_ASSET", diff --git a/src/pages/hooks/project/use-ui-libraries.ts b/src/pages/hooks/project/use-ui-libraries.ts index 7e7be59bf..23c072cd1 100644 --- a/src/pages/hooks/project/use-ui-libraries.ts +++ b/src/pages/hooks/project/use-ui-libraries.ts @@ -1,10 +1,12 @@ import { getBlocksFromHTML } from "@/core/main"; -import { useFetch } from "@/pages/hooks/utils/use-fetch"; import { registerChaiLibrary } from "@/runtime/client"; import { ChaiLibrary, ChaiLibraryBlock } from "@/types/chaibuilder-editor-props"; import { useQuery } from "@tanstack/react-query"; import { get, isArray } from "lodash-es"; +import { useWebsiteData } from "../use-website-data"; +import { useFetch } from "../utils/use-fetch"; import { useApiUrl } from "./use-builder-prop"; +import { ACTIONS } from "@/pages/constants/ACTIONS"; const uiLibrariesChaiApi = { async getUILibraryBlock(uiLibBlock: ChaiLibraryBlock, fetchAPI: any, apiUrl: string) { @@ -29,14 +31,16 @@ const uiLibrariesChaiApi = { }; export const useUILibraries = () => { - const apiUrl = useApiUrl(); const fetchAPI = useFetch(); + const apiUrl = useApiUrl(); + const { data } = useWebsiteData(); + const allLibraries = data?.libraries; + return useQuery({ - queryKey: ["uiLibraries"], + queryKey: [ACTIONS.UI_LIBRARIES], staleTime: "static", queryFn: async () => { - const response = await fetchAPI(apiUrl, { action: "GET_LIBRARIES" }); - const libraries = response.map((library: any) => ({ + const libraries = allLibraries.map((library: any) => ({ ...library, })); libraries.forEach((library: any) => { diff --git a/src/pages/hooks/use-website-data.ts b/src/pages/hooks/use-website-data.ts index 2bb3ae93a..b29546659 100644 --- a/src/pages/hooks/use-website-data.ts +++ b/src/pages/hooks/use-website-data.ts @@ -16,7 +16,7 @@ export const useWebsiteData = () => { queryClient.setQueryData([ACTIONS.GET_WEBSITE_DRAFT_SETTINGS], data.websiteSettings); queryClient.setQueryData([ACTIONS.GET_WEBSITE_PAGES], data.websitePages); queryClient.setQueryData([ACTIONS.GET_PAGE_TYPES], data.pageTypes); - queryClient.setQueryData([ACTIONS.UI_LIBRARIES], data.libraries); + queryClient.setQueryData([ACTIONS.GET_LIBRARIES], data.libraries); queryClient.setQueryData([ACTIONS.GET_COLLECTIONS], data.collections); return data; From 54109eab0eabddc894ec4113a5b90c99d69ad28a Mon Sep 17 00:00:00 2001 From: Chaibuilder Developer Date: Mon, 23 Feb 2026 16:51:00 +0530 Subject: [PATCH 08/13] refactor: improve outline tree visual hierarchy with better spacing --- .../sidepanels/panels/outline/list-tree.tsx | 8 +-- .../sidepanels/panels/outline/node.tsx | 68 +++++++++++++++---- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/core/components/sidepanels/panels/outline/list-tree.tsx b/src/core/components/sidepanels/panels/outline/list-tree.tsx index f4e6c86ab..9167b7790 100644 --- a/src/core/components/sidepanels/panels/outline/list-tree.tsx +++ b/src/core/components/sidepanels/panels/outline/list-tree.tsx @@ -303,10 +303,10 @@ const ListTree = () => { aria-selected={true} aria-expanded={false} onClick={() => setIds([])} - className="flex h-full items-center border-b border-transparent"> + className="flex min-h-[25px] items-center">
@@ -325,8 +325,8 @@ const ListTree = () => { ) = ); } + const isLastChild = node.parent && node.parent.children && node.childIndex === node.parent.children.length - 1; + return ( -
+
highlightBlock(id)} onMouseLeave={() => clearHighlight()} onClick={handleNodeClickWithoutPropagating} @@ -235,15 +238,44 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) = }}> {node.level > 0 && (
- {Array.from({ length: node.level }).map((_, index) => ( -
- ))} + {Array.from({ length: node.level }).map((_, index) => { + const isCurrentLevel = index === node.level - 1; + const shouldShowHalfHeight = isCurrentLevel && isLastChild; + + // Check if this ancestor level has more siblings after current node's branch + let ancestorNode: any = node; + for (let i = 0; i < node.level - index - 1; i++) { + ancestorNode = ancestorNode?.parent; + if (!ancestorNode) break; + } + + const hasMoreSiblings = + ancestorNode?.parent?.children && ancestorNode.childIndex < ancestorNode.parent.children.length - 1; + + // Don't show line if it's not current level and has no more siblings + if (!isCurrentLevel && !hasMoreSiblings) { + return null; + } + + return ( +
+ ); + })} +
)} {hasPermission(PERMISSIONS.ADD_BLOCK) && @@ -268,15 +300,21 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) =
)} +
+
+
Date: Mon, 23 Feb 2026 18:44:29 +0530 Subject: [PATCH 09/13] refactor: improve outline tree cursor positioning and add visual indicator dot --- .../sidepanels/panels/outline/default-cursor.tsx | 11 +++++++---- .../components/sidepanels/panels/outline/node.tsx | 5 ++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/core/components/sidepanels/panels/outline/default-cursor.tsx b/src/core/components/sidepanels/panels/outline/default-cursor.tsx index cfde91fd2..f023f4b62 100644 --- a/src/core/components/sidepanels/panels/outline/default-cursor.tsx +++ b/src/core/components/sidepanels/panels/outline/default-cursor.tsx @@ -7,17 +7,20 @@ const placeholderStyle = { zIndex: 1, }; -export const DefaultCursor = React.memo(function DefaultCursor({ top, left }: CursorProps) { +export const DefaultCursor = React.memo(function DefaultCursor({ top, left, indent }: CursorProps) { const style: CSSProperties = { position: "absolute", pointerEvents: "none", - top: top + "px", - left: left + "px", + top: top - 1 + "px", + left: "0px", right: 0, + paddingLeft: left + indent + "px", }; + return (
-
+
+
); }); diff --git a/src/core/components/sidepanels/panels/outline/node.tsx b/src/core/components/sidepanels/panels/outline/node.tsx index c6f3eca3b..1e4bdaca1 100644 --- a/src/core/components/sidepanels/panels/outline/node.tsx +++ b/src/core/components/sidepanels/panels/outline/node.tsx @@ -211,9 +211,8 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) = } const isLastChild = node.parent && node.parent.children && node.childIndex === node.parent.children.length - 1; - return ( -
+
highlightBlock(id)} @@ -329,7 +328,7 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) =
Date: Mon, 23 Feb 2026 22:00:45 +0530 Subject: [PATCH 10/13] refactor: simplify outline tree hierarchy lines and adjust horizontal connector styling --- .../sidepanels/panels/outline/node.tsx | 28 +++---------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/core/components/sidepanels/panels/outline/node.tsx b/src/core/components/sidepanels/panels/outline/node.tsx index 1e4bdaca1..b08d7dd89 100644 --- a/src/core/components/sidepanels/panels/outline/node.tsx +++ b/src/core/components/sidepanels/panels/outline/node.tsx @@ -210,7 +210,6 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) = ); } - const isLastChild = node.parent && node.parent.children && node.childIndex === node.parent.children.length - 1; return (
) = {node.level > 0 && (
{Array.from({ length: node.level }).map((_, index) => { - const isCurrentLevel = index === node.level - 1; - const shouldShowHalfHeight = isCurrentLevel && isLastChild; - - // Check if this ancestor level has more siblings after current node's branch - let ancestorNode: any = node; - for (let i = 0; i < node.level - index - 1; i++) { - ancestorNode = ancestorNode?.parent; - if (!ancestorNode) break; - } - - const hasMoreSiblings = - ancestorNode?.parent?.children && ancestorNode.childIndex < ancestorNode.parent.children.length - 1; - - // Don't show line if it's not current level and has no more siblings - if (!isCurrentLevel && !hasMoreSiblings) { - return null; - } - return (
) = ); })}
0 ? "w-1" : "w-3"} border-b border-black/0 transition-colors group-hover/parent:border-black/30`} style={{ left: `${(node.level - 1) * 14 + 10}px`, }} From 9cf294e647f68453865ababee1287e0c7eb70b8e Mon Sep 17 00:00:00 2001 From: Chaibuilder Developer Date: Mon, 23 Feb 2026 22:16:28 +0530 Subject: [PATCH 11/13] refactor: remove horizontal connector line from outline tree node hierarchy --- src/core/components/sidepanels/panels/outline/node.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/core/components/sidepanels/panels/outline/node.tsx b/src/core/components/sidepanels/panels/outline/node.tsx index b08d7dd89..9d20a440e 100644 --- a/src/core/components/sidepanels/panels/outline/node.tsx +++ b/src/core/components/sidepanels/panels/outline/node.tsx @@ -249,12 +249,6 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) = /> ); })} -
0 ? "w-1" : "w-3"} border-b border-black/0 transition-colors group-hover/parent:border-black/30`} - style={{ - left: `${(node.level - 1) * 14 + 10}px`, - }} - />
)} {hasPermission(PERMISSIONS.ADD_BLOCK) && From f12f6ca663c672e70cddd9f6e06adb82dbfc7203 Mon Sep 17 00:00:00 2001 From: Chaibuilder Developer Date: Mon, 23 Feb 2026 22:42:42 +0530 Subject: [PATCH 12/13] refactor: add isShown to outline node useEffect dependencies and remove eslint-disable comment --- src/core/components/sidepanels/panels/outline/node.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/components/sidepanels/panels/outline/node.tsx b/src/core/components/sidepanels/panels/outline/node.tsx index 9d20a440e..18c8651b7 100644 --- a/src/core/components/sidepanels/panels/outline/node.tsx +++ b/src/core/components/sidepanels/panels/outline/node.tsx @@ -144,8 +144,7 @@ export const Node = memo(({ node, style, dragHandle }: NodeRendererProps) = }, 500); return () => clearTimeout(timedToggle); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [willReceiveDrop, node, isDragging]); + }, [willReceiveDrop, node, isDragging, isShown]); const setDropAttribute = (id: string, value: string) => { const innerDoc = iframe.contentDocument || iframe.contentWindow?.document; From d00b13d40702543e8b1d27d93dd4ddbf7f8a2413 Mon Sep 17 00:00:00 2001 From: amitdhiman5086 Date: Tue, 24 Feb 2026 15:43:42 +0530 Subject: [PATCH 13/13] feat: page type search by removing the page type dropdown and implementing client-side filtering --- src/core/rjsf-widgets/link.tsx | 70 +++++++---------------- src/pages/chaibuilder-pages.tsx | 18 ++---- src/pages/hooks/project/use-page-types.ts | 46 ++++++++------- 3 files changed, 53 insertions(+), 81 deletions(-) diff --git a/src/core/rjsf-widgets/link.tsx b/src/core/rjsf-widgets/link.tsx index de070032b..6ec5b01b2 100644 --- a/src/core/rjsf-widgets/link.tsx +++ b/src/core/rjsf-widgets/link.tsx @@ -9,27 +9,16 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { DataBindingSelector } from "./data-binding-selector"; -const PageTypeField = ({ - href, - pageTypes, - onChange, -}: { - href: string; - pageTypes: any[]; - onChange: (href: string) => void; -}) => { +const PageTypeField = ({ href, onChange }: { href: string; onChange: (href: string) => void }) => { const { t } = useTranslation(); const searchPageTypeItems = useBuilderProp("searchPageTypeItems", (_: string, __: any) => []); const [loading, setLoading] = useState(false); const [isSearching, setIsSearching] = useState(false); - const [pageType, setPageType] = useState("page"); const [searchQuery, setSearchQuery] = useState(""); const [pageTypeItems, setPageTypeItems] = useState([]); const [selectedIndex, setSelectedIndex] = useState(-1); const listRef = useRef(null); - const currentPageTypeName = pageTypes?.find((_pageType) => _pageType.key === pageType)?.name; - useEffect(() => { setSearchQuery(""); setPageTypeItems([]); @@ -38,11 +27,9 @@ const PageTypeField = ({ if (!href || loading || !startsWith(href, "pageType:")) return; const initHref = split(href, ":"); - const _pageType = get(initHref, 1, "page") || "page"; - setPageType(_pageType); (async () => { - const initalValue = await searchPageTypeItems(_pageType, [get(initHref, 2, "page")]); + const initalValue = await searchPageTypeItems("", [get(initHref, 2, "page")]); if (initalValue && Array.isArray(initalValue)) { setSearchQuery(get(initalValue, [0, "name"], "")); } @@ -55,18 +42,18 @@ const PageTypeField = ({ if (isEmpty(query)) { setPageTypeItems([]); } else { - const pageTypeItemResponse = await searchPageTypeItems(pageType, query); + const pageTypeItemResponse = await searchPageTypeItems("", query); setPageTypeItems(pageTypeItemResponse); } setLoading(false); setSelectedIndex(-1); }, - [pageType], + [], 300, ); const handleSelect = (pageTypeItem: any) => { - const href = ["pageType", pageType, pageTypeItem.primaryPage ?? pageTypeItem.id]; + const href = ["pageType", pageTypeItem.pageType || "page", pageTypeItem.primaryPage ?? pageTypeItem.id]; if (!href[1]) return; onChange(href.join(":")); setSearchQuery(pageTypeItem.name); @@ -124,32 +111,23 @@ const PageTypeField = ({ return (
- - {pageType && ( -
- handleSearch(e.target.value)} - onKeyDown={handleKeyDown} - placeholder={t(`Search ${currentPageTypeName ?? ""}`)} - className="w-full rounded-md border border-gray-300 p-2 pr-16" - /> -
- {searchQuery && ( - - )} -
+
+ handleSearch(e.target.value)} + onKeyDown={handleKeyDown} + placeholder={t("Search pages")} + className="w-full rounded-md border border-gray-300 p-2 pr-16" + /> +
+ {searchQuery && ( + + )}
- )} +
{(loading || !isEmpty(pageTypeItems) || (isSearching && isEmpty(pageTypeItems))) && (
@@ -237,11 +215,7 @@ const LinkField = ({ schema, formData, onChange, name }: FieldProps) => { )} {linkType === "pageType" && !isEmpty(pageTypes) ? ( - onChange({ ...formData, href })} - /> + onChange({ ...formData, href })} /> ) : null} { const { data: websiteData } = useWebsiteData(); const { data: siteWideUsage } = useSiteWideUsage(); - const {data: uiLibraries} = useUILibraries(); - const { collections, pageTypes, websiteSettings: websiteConfig } = websiteData; + const { data: uiLibraries } = useUILibraries(); + const { collections, pageTypes, websiteSettings: websiteConfig } = websiteData; const fallbackLang = useMemo(() => websiteConfig?.fallbackLang || "en", [websiteConfig]); const { data: accessData, isFetching: isFetchingAccessData } = useCheckUserAccess(); const roleAndPermissions = accessData || DEFAULT_ROLES_AND_PERMISSIONS; @@ -118,7 +118,7 @@ const ChaiBuilderInner = ({ ...props }: ChaiBuilderInnerProps) => { const { onSave } = usePagesSavePage(); const { mutateAsync: getBlockAsyncProps } = useGetBlockAysncProps(); const { getPartialBlocks, getPartialBlockBlocks } = usePartialBlocksFn(); - const { mutateAsync: searchPageTypePages } = useSearchPageTypePages(); + const { searchPages } = useSearchPageTypePages(); const { mutateAsync: updateSettings } = useUpdateWebsiteFields(); const gotoPage = useGotoPage(); @@ -169,14 +169,6 @@ const ChaiBuilderInner = ({ ...props }: ChaiBuilderInnerProps) => { return uiLibraries?.some((library: any) => library.isSiteLibrary); }, [uiLibraries]); - // * SEARCH for page types - const searchPageTypeItems = useCallback( - async (pageType: string, query: string) => { - return await searchPageTypePages({ pageType, query }); - }, - [searchPageTypePages], - ); - return ( <> {isFetchingPageAllData && ( @@ -218,7 +210,7 @@ const ChaiBuilderInner = ({ ...props }: ChaiBuilderInnerProps) => { blocks={isFetchingPageAllData ? [] : blocks} theme={cloneDeep(currentTheme)} pageTypes={pageTypes} - searchPageTypeItems={searchPageTypeItems} + searchPageTypeItems={searchPages} askAiCallBack={askAiCallBack} onSave={async ({ blocks: _blocks, needTranslations, partialIds, linkPageIds, designTokens }) => { if (!page) return true; diff --git a/src/pages/hooks/project/use-page-types.ts b/src/pages/hooks/project/use-page-types.ts index 6588640ab..481207e98 100644 --- a/src/pages/hooks/project/use-page-types.ts +++ b/src/pages/hooks/project/use-page-types.ts @@ -1,9 +1,10 @@ import { ACTIONS } from "@/pages/constants/ACTIONS"; import { useFetch } from "@/pages/hooks/utils/use-fetch"; import { ChaiPageType } from "@/types/actions"; -import { useMutation, useQuery } from "@tanstack/react-query"; -import { useMemo } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { useCallback, useMemo } from "react"; import { useApiUrl } from "./use-builder-prop"; +import { useWebsitePages } from "@/pages"; export const usePageTypes = () => { const apiUrl = useApiUrl(); @@ -24,24 +25,29 @@ export const usePageType = (pageType: string) => { }; export const useSearchPageTypePages = () => { - const apiUrl = useApiUrl(); - const fetchAPI = useFetch(); - return useMutation({ - mutationFn: async ({ pageType, query }: any) => { - try { - return ( - fetchAPI(apiUrl, { - action: ACTIONS.SEARCH_PAGES, - data: { - pageType, - query: Array.isArray(query) && query.length > 0 ? query[0] : query, - }, - }) || [] - ); - } catch (_error) { - console.error(_error); - return []; + const { data: primaryPages } = useWebsitePages(); + const searchPages = useCallback( + async (_pageType: string, query: string | string[]) => { + if (!primaryPages || !query) return []; + + // When query is an array (ID lookup during initialization) + if (Array.isArray(query)) { + return primaryPages.filter((page: any) => query.includes(page.id)); } + + // Normal text search — exclude partials (no slug) and dynamic pages + const lowerQuery = query.toLowerCase(); + return primaryPages.filter( + (page: any) => + page.slug && + !page.dynamic && + (page.id === query || + (page.name || "").toLowerCase().includes(lowerQuery) || + (page.slug || "").toLowerCase().includes(lowerQuery)), + ); }, - }); + [primaryPages], + ); + + return { searchPages }; };