Repository: PostHog/posthog
Pull request #31861: fix(project-tree): fixes and nits #1
Author: login: adamleithp
Created: 2025-05-02T10:32:37Z | Merged: 2025-05-02T12:18:54Z

## Problem
* People thought the checkbox on initial hover was confusing as they thought this would toggle the accordion
* Lemon Tree had a 2 props which were doing the same thing (folderSelectMode/Multi select mode)
* Add toggle for enabling `select mode === multi` and then returning to `default` state in panel actions
* Update the tree logic to automatically enter `multi` mode, so that when you use the menu to `select`/`deselect` it changes the table mode accordingly
* Update the menu items separators as some states of the menu were showing floating lines
* Lemon tree wasn't doing `gap-y-px` pixel gap (was lost in DOM changes before)
* Lemon tree SelectedId styling was lost, this re-adds it (albeit still not final will follow up in another PR which touches more on `selectMode==='folder-only'`
* `<FolderSelect>` now uses `folder-only`, which 'disables' non-folders and doesn't show checkboxes

![2025-05-02 11 12 56](https://github.com/user-attachments/assets/ae9b8029-2563-4f48-9656-fc2f943c857b)

## Changes
* remove checkbox from showing on hover initially (now in toggle button/ select from menu)
* [UX decision] we don't exit `multi-select` mode when 0 items are selected
* merged `LemonTree` props
* moved separators around in project tree menu
* cleaned up `LemonTree` classes, fixing `gap-y-px` spacing
* SelectedId in `LemonTree` now shows 'correctly' as it was before (this affected the new breadcumbs clicking and assureVisibility)
* Added `LemonTree` checkbox animation/transition so it didn't pop into existance
* Bump `posthog/icons` repo to prepare for `FolderMove` button in `TopBar` (next PR)

## How did you test this code?
Extensively clicking/testing all things related to this



Diff:
diff --git a/frontend/__snapshots__/lemon-ui-lemon-tree--default--dark.png b/frontend/__snapshots__/lemon-ui-lemon-tree--default--dark.png
index c262f96ec86..2e6b8c23c33 100644
Binary files a/frontend/__snapshots__/lemon-ui-lemon-tree--default--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-tree--default--dark.png differ
diff --git a/frontend/__snapshots__/lemon-ui-lemon-tree--default--light.png b/frontend/__snapshots__/lemon-ui-lemon-tree--default--light.png
index 0f9287923b3..69f847f6d0b 100644
Binary files a/frontend/__snapshots__/lemon-ui-lemon-tree--default--light.png and b/frontend/__snapshots__/lemon-ui-lemon-tree--default--light.png differ
diff --git a/frontend/src/layout/panel-layout/ProjectTree/ProjectTree.tsx b/frontend/src/layout/panel-layout/ProjectTree/ProjectTree.tsx
index 5716bbf31da..f5b0833ce48 100644
--- a/frontend/src/layout/panel-layout/ProjectTree/ProjectTree.tsx
+++ b/frontend/src/layout/panel-layout/ProjectTree/ProjectTree.tsx
@@ -1,4 +1,4 @@
-import { IconChevronRight, IconFolder, IconFolderPlus } from '@posthog/icons'
+import { IconCheckbox, IconChevronRight, IconFolder, IconFolderPlus, IconX } from '@posthog/icons'
 import { useActions, useValues } from 'kea'
 import { MoveFilesModal } from 'lib/components/FileSystem/MoveFilesModal'
 import { FlaggedFeature } from 'lib/components/FlaggedFeature'
@@ -54,6 +54,7 @@ export function ProjectTree(): JSX.Element {
         movingItems,
         treeTableColumnSizes,
         treeTableTotalWidth,
+        selectMode,
     } = useValues(projectTreeLogic)
 
     const {
@@ -76,6 +77,7 @@ export function ProjectTree(): JSX.Element {
         setEditingItemId,
         setMovingItems,
         setTreeTableColumnSizes,
+        setSelectMode,
     } = useActions(projectTreeLogic)
 
     const { showLayoutPanel, setPanelTreeRef, clearActivePanelIdentifier, setProjectTreeMode } =
@@ -113,16 +115,20 @@ export function ProjectTree(): JSX.Element {
 
         return (
             <>
-                {item.record?.path ? (
-                    <MenuItem
-                        asChild
-                        onClick={(e) => {
-                            e.stopPropagation()
-                            onItemChecked(item.id, !checkedItems[item.id], false)
-                        }}
-                    >
-                        <ButtonPrimitive menuItem>{checkedItems[item.id] ? 'Deselect' : 'Select'}</ButtonPrimitive>
-                    </MenuItem>
+                {item.record?.path && !item.disableSelect ? (
+                    <>
+                        <MenuItem
+                            asChild
+                            onClick={(e) => {
+                                e.stopPropagation()
+                                onItemChecked(item.id, !checkedItems[item.id], false)
+                            }}
+                        >
+                            <ButtonPrimitive menuItem>{checkedItems[item.id] ? 'Deselect' : 'Select'}</ButtonPrimitive>
+                        </MenuItem>
+
+                        <MenuSeparator />
+                    </>
                 ) : null}
 
                 {item.record?.path && item.record?.type !== 'folder' && item.record?.href ? (
@@ -152,7 +158,6 @@ export function ProjectTree(): JSX.Element {
 
                 {checkedItemCountNumeric > 0 && item.record?.type === 'folder' ? (
                     <>
-                        <MenuSeparator />
                         <MenuItem
                             asChild
                             onClick={(e) => {
@@ -175,12 +180,13 @@ export function ProjectTree(): JSX.Element {
                                 Create {checkedItemsCount} shortcut{checkedItemsCount === '1' ? '' : 's'} here
                             </ButtonPrimitive>
                         </MenuItem>
+
+                        <MenuSeparator />
                     </>
                 ) : null}
 
                 {item.record?.type === 'folder' || item.id?.startsWith('project-folder-empty/') ? (
                     <>
-                        <MenuSeparator />
                         <MenuSub key="new">
                             <MenuSubTrigger asChild>
                                 <ButtonPrimitive menuItem>
@@ -260,6 +266,7 @@ export function ProjectTree(): JSX.Element {
                                 })}
                             </MenuSubContent>
                         </MenuSub>
+
                         <MenuSeparator />
                     </>
                 ) : null}
@@ -335,14 +342,37 @@ export function ProjectTree(): JSX.Element {
             searchPlaceholder="Search your project"
             panelActions={
                 <>
-                    <ButtonPrimitive onClick={() => createFolder('')} tooltip="New root folder">
+                    <ButtonPrimitive onClick={() => createFolder('')} tooltip="New root folder" iconOnly>
                         <IconFolderPlus className="text-tertiary" />
                     </ButtonPrimitive>
-                    {checkedItemCountNumeric > 0 && checkedItemsCount !== '0+' ? (
-                        <ButtonPrimitive onClick={() => setCheckedItems({})} tooltip="Clear">
-                            <LemonTag type="highlight">{checkedItemsCount} selected</LemonTag>
+
+                    {selectMode === 'default' && checkedItemCountNumeric === 0 ? (
+                        <ButtonPrimitive onClick={() => setSelectMode('multi')} tooltip="Enable multi-select" iconOnly>
+                            <IconCheckbox className="text-tertiary size-4" />
                         </ButtonPrimitive>
-                    ) : null}
+                    ) : (
+                        <>
+                            {checkedItemCountNumeric > 0 && checkedItemsCount !== '0+' ? (
+                                <ButtonPrimitive
+                                    onClick={() => {
+                                        setCheckedItems({})
+                                        setSelectMode('default')
+                                    }}
+                                    tooltip="Clear selected and disable multi-select"
+                                >
+                                    <LemonTag type="highlight">{checkedItemsCount} selected</LemonTag>
+                                </ButtonPrimitive>
+                            ) : (
+                                <ButtonPrimitive
+                                    onClick={() => setSelectMode('default')}
+                                    tooltip="Disable multi-select"
+                                    iconOnly
+                                >
+                                    <IconX className="text-tertiary size-4" />
+                                </ButtonPrimitive>
+                            )}
+                        </>
+                    )}
                 </>
             }
         >
@@ -375,7 +405,7 @@ export function ProjectTree(): JSX.Element {
                     }
                     return window.location.href.endsWith(item.record?.href)
                 }}
-                enableMultiSelection={checkedItemCountNumeric > 0}
+                selectMode={selectMode}
                 onItemChecked={onItemChecked}
                 checkedItemCount={checkedItemCountNumeric}
                 onNodeClick={(node) => {
diff --git a/frontend/src/layout/panel-layout/ProjectTree/projectTreeLogic.tsx b/frontend/src/layout/panel-layout/ProjectTree/projectTreeLogic.tsx
index cb47b1e4475..a71fe168b77 100644
--- a/frontend/src/layout/panel-layout/ProjectTree/projectTreeLogic.tsx
+++ b/frontend/src/layout/panel-layout/ProjectTree/projectTreeLogic.tsx
@@ -6,7 +6,7 @@ import { subscriptions } from 'kea-subscriptions'
 import api from 'lib/api'
 import { dayjs } from 'lib/dayjs'
 import { GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic'
-import { TreeDataItem, TreeTableViewKeys } from 'lib/lemon-ui/LemonTree/LemonTree'
+import { LemonTreeSelectMode, TreeDataItem, TreeTableViewKeys } from 'lib/lemon-ui/LemonTree/LemonTree'
 import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
 import { capitalizeFirstLetter } from 'lib/utils'
 import { urls } from 'scenes/urls'
@@ -107,6 +107,7 @@ export const projectTreeLogic = kea<projectTreeLogicType>([
         clearScrollTarget: true,
         setEditingItemId: (id: string) => ({ id }),
         setMovingItems: (items: FileSystemEntry[]) => ({ items }),
+        setSelectMode: (selectMode: LemonTreeSelectMode) => ({ selectMode }),
         setTreeTableColumnSizes: (sizes: number[]) => ({ sizes }),
     }),
     loaders(({ actions, values }) => ({
@@ -492,6 +493,12 @@ export const projectTreeLogic = kea<projectTreeLogicType>([
                 setEditingItemId: (_, { id }) => id,
             },
         ],
+        selectMode: [
+            'default' as LemonTreeSelectMode,
+            {
+                setSelectMode: (_, { selectMode }) => selectMode,
+            },
+        ],
         treeTableColumnSizes: [
             [350, 200, 200] as number[],
             { persist: true },
@@ -1066,6 +1073,12 @@ export const projectTreeLogic = kea<projectTreeLogicType>([
 
             actions.setLastCheckedItem(id, checked, shift)
             actions.setCheckedItems(checkedItems)
+
+            // If any items are checked, set the select mode to multi
+            // We don't do the inverse because we don't want to set the select mode to default when deselecting all
+            if (Object.values(checkedItems).some((v) => !!v)) {
+                actions.setSelectMode('multi')
+            }
         },
         moveCheckedItems: ({ path }) => {
             const { checkedItems } = values
diff --git a/frontend/src/lib/components/FolderSelect/FolderSelect.tsx b/frontend/src/lib/components/FolderSelect/FolderSelect.tsx
index 1707834f35b..817f956cb23 100644
--- a/frontend/src/lib/components/FolderSelect/FolderSelect.tsx
+++ b/frontend/src/lib/components/FolderSelect/FolderSelect.tsx
@@ -109,7 +109,7 @@ export function FolderSelect({ value, onChange, className }: FolderSelectProps):
         <div className={clsx('bg-surface-primary p-2 border rounded-[var(--radius)] overflow-y-scroll', className)}>
             <LemonTree
                 ref={treeRef}
-                folderSelectMode
+                selectMode="folder-only"
                 className="px-0 py-1"
                 data={projectTreeOnlyFolders}
                 mode="tree"
@@ -125,7 +125,6 @@ export function FolderSelect({ value, onChange, className }: FolderSelectProps):
                     setLocalEditingId('')
                 }}
                 // handleStartEditing={(itemId) => setLocalEditingId(itemId) }
-                enableMultiSelection={false}
                 showFolderActiveState={true}
                 checkedItemCount={0}
                 onFolderClick={(folder) => {
diff --git a/frontend/src/lib/lemon-ui/LemonTree/LemonTree.tsx b/frontend/src/lib/lemon-ui/LemonTree/LemonTree.tsx
index 06fff51967d..302d1976211 100644
--- a/frontend/src/lib/lemon-ui/LemonTree/LemonTree.tsx
+++ b/frontend/src/lib/lemon-ui/LemonTree/LemonTree.tsx
@@ -28,6 +28,8 @@ import {
     TreeNodeDroppable,
 } from './LemonTreeUtils'
 
+export type LemonTreeSelectMode = 'default' | 'multi' | 'folder-only'
+
 export type TreeDataItem = {
     /** The ID of the item. */
     id: string
@@ -100,8 +102,8 @@ type LemonTreeBaseProps = Omit<HTMLAttributes<HTMLDivElement>, 'onDragEnd'> & {
     showFolderActiveState?: boolean
     /** Whether to enable drag and drop of items. */
     enableDragAndDrop?: boolean
-    /** Whether to enable multi-selection. */
-    enableMultiSelection?: boolean
+    /** The mode of the tree. */
+    selectMode?: LemonTreeSelectMode
     /** Whether the item is active, useful for highlighting the current item against a URL path,
      * this takes precedence over showFolderActiveState, and selectedId state */
     isItemActive?: (item: TreeDataItem) => boolean
@@ -138,10 +140,6 @@ type LemonTreeBaseProps = Omit<HTMLAttributes<HTMLDivElement>, 'onDragEnd'> & {
     setFocusToElementFromId?: (id: string) => void
     /** Set the focus to the last focused element. */
     setFocusToLastFocusedElement?: () => void
-
-    /** Whether to enable folder select mode. */
-    folderSelectMode?: boolean
-
     /** The keys for the table view */
     tableViewKeys?: TreeTableViewKeys
 
@@ -217,13 +215,12 @@ const LemonTreeNode = forwardRef<HTMLDivElement, LemonTreeNodeProps>(
             enableDragAndDrop = false,
             disableKeyboardInput,
             itemContextMenu,
-            enableMultiSelection = false,
+            selectMode = 'default',
             onItemChecked,
             isDragging,
             checkedItemCount,
             setFocusToElementFromId,
             setFocusToLastFocusedElement,
-            folderSelectMode = false,
             ...props
         },
         ref
@@ -258,332 +255,320 @@ const LemonTreeNode = forwardRef<HTMLDivElement, LemonTreeNodeProps>(
 
         return (
             <div
-                className={cn('list-none m-0 p-0 h-full overflow-hidden', className, {
+                className={cn('flex flex-col gap-y-px list-none m-0 p-0 h-full overflow-hidden', className, {
                     'overflow-x-auto pt-0': mode === 'table' && depth === 0,
                 })}
             >
-                <div
-                    className={cn('list-none m-0 p-0 h-full flex flex-col', {
-                        'w-full': mode === 'table' && depth === 0,
-                    })}
-                >
-                    {data.map((item, index) => {
-                        const displayName = item.displayName ?? item.name
-                        const isFolder = item.record?.type === 'folder'
-                        const isEmptyFolder = item.type === 'empty-folder'
-                        const folderLinesOffset = DEPTH_OFFSET
-                        const emptySpaceOffset = DEPTH_OFFSET + 16
-                        const iconWrapperOffset = DEPTH_OFFSET + 5
-                        const iconWrapperOffsetMultiSelection = DEPTH_OFFSET + 28
-
-                        const firstColumnOffset =
-                            enableMultiSelection && !item.disableSelect ? emptySpaceOffset + 26 : emptySpaceOffset
-
-                        // If table mode, renders: "tree item: Name: My App Dashboard, Created at: Mar 28, 2025, Created by: Adam etc"
-                        // If empty folder, renders: "empty folder"
-                        // If tree mode, renders: "tree item: My App Dashboard"
-                        const ariaLabel =
-                            mode === 'table' && tableViewKeys
-                                ? `tree item: ${tableViewKeys?.headers
-                                      .map((header) => {
-                                          const value = header.key
-                                              .split('.')
-                                              .reduce((obj, key) => (obj as any)?.[key], item)
-                                          const formattedValue = header.formatFunction
-                                              ? header.formatFunction(value)
-                                              : value
-                                          // Add null/undefined check and handle object values properly
-                                          const displayValue =
-                                              formattedValue === null || formattedValue === undefined
-                                                  ? ''
-                                                  : typeof formattedValue === 'object'
-                                                  ? JSON.stringify(formattedValue)
-                                                  : String(formattedValue)
-                                          return `${header.title}: ${displayValue}`
-                                      })
-                                      .join(', ')}`
-                                : isEmptyFolder
-                                ? 'empty folder'
-                                : `tree item: ${item.name}`
-
-                        if (item.type === 'separator') {
-                            return (
-                                <div key={item.id} className="h-1 -mx-2 flex items-center">
-                                    <div className="border-b border-primary h-px my-2 flex-1" />
-                                </div>
-                            )
-                        }
+                {data.map((item, index) => {
+                    const displayName = item.displayName ?? item.name
+                    const isFolder = item.record?.type === 'folder'
+                    const isEmptyFolder = item.type === 'empty-folder'
+                    const folderLinesOffset = DEPTH_OFFSET
+                    const emptySpaceOffset = DEPTH_OFFSET + 16
+                    const iconWrapperOffset = DEPTH_OFFSET + 5
+                    const iconWrapperOffsetMultiSelection = DEPTH_OFFSET + 28
+
+                    const firstColumnOffset =
+                        selectMode === 'multi' && !item.disableSelect ? emptySpaceOffset + 26 : emptySpaceOffset
+
+                    // If table mode, renders: "tree item: Name: My App Dashboard, Created at: Mar 28, 2025, Created by: Adam etc"
+                    // If empty folder, renders: "empty folder"
+                    // If tree mode, renders: "tree item: My App Dashboard"
+                    const ariaLabel =
+                        mode === 'table' && tableViewKeys
+                            ? `tree item: ${tableViewKeys?.headers
+                                  .map((header) => {
+                                      const value = header.key
+                                          .split('.')
+                                          .reduce((obj, key) => (obj as any)?.[key], item)
+                                      const formattedValue = header.formatFunction
+                                          ? header.formatFunction(value)
+                                          : value
+                                      // Add null/undefined check and handle object values properly
+                                      const displayValue =
+                                          formattedValue === null || formattedValue === undefined
+                                              ? ''
+                                              : typeof formattedValue === 'object'
+                                              ? JSON.stringify(formattedValue)
+                                              : String(formattedValue)
+                                      return `${header.title}: ${displayValue}`
+                                  })
+                                  .join(', ')}`
+                            : isEmptyFolder
+                            ? 'empty folder'
+                            : `tree item: ${item.name}`
+
+                    if (item.type === 'separator') {
+                        return (
+                            <div key={item.id} className="h-1 -mx-2 flex items-center">
+                                <div className="border-b border-primary h-px my-2 flex-1" />
+                            </div>
+                        )
+                    }
 
-                        let button = (
-                            <Link
-                                data-id={item.id}
-                                // When dragging, don't allow links to be clicked,
-                                // without this drag end would fire this href causing a reload
-                                to={isEmptyFolder ? '#' : item.record?.href || '#'}
-                                onClick={() => {
-                                    handleClick(item)
-                                }}
-                                disabled={isDragging}
-                                role="treeitem"
-                                buttonProps={{
-                                    active: getItemActiveState(item),
-                                    menuItem: true,
-                                    hasSideActionRight: true,
-                                    disabled: isEmptyFolder,
-                                    className: cn(
-                                        'group/lemon-tree-button',
-                                        'relative z-1 focus-visible:bg-fill-button-tertiary-hover h-[var(--button-height-base)] motion-safe:transition-[padding] duration-50',
-                                        {
-                                            'bg-fill-button-tertiary-hover':
-                                                (folderSelectMode && selectedId === item.id && !isEmptyFolder) ||
-                                                isContextMenuOpenForItem === item.id,
-                                            'bg-fill-button-tertiary-active': getItemActiveState(item),
-                                            'group-hover/lemon-tree-button-group:bg-fill-button-tertiary-hover cursor-pointer':
-                                                !isEmptyFolder,
-                                            'hover:bg-transparent opacity-50 cursor-default':
-                                                (folderSelectMode && !isFolder) || isEmptyFolder,
-                                        }
-                                    ),
+                    let button = (
+                        <Link
+                            data-id={item.id}
+                            // When dragging, don't allow links to be clicked,
+                            // without this drag end would fire this href causing a reload
+                            to={isEmptyFolder ? '#' : item.record?.href || '#'}
+                            onClick={() => {
+                                handleClick(item)
+                            }}
+                            disabled={isDragging}
+                            role="treeitem"
+                            buttonProps={{
+                                active: getItemActiveState(item),
+                                menuItem: true,
+                                hasSideActionRight: true,
+                                disabled: isEmptyFolder,
+                                className: cn(
+                                    'group/lemon-tree-button',
+                                    'relative z-1 focus-visible:bg-fill-button-tertiary-hover h-[var(--button-height-base)] motion-safe:transition-[padding] duration-50',
+                                    {
+                                        'bg-fill-button-tertiary-hover':
+                                            (selectMode === 'folder-only' &&
+                                                selectedId === item.id &&
+                                                !isEmptyFolder) ||
+                                            isContextMenuOpenForItem === item.id,
+                                        'bg-fill-button-tertiary-active': getItemActiveState(item),
+                                        'group-hover/lemon-tree-button-group:bg-fill-button-tertiary-hover cursor-pointer':
+                                            !isEmptyFolder,
+                                        'hover:bg-transparent opacity-50 cursor-default':
+                                            (selectMode === 'folder-only' && !isFolder) || isEmptyFolder,
+                                    }
+                                ),
+                            }}
+                            tabIndex={isEmptyFolder ? -1 : 0}
+                            aria-level={depth + 1}
+                            aria-setsize={data.length} // TODO: somehow get all loaded items length here in children
+                            aria-posinset={index + 1}
+                            aria-selected={selectedId === item.id}
+                            aria-disabled={!!item.disabledReason}
+                            aria-haspopup={!!itemContextMenu?.(item)}
+                            aria-roledescription="tree item"
+                            aria-rolemap={`item-${item.id}`}
+                            aria-label={ariaLabel}
+                            tooltip={isDragging || isEmptyFolder || mode === 'table' ? undefined : displayName}
+                            tooltipPlacement="right"
+                        >
+                            {/* Spacer to offset button padding */}
+                            <span
+                                className="h-full bg-transparent pointer-events-none flex-shrink-0 transition-[width] duration-50"
+                                // eslint-disable-next-line react/forbid-dom-props
+                                style={{
+                                    width: `${firstColumnOffset}px`,
                                 }}
-                                tabIndex={isEmptyFolder ? -1 : 0}
-                                aria-level={depth + 1}
-                                aria-setsize={data.length} // TODO: somehow get all loaded items length here in children
-                                aria-posinset={index + 1}
-                                aria-selected={selectedId === item.id}
-                                aria-disabled={!!item.disabledReason}
-                                aria-haspopup={!!itemContextMenu?.(item)}
-                                aria-roledescription="tree item"
-                                aria-rolemap={`item-${item.id}`}
-                                aria-label={ariaLabel}
-                                tooltip={isDragging || isEmptyFolder || mode === 'table' ? undefined : displayName}
-                                tooltipPlacement="right"
-                            >
-                                {/* Spacer to offset button padding */}
-                                <span
-                                    className="h-full bg-transparent pointer-events-none flex-shrink-0 transition-[width] duration-50"
-                                    // eslint-disable-next-line react/forbid-dom-props
-                                    style={{
-                                        width: `${firstColumnOffset}px`,
-                                    }}
-                                />
-
-                                {mode === 'table' ? (
-                                    tableModeRow?.(item, firstColumnOffset + 20)
-                                ) : (
-                                    <span className="relative truncate text-left">
-                                        {renderItem ? (
-                                            <>
-                                                {renderItem(
-                                                    item,
-                                                    <span
-                                                        className={cn({
-                                                            'font-semibold': isFolder && !isEmptyFolder,
-                                                        })}
-                                                    >
-                                                        <span>{displayName}</span>
-                                                    </span>
-                                                )}
-                                            </>
-                                        ) : (
-                                            <span
-                                                className={cn('truncate', {
-                                                    'font-semibold': isFolder && !isEmptyFolder,
-                                                })}
-                                            >
-                                                <span>{displayName}</span>
-                                            </span>
-                                        )}
+                            />
+
+                            {mode === 'table' ? (
+                                tableModeRow?.(item, firstColumnOffset + 20)
+                            ) : (
+                                <span className="relative truncate text-left">
+                                    {renderItem ? (
+                                        <>
+                                            {renderItem(
+                                                item,
+                                                <span
+                                                    className={cn({
+                                                        'font-semibold': isFolder && !isEmptyFolder,
+                                                    })}
+                                                >
+                                                    <span>{displayName}</span>
+                                                </span>
+                                            )}
+                                        </>
+                                    ) : (
+                                        <span
+                                            className={cn('truncate', {
+                                                'font-semibold': isFolder && !isEmptyFolder,
+                                            })}
+                                        >
+                                            <span>{displayName}</span>
+                                        </span>
+                                    )}
 
-                                        {/* Loading state */}
-                                        {item.record?.loading && <Spinner className="ml-1" />}
+                                    {/* Loading state */}
+                                    {item.record?.loading && <Spinner className="ml-1" />}
 
-                                        {/* Unapplied state */}
-                                        {item.record?.unapplied && <IconUpload className="ml-1 text-warning" />}
-                                    </span>
-                                )}
-                            </Link>
+                                    {/* Unapplied state */}
+                                    {item.record?.unapplied && <IconUpload className="ml-1 text-warning" />}
+                                </span>
+                            )}
+                        </Link>
+                    )
+
+                    if (enableDragAndDrop && isItemDraggable?.(item) && item.id) {
+                        button = (
+                            <TreeNodeDraggable id={item.id} enableDragging className="h-[var(--button-height-base)]">
+                                {button}
+                            </TreeNodeDraggable>
                         )
+                    }
 
-                        if (enableDragAndDrop && isItemDraggable?.(item) && item.id) {
-                            button = (
-                                <TreeNodeDraggable
-                                    id={item.id}
-                                    enableDragging
-                                    className="h-[var(--button-height-base)]"
-                                >
-                                    {button}
-                                </TreeNodeDraggable>
-                            )
-                        }
+                    const content = (
+                        <AccordionPrimitive.Root
+                            type="multiple"
+                            value={expandedItemIds}
+                            onValueChange={(s) => {
+                                onSetExpandedItemIds?.(s)
+                            }}
+                            ref={ref}
+                            key={item.id}
+                            disabled={!!item.disabledReason}
+                        >
+                            <AccordionPrimitive.Item value={item.id} className="flex flex-col w-full gap-y-px">
+                                <AccordionPrimitive.Trigger className="flex items-center gap-2 w-full h-8" asChild>
+                                    <ContextMenu
+                                        onOpenChange={(open) => {
+                                            handleContextMenuOpen(open, item.id)
+                                        }}
+                                    >
+                                        {/* Folder lines */}
+                                        {depth !== 0 && (
+                                            <div
+                                                className="folder-line absolute border-r border-primary h-[calc(100%+2px)] -top-px pointer-events-none z-0"
+                                                // eslint-disable-next-line react/forbid-dom-props
+                                                style={{ width: `${folderLinesOffset}px` }}
+                                            />
+                                        )}
 
-                        const content = (
-                            <AccordionPrimitive.Root
-                                type="multiple"
-                                value={expandedItemIds}
-                                onValueChange={(s) => {
-                                    onSetExpandedItemIds?.(s)
-                                }}
-                                ref={ref}
-                                key={item.id}
-                                disabled={!!item.disabledReason}
-                            >
-                                <AccordionPrimitive.Item value={item.id} className="flex flex-col w-full gap-y-px">
-                                    <AccordionPrimitive.Trigger className="flex items-center gap-2 w-full h-8" asChild>
-                                        <ContextMenu
-                                            onOpenChange={(open) => {
-                                                handleContextMenuOpen(open, item.id)
-                                            }}
-                                        >
-                                            {/* Folder lines */}
-                                            {depth !== 0 && (
-                                                <div
-                                                    className="folder-line absolute border-r border-primary h-[calc(100%+2px)] -top-px pointer-events-none z-0"
-                                                    // eslint-disable-next-line react/forbid-dom-props
-                                                    style={{ width: `${folderLinesOffset}px` }}
+                                        <ContextMenuTrigger asChild>
+                                            <ButtonGroupPrimitive
+                                                fullWidth
+                                                className="group/lemon-tree-button-group relative h-[var(--button-height-base)]"
+                                            >
+                                                {/* The contents of this <TreeNodeDisplayIconWrapper> are positioned absolutely, so to give the effect it's inside the button */}
+                                                <TreeNodeDisplayIconWrapper
+                                                    item={item}
+                                                    expandedItemIds={expandedItemIds}
+                                                    defaultNodeIcon={defaultNodeIcon}
+                                                    handleClick={handleClick}
+                                                    selectMode={selectMode}
+                                                    defaultOffset={iconWrapperOffset}
+                                                    multiSelectionOffset={iconWrapperOffsetMultiSelection}
+                                                    onItemChecked={onItemChecked}
+                                                    isEmptyFolder={isEmptyFolder}
                                                 />
-                                            )}
 
-                                            <ContextMenuTrigger asChild>
-                                                <ButtonGroupPrimitive
-                                                    fullWidth
-                                                    className="group/lemon-tree-button-group relative h-[var(--button-height-base)]"
-                                                >
-                                                    {/* The contents of this <TreeNodeDisplayIconWrapper> are positioned absolutely, so to give the effect it's inside the button */}
-                                                    <TreeNodeDisplayIconWrapper
-                                                        item={item}
-                                                        expandedItemIds={expandedItemIds}
-                                                        defaultNodeIcon={defaultNodeIcon}
-                                                        handleClick={handleClick}
-                                                        enableMultiSelection={enableMultiSelection}
-                                                        defaultOffset={iconWrapperOffset}
-                                                        multiSelectionOffset={iconWrapperOffsetMultiSelection}
-                                                        checkedItemCount={checkedItemCount}
-                                                        onItemChecked={onItemChecked}
-                                                        folderSelectMode={folderSelectMode}
-                                                        isEmptyFolder={isEmptyFolder}
+                                                {isItemEditing?.(item) ? (
+                                                    <InlineEditField
+                                                        value={item.name}
+                                                        handleSubmit={(value) => {
+                                                            onItemNameChange?.(item, value)
+                                                            disableKeyboardInput?.(false)
+                                                        }}
+                                                        className="z-1"
+                                                        style={{
+                                                            paddingLeft: 'var(--button-padding-x-base)',
+                                                            width:
+                                                                selectMode === 'multi' && !item.disableSelect
+                                                                    ? `${emptySpaceOffset + 26}px`
+                                                                    : `${emptySpaceOffset}px`,
+                                                        }}
                                                     />
+                                                ) : (
+                                                    button
+                                                )}
 
-                                                    {isItemEditing?.(item) ? (
-                                                        <InlineEditField
-                                                            value={item.name}
-                                                            handleSubmit={(value) => {
-                                                                onItemNameChange?.(item, value)
-                                                                disableKeyboardInput?.(false)
-                                                            }}
-                                                            className="z-1"
-                                                            style={{
-                                                                paddingLeft: 'var(--button-padding-x-base)',
-                                                                width:
-                                                                    enableMultiSelection && !item.disableSelect
-                                                                        ? `${emptySpaceOffset + 26}px`
-                                                                        : `${emptySpaceOffset}px`,
-                                                            }}
-                                                        />
-                                                    ) : (
-                                                        button
-                                                    )}
-
-                                                    {itemSideAction && !isEmptyFolder && (
-                                                        <DropdownMenu>
-                                                            <DropdownMenuTrigger asChild>
-                                                                <ButtonPrimitive
-                                                                    iconOnly
-                                                                    isSideActionRight
-                                                                    className="z-2 shrink-0 motion-safe:transition-opacity duration-[50ms] group-hover/lemon-tree-button-group:opacity-100 aria-expanded:opacity-100"
-                                                                >
-                                                                    <IconEllipsis className="size-3 text-tertiary" />
-                                                                </ButtonPrimitive>
-                                                            </DropdownMenuTrigger>
-
-                                                            {/* The Dropdown content menu */}
-                                                            <DropdownMenuContent
-                                                                loop
-                                                                align="end"
-                                                                side="bottom"
-                                                                className="max-w-[250px]"
+                                                {itemSideAction && !isEmptyFolder && (
+                                                    <DropdownMenu>
+                                                        <DropdownMenuTrigger asChild>
+                                                            <ButtonPrimitive
+                                                                iconOnly
+                                                                isSideActionRight
+                                                                className="z-2 shrink-0 motion-safe:transition-opacity duration-[50ms] group-hover/lemon-tree-button-group:opacity-100 aria-expanded:opacity-100"
                                                             >
-                                                                {itemSideAction(item)}
-                                                            </DropdownMenuContent>
-                                                        </DropdownMenu>
-                                                    )}
-                                                </ButtonGroupPrimitive>
-                                            </ContextMenuTrigger>
-
-                                            {isContextMenuOpenForItem === item.id && itemContextMenu?.(item) ? (
-                                                <ContextMenuContent loop className="max-w-[250px]">
-                                                    {itemContextMenu(item)}
-                                                </ContextMenuContent>
-                                            ) : null}
-                                        </ContextMenu>
-                                    </AccordionPrimitive.Trigger>
-
-                                    {item.children && (
-                                        <AccordionPrimitive.Content className="relative">
-                                            <LemonTreeNode
-                                                data={item.children}
-                                                mode={mode}
-                                                tableViewKeys={tableViewKeys}
-                                                selectedId={selectedId}
-                                                handleClick={handleClick}
-                                                expandedItemIds={expandedItemIds}
-                                                onSetExpandedItemIds={onSetExpandedItemIds}
-                                                defaultNodeIcon={defaultNodeIcon}
-                                                showFolderActiveState={showFolderActiveState}
-                                                renderItem={renderItem}
-                                                itemSideAction={itemSideAction}
-                                                className="deprecated-space-y-px"
-                                                depth={depth + 1}
-                                                isItemActive={isItemActive}
-                                                isItemDraggable={isItemDraggable}
-                                                isItemDroppable={isItemDroppable}
-                                                enableDragAndDrop={enableDragAndDrop}
-                                                itemContextMenu={itemContextMenu}
-                                                enableMultiSelection={enableMultiSelection}
-                                                onItemChecked={onItemChecked}
-                                                isDragging={isDragging}
-                                                checkedItemCount={checkedItemCount}
-                                                tableModeTotalWidth={tableModeTotalWidth}
-                                                isItemEditing={isItemEditing}
-                                                disableKeyboardInput={disableKeyboardInput}
-                                                setFocusToElementFromId={setFocusToElementFromId}
-                                                onItemNameChange={onItemNameChange}
-                                                folderSelectMode={folderSelectMode}
-                                                tableModeRow={tableModeRow}
-                                                {...props}
-                                            />
-                                        </AccordionPrimitive.Content>
-                                    )}
-                                </AccordionPrimitive.Item>
-                            </AccordionPrimitive.Root>
+                                                                <IconEllipsis className="size-3 text-tertiary" />
+                                                            </ButtonPrimitive>
+                                                        </DropdownMenuTrigger>
+
+                                                        {/* The Dropdown content menu */}
+                                                        <DropdownMenuContent
+                                                            loop
+                                                            align="end"
+                                                            side="bottom"
+                                                            className="max-w-[250px]"
+                                                        >
+                                                            {itemSideAction(item)}
+                                                        </DropdownMenuContent>
+                                                    </DropdownMenu>
+                                                )}
+                                            </ButtonGroupPrimitive>
+                                        </ContextMenuTrigger>
+
+                                        {isContextMenuOpenForItem === item.id && itemContextMenu?.(item) ? (
+                                            <ContextMenuContent loop className="max-w-[250px]">
+                                                {itemContextMenu(item)}
+                                            </ContextMenuContent>
+                                        ) : null}
+                                    </ContextMenu>
+                                </AccordionPrimitive.Trigger>
+
+                                {item.children && (
+                                    <AccordionPrimitive.Content className="relative">
+                                        <LemonTreeNode
+                                            data={item.children}
+                                            mode={mode}
+                                            tableViewKeys={tableViewKeys}
+                                            selectedId={selectedId}
+                                            handleClick={handleClick}
+                                            expandedItemIds={expandedItemIds}
+                                            onSetExpandedItemIds={onSetExpandedItemIds}
+                                            defaultNodeIcon={defaultNodeIcon}
+                                            showFolderActiveState={showFolderActiveState}
+                                            renderItem={renderItem}
+                                            itemSideAction={itemSideAction}
+                                            depth={depth + 1}
+                                            isItemActive={isItemActive}
+                                            isItemDraggable={isItemDraggable}
+                                            isItemDroppable={isItemDroppable}
+                                            enableDragAndDrop={enableDragAndDrop}
+                                            itemContextMenu={itemContextMenu}
+                                            selectMode={selectMode}
+                                            onItemChecked={onItemChecked}
+                                            isDragging={isDragging}
+                                            checkedItemCount={checkedItemCount}
+                                            tableModeTotalWidth={tableModeTotalWidth}
+                                            isItemEditing={isItemEditing}
+                                            disableKeyboardInput={disableKeyboardInput}
+                                            setFocusToElementFromId={setFocusToElementFromId}
+                                            onItemNameChange={onItemNameChange}
+                                            tableModeRow={tableModeRow}
+                                            {...props}
+                                        />
+                                    </AccordionPrimitive.Content>
+                                )}
+                            </AccordionPrimitive.Item>
+                        </AccordionPrimitive.Root>
+                    )
+
+                    // Wrap content in Draggable/Droppable if needed
+                    let wrappedContent = content
+
+                    if (isItemDroppable?.(item)) {
+                        wrappedContent = (
+                            <TreeNodeDroppable id={item.id} isDroppable={item.record?.type === 'folder'}>
+                                {wrappedContent}
+                            </TreeNodeDroppable>
                         )
+                    }
 
-                        // Wrap content in Draggable/Droppable if needed
-                        let wrappedContent = content
-
-                        if (isItemDroppable?.(item)) {
-                            wrappedContent = (
-                                <TreeNodeDroppable id={item.id} isDroppable={item.record?.type === 'folder'}>
-                                    {wrappedContent}
-                                </TreeNodeDroppable>
-                            )
-                        }
-
-                        // If table and first level, show table headers
-                        if (index === 0 && depth === 0 && mode === 'table') {
-                            return (
-                                <div className="flex flex-col gap-1 sticky top-0" key={`table-header-${item.id}`}>
-                                    <div className="relative h-[30px] opacity-100 border-b border-primary -ml-[3px] motion-safe:transition-[height,display] duration-200 starting:h-0 [transition-behavior:allow-discrete] z-5">
-                                        {tableModeHeader?.()}
-                                    </div>
-
-                                    {wrappedContent}
+                    // If table and first level, show table headers
+                    if (index === 0 && depth === 0 && mode === 'table') {
+                        return (
+                            <div className="flex flex-col gap-1 sticky top-0" key={`table-header-${item.id}`}>
+                                <div className="relative h-[30px] opacity-100 border-b border-primary -ml-[3px] motion-safe:transition-[height,display] duration-200 starting:h-0 [transition-behavior:allow-discrete] z-5">
+                                    {tableModeHeader?.()}
                                 </div>
-                            )
-                        }
 
-                        return <div key={item.id}>{wrappedContent}</div>
-                    })}
-                </div>
+                                {wrappedContent}
+                            </div>
+                        )
+                    }
+
+                    return <div key={item.id}>{wrappedContent}</div>
+                })}
             </div>
         )
     }
@@ -614,12 +599,11 @@ const LemonTree = forwardRef<LemonTreeRef, LemonTreeProps>(
             enableDragAndDrop = false,
             itemContextMenu,
             isFinishedBuildingTreeData,
-            enableMultiSelection = false,
+            selectMode = 'default',
             onItemChecked,
             checkedItemCount = 0,
             tableViewKeys,
             emptySpaceContextMenu,
-            folderSelectMode = false,
             tableModeTotalWidth,
             tableModeHeader,
             tableModeRow,
@@ -723,7 +707,7 @@ const LemonTree = forwardRef<LemonTreeRef, LemonTreeProps>(
 
                 nodeArray.forEach((node) => {
                     // if folderSelectMode we don't return folder items
-                    if (!folderSelectMode || (folderSelectMode && node.children)) {
+                    if (selectMode !== 'folder-only') {
                         items.push(node)
                     }
                     if (node.children && expandedItemIdsState?.includes(node.id)) {
@@ -734,7 +718,7 @@ const LemonTree = forwardRef<LemonTreeRef, LemonTreeProps>(
 
             traverse(data)
             return items
-        }, [data, expandedItemIdsState, folderSelectMode])
+        }, [data, expandedItemIdsState, selectMode])
 
         // Focus on provided content ref
         const focusContent = useCallback(() => {
@@ -1271,7 +1255,7 @@ const LemonTree = forwardRef<LemonTreeRef, LemonTreeProps>(
                             itemSideAction={itemSideAction}
                             isItemEditing={isItemEditing}
                             onItemNameChange={onItemNameChange}
-                            className={cn('deprecated-space-y-px p-1', {
+                            className={cn('p-1', {
                                 'flex-1': isDragging,
                             })}
                             isItemDraggable={isItemDraggable}
@@ -1281,12 +1265,11 @@ const LemonTree = forwardRef<LemonTreeRef, LemonTreeProps>(
                                 setDisableKeyboardInput(disable)
                             }}
                             itemContextMenu={itemContextMenu}
-                            enableMultiSelection={enableMultiSelection}
+                            selectMode={selectMode}
                             onItemChecked={onItemChecked}
                             isDragging={isDragging}
                             checkedItemCount={checkedItemCount}
                             setFocusToElementFromId={focusElementFromId}
-                            folderSelectMode={folderSelectMode}
                             tableModeHeader={tableModeHeader}
                             tableModeRow={tableModeRow}
                             {...props}
diff --git a/frontend/src/lib/lemon-ui/LemonTree/LemonTreeUtils.tsx b/frontend/src/lib/lemon-ui/LemonTree/LemonTreeUtils.tsx
index 7a606512834..232f8c69813 100644
--- a/frontend/src/lib/lemon-ui/LemonTree/LemonTreeUtils.tsx
+++ b/frontend/src/lib/lemon-ui/LemonTree/LemonTreeUtils.tsx
@@ -5,7 +5,7 @@ import { cn } from 'lib/utils/css-classes'
 import { CSSProperties, useEffect, useRef } from 'react'
 
 import { LemonCheckbox } from '../LemonCheckbox'
-import { TreeDataItem } from './LemonTree'
+import { LemonTreeSelectMode, TreeDataItem } from './LemonTree'
 
 export const ICON_CLASSES = 'text-tertiary size-5 flex items-center justify-center'
 
@@ -14,12 +14,10 @@ type TreeNodeDisplayIconWrapperProps = {
     expandedItemIds?: string[]
     defaultNodeIcon?: React.ReactNode
     handleClick: (item: TreeDataItem) => void
-    enableMultiSelection: boolean
+    selectMode: LemonTreeSelectMode
     defaultOffset: number
     multiSelectionOffset: number
-    checkedItemCount?: number
     onItemChecked?: (id: string, checked: boolean, shift: boolean) => void
-    folderSelectMode: boolean
     isEmptyFolder: boolean
 }
 
@@ -28,12 +26,10 @@ export const TreeNodeDisplayIconWrapper = ({
     expandedItemIds,
     defaultNodeIcon,
     handleClick,
-    enableMultiSelection,
-    checkedItemCount,
+    selectMode,
     onItemChecked,
     defaultOffset,
     multiSelectionOffset,
-    folderSelectMode,
     isEmptyFolder,
 }: TreeNodeDisplayIconWrapperProps): JSX.Element => {
     return (
@@ -47,8 +43,6 @@ export const TreeNodeDisplayIconWrapper = ({
                 className={cn(
                     'absolute flex items-center justify-center bg-transparent flex-shrink-0 h-[var(--button-height-base)] z-3',
                     {
-                        // Apply group class only when there are no checked items
-                        'group/lemon-tree-icon-wrapper': checkedItemCount === 0 && !folderSelectMode && !isEmptyFolder,
                         'cursor-default': isEmptyFolder,
                     }
                 )}
@@ -59,9 +53,8 @@ export const TreeNodeDisplayIconWrapper = ({
                         onItemChecked?.(item.id, checked, shift)
                     }}
                     className={cn('absolute z-2', {
-                        // Apply hidden class only when hovering the (conditional)group and there are no checked items
-                        'hidden group-hover/lemon-tree-icon-wrapper:block transition-all duration-50':
-                            checkedItemCount === 0 || folderSelectMode,
+                        // Hide checkboxwhen select mode is default/folder only
+                        hidden: selectMode === 'default' || selectMode === 'folder-only',
                     })}
                     style={{
                         left: `${defaultOffset}px`,
@@ -74,7 +67,7 @@ export const TreeNodeDisplayIconWrapper = ({
                     style={{
                         // If multi-selection is enabled, we need to offset the icon to the right to make space for the checkbox
                         left:
-                            enableMultiSelection && !item.disableSelect
+                            selectMode === 'multi' && !item.disableSelect
                                 ? `${multiSelectionOffset}px`
                                 : `${defaultOffset}px`,
                     }}
@@ -117,11 +110,14 @@ export const TreeNodeDisplayCheckbox = ({
         >
             <div className={ICON_CLASSES}>
                 <LemonCheckbox
-                    className={cn('size-5 ml-[2px]', {
-                        // Hide the checkbox if the item is disabled from being checked and is a folder
-                        // When searching we disable folders from being checked
-                        hidden: item.disableSelect && item.record?.type === 'folder',
-                    })}
+                    className={cn(
+                        'size-5 ml-[2px] starting:opacity-0 starting:-translate-x-2 translate-x-0 opacity-100 motion-safe:transition-all [transition-behavior:allow-discrete] duration-100',
+                        {
+                            // Hide the checkbox if the item is disabled from being checked and is a folder
+                            // When searching we disable folders from being checked
+                            hidden: item.disableSelect && item.record?.type === 'folder',
+                        }
+                    )}
                     checked={isChecked ?? false}
                     onChange={(checked, event) => {
                         // Just in case
@@ -175,12 +171,7 @@ export const TreeNodeDisplayIcon = ({
     }
 
     return (
-        <div
-            className={cn('flex gap-1 relative [&_svg]:size-4', {
-                // Don't hide the icon on hover if the item is disabled from being checked
-                'group-hover/lemon-tree-icon-wrapper:opacity-0': !item.disableSelect,
-            })}
-        >
+        <div className="flex gap-1 relative [&_svg]:size-4">
             {isFolder && (
                 <div
                     className={cn(
diff --git a/frontend/src/lib/lemon-ui/icons/categories.ts b/frontend/src/lib/lemon-ui/icons/categories.ts
index bd13730f9b1..0e334bc235a 100644
--- a/frontend/src/lib/lemon-ui/icons/categories.ts
+++ b/frontend/src/lib/lemon-ui/icons/categories.ts
@@ -129,6 +129,7 @@ export const TECHNOLOGY = {
         'IconFolder',
         'IconFolderOpen',
         'IconFolderPlus',
+        'IconFolderMove',
     ],
     UI: [
         'IconPassword',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cd053798bcd..9f08886f26a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -43,10 +43,10 @@ importers:
     devDependencies:
       '@parcel/packager-ts':
         specifier: 2.13.3
-        version: 2.13.3(@parcel/core@2.13.3(@swc/helpers@0.5.15))
+        version: 2.13.3(@parcel/core@2.13.3)
       '@parcel/transformer-typescript-types':
         specifier: 2.13.3
-        version: 2.13.3(@parcel/core@2.13.3(@swc/helpers@0.5.15))(typescript@4.9.5)
+        version: 2.13.3(@parcel/core@2.13.3)(typescript@4.9.5)
       eslint:
         specifier: ^8.57.0
         version: 8.57.0
@@ -67,7 +67,7 @@ importers:
         version: 2.29.0(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)
       eslint-plugin-jest:
         specifier: ^28.6.0
-        version: 28.6.0(@typescript-eslint/eslint-plugin@7.1.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(jest@29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@4.9.5)))(typescript@4.9.5)
+        version: 28.6.0(@typescript-eslint/eslint-plugin@7.1.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(jest@29.7.0)(typescript@4.9.5)
       eslint-plugin-posthog:
         specifier: workspace:*
         version: link:common/eslint_rules
@@ -100,7 +100,7 @@ importers:
         version: 4.3.0(stylelint@15.11.0(typescript@4.9.5))
       stylelint-config-standard-scss:
         specifier: ^11.1.0
-        version: 11.1.0(postcss@8.4.31)(stylelint@15.11.0(typescript@4.9.5))
+        version: 11.1.0(postcss@8.5.3)(stylelint@15.11.0(typescript@4.9.5))
       stylelint-order:
         specifier: ^6.0.3
         version: 6.0.3(stylelint@15.11.0(typescript@4.9.5))
@@ -1101,7 +1101,7 @@ importers:
         version: 2.29.0(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)
       eslint-plugin-jest:
         specifier: ^28.6.0
-        version: 28.6.0(@typescript-eslint/eslint-plugin@7.1.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(jest@29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@4.9.5)))(typescript@4.9.5)
+        version: 28.6.0(@typescript-eslint/eslint-plugin@7.1.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(jest@29.7.0)(typescript@4.9.5)
       eslint-plugin-posthog:
         specifier: workspace:*
         version: link:../common/eslint_rules
@@ -1185,7 +1185,7 @@ importers:
         version: 4.3.0(stylelint@15.11.0(typescript@4.9.5))
       stylelint-config-standard-scss:
         specifier: ^11.1.0
-        version: 11.1.0(postcss@8.5.3)(stylelint@15.11.0(typescript@4.9.5))
+        version: 11.1.0(postcss@8.4.31)(stylelint@15.11.0(typescript@4.9.5))
       stylelint-order:
         specifier: ^6.0.3
         version: 6.0.3(stylelint@15.11.0(typescript@4.9.5))
@@ -16673,7 +16673,7 @@ snapshots:
       '@babel/traverse': 7.26.4
       '@babel/types': 7.26.3
       convert-source-map: 2.0.0
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -16738,7 +16738,7 @@ snapshots:
       '@babel/core': 7.26.0
       '@babel/helper-compilation-targets': 7.25.9
       '@babel/helper-plugin-utils': 7.24.7
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       lodash.debounce: 4.0.8
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -16749,7 +16749,7 @@ snapshots:
       '@babel/core': 7.26.0
       '@babel/helper-compilation-targets': 7.25.9
       '@babel/helper-plugin-utils': 7.24.7
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       lodash.debounce: 4.0.8
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -16760,7 +16760,7 @@ snapshots:
       '@babel/core': 7.26.0
       '@babel/helper-compilation-targets': 7.25.9
       '@babel/helper-plugin-utils': 7.24.7
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       lodash.debounce: 4.0.8
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -17531,7 +17531,7 @@ snapshots:
       '@babel/parser': 7.26.3
       '@babel/template': 7.25.9
       '@babel/types': 7.26.3
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -18071,7 +18071,7 @@ snapshots:
   '@eslint/eslintrc@2.1.4':
     dependencies:
       ajv: 6.12.6
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       espree: 9.6.1
       globals: 13.23.0
       ignore: 5.2.4
@@ -18201,7 +18201,7 @@ snapshots:
   '@humanwhocodes/config-array@0.11.14':
     dependencies:
       '@humanwhocodes/object-schema': 2.0.3
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
@@ -18602,7 +18602,7 @@ snapshots:
       '@open-draft/until': 1.0.3
       '@types/debug': 4.1.7
       '@xmldom/xmldom': 0.8.6
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       headers-polyfill: 3.2.5
       outvariant: 1.4.0
       strict-event-emitter: 0.2.8
@@ -18761,6 +18761,14 @@ snapshots:
       '@parcel/utils': 2.13.3
       lmdb: 2.8.5
 
+  '@parcel/cache@2.13.3(@parcel/core@2.13.3)':
+    dependencies:
+      '@parcel/core': 2.13.3
+      '@parcel/fs': 2.13.3(@parcel/core@2.13.3)
+      '@parcel/logger': 2.13.3
+      '@parcel/utils': 2.13.3
+      lmdb: 2.8.5
+
   '@parcel/codeframe@2.13.3':
     dependencies:
       chalk: 4.1.2
@@ -18817,6 +18825,36 @@ snapshots:
       - typescript
       - uncss
 
+  '@parcel/core@2.13.3':
+    dependencies:
+      '@mischnic/json-sourcemap': 0.1.1
+      '@parcel/cache': 2.13.3(@parcel/core@2.13.3)
+      '@parcel/diagnostic': 2.13.3
+      '@parcel/events': 2.13.3
+      '@parcel/feature-flags': 2.13.3
+      '@parcel/fs': 2.13.3(@parcel/core@2.13.3)
+      '@parcel/graph': 3.3.3
+      '@parcel/logger': 2.13.3
+      '@parcel/package-manager': 2.13.3(@parcel/core@2.13.3)
+      '@parcel/plugin': 2.13.3(@parcel/core@2.13.3)
+      '@parcel/profiler': 2.13.3
+      '@parcel/rust': 2.13.3
+      '@parcel/source-map': 2.1.1
+      '@parcel/types': 2.13.3(@parcel/core@2.13.3)
+      '@parcel/utils': 2.13.3
+      '@parcel/workers': 2.13.3(@parcel/core@2.13.3)
+      base-x: 3.0.10
+      browserslist: 4.24.3
+      clone: 2.1.2
+      dotenv: 16.4.7
+      dotenv-expand: 11.0.7
+      json5: 2.2.3
+      msgpackr: 1.11.2
+      nullthrows: 1.1.1
+      semver: 7.7.0
+    transitivePeerDependencies:
+      - '@swc/helpers'
+
   '@parcel/core@2.13.3(@swc/helpers@0.5.15)':
     dependencies:
       '@mischnic/json-sourcemap': 0.1.1
@@ -18866,6 +18904,16 @@ snapshots:
       '@parcel/watcher': 2.5.1
       '@parcel/workers': 2.13.3(@parcel/core@2.13.3(@swc/helpers@0.5.15))
 
+  '@parcel/fs@2.13.3(@parcel/core@2.13.3)':
+    dependencies:
+      '@parcel/core': 2.13.3
+      '@parcel/feature-flags': 2.13.3
+      '@parcel/rust': 2.13.3
+      '@parcel/types-internal': 2.13.3
+      '@parcel/utils': 2.13.3
+      '@parcel/watcher': 2.5.1
+      '@parcel/workers': 2.13.3(@parcel/core@2.13.3)
+
   '@parcel/graph@3.3.3':
     dependencies:
       '@parcel/feature-flags': 2.13.3
@@ -18900,6 +18948,18 @@ snapshots:
     transitivePeerDependencies:
       - '@parcel/core'
 
+  '@parcel/node-resolver-core@3.4.3(@parcel/core@2.13.3)':
+    dependencies:
+      '@mischnic/json-sourcemap': 0.1.1
+      '@parcel/diagnostic': 2.13.3
+      '@parcel/fs': 2.13.3(@parcel/core@2.13.3)
+      '@parcel/rust': 2.13.3
+      '@parcel/utils': 2.13.3
+      nullthrows: 1.1.1
+      semver: 7.7.0
+    transitivePeerDependencies:
+      - '@parcel/core'
+
   '@parcel/optimizer-css@2.13.3(@parcel/core@2.13.3(@swc/helpers@0.5.15))':
     dependencies:
       '@parcel/diagnostic': 2.13.3
@@ -18976,6 +19036,21 @@ snapshots:
     transitivePeerDependencies:
       - '@swc/helpers'
 
+  '@parcel/package-manager@2.13.3(@parcel/core@2.13.3)':
+    dependencies:
+      '@parcel/core': 2.13.3
+      '@parcel/diagnostic': 2.13.3
+      '@parcel/fs': 2.13.3(@parcel/core@2.13.3)
+      '@parcel/logger': 2.13.3
+      '@parcel/node-resolver-core': 3.4.3(@parcel/core@2.13.3)
+      '@parcel/types': 2.13.3(@parcel/core@2.13.3)
+      '@parcel/utils': 2.13.3
+      '@parcel/workers': 2.13.3(@parcel/core@2.13.3)
+      '@swc/core': 1.11.4(@swc/helpers@0.5.15)
+      semver: 7.7.0
+    transitivePeerDependencies:
+      - '@swc/helpers'
+
   '@parcel/packager-css@2.13.3(@parcel/core@2.13.3(@swc/helpers@0.5.15))':
     dependencies:
       '@parcel/diagnostic': 2.13.3
@@ -19031,6 +19106,12 @@ snapshots:
     transitivePeerDependencies:
       - '@parcel/core'
 
+  '@parcel/packager-ts@2.13.3(@parcel/core@2.13.3)':
+    dependencies:
+      '@parcel/plugin': 2.13.3(@parcel/core@2.13.3)
+    transitivePeerDependencies:
+      - '@parcel/core'
+
   '@parcel/packager-wasm@2.13.3(@parcel/core@2.13.3(@swc/helpers@0.5.15))':
     dependencies:
       '@parcel/plugin': 2.13.3(@parcel/core@2.13.3(@swc/helpers@0.5.15))
@@ -19043,6 +19124,12 @@ snapshots:
     transitivePeerDependencies:
       - '@parcel/core'
 
+  '@parcel/plugin@2.13.3(@parcel/core@2.13.3)':
+    dependencies:
+      '@parcel/types': 2.13.3(@parcel/core@2.13.3)
+    transitivePeerDependencies:
+      - '@parcel/core'
+
   '@parcel/profiler@2.13.3':
     dependencies:
       '@parcel/diagnostic': 2.13.3
@@ -19255,6 +19342,18 @@ snapshots:
     transitivePeerDependencies:
       - '@parcel/core'
 
+  '@parcel/transformer-typescript-types@2.13.3(@parcel/core@2.13.3)(typescript@4.9.5)':
+    dependencies:
+      '@parcel/diagnostic': 2.13.3
+      '@parcel/plugin': 2.13.3(@parcel/core@2.13.3)
+      '@parcel/source-map': 2.1.1
+      '@parcel/ts-utils': 2.13.3(typescript@4.9.5)
+      '@parcel/utils': 2.13.3
+      nullthrows: 1.1.1
+      typescript: 4.9.5
+    transitivePeerDependencies:
+      - '@parcel/core'
+
   '@parcel/ts-utils@2.13.3(typescript@4.9.5)':
     dependencies:
       nullthrows: 1.1.1
@@ -19274,6 +19373,13 @@ snapshots:
     transitivePeerDependencies:
       - '@parcel/core'
 
+  '@parcel/types@2.13.3(@parcel/core@2.13.3)':
+    dependencies:
+      '@parcel/types-internal': 2.13.3
+      '@parcel/workers': 2.13.3(@parcel/core@2.13.3)
+    transitivePeerDependencies:
+      - '@parcel/core'
+
   '@parcel/utils@2.13.3':
     dependencies:
       '@parcel/codeframe': 2.13.3
@@ -19355,6 +19461,16 @@ snapshots:
       '@parcel/utils': 2.13.3
       nullthrows: 1.1.1
 
+  '@parcel/workers@2.13.3(@parcel/core@2.13.3)':
+    dependencies:
+      '@parcel/core': 2.13.3
+      '@parcel/diagnostic': 2.13.3
+      '@parcel/logger': 2.13.3
+      '@parcel/profiler': 2.13.3
+      '@parcel/types-internal': 2.13.3
+      '@parcel/utils': 2.13.3
+      nullthrows: 1.1.1
+
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
@@ -21407,7 +21523,7 @@ snapshots:
       '@swc-node/sourcemap-support': 0.5.1
       '@swc/core': 1.11.4(@swc/helpers@0.5.15)
       colorette: 2.0.20
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       oxc-resolver: 1.12.0
       pirates: 4.0.6
       tslib: 2.8.1
@@ -22548,7 +22664,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/typescript-estree': 7.1.1(typescript@4.9.5)
       '@typescript-eslint/utils': 7.1.1(eslint@8.57.0)(typescript@4.9.5)
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       eslint: 8.57.0
       ts-api-utils: 1.0.2(typescript@4.9.5)
     optionalDependencies:
@@ -22564,7 +22680,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/types': 5.62.0
       '@typescript-eslint/visitor-keys': 5.62.0
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.7.0
@@ -22578,7 +22694,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/types': 7.1.1
       '@typescript-eslint/visitor-keys': 7.1.1
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       globby: 11.1.0
       is-glob: 4.0.3
       minimatch: 9.0.3
@@ -22823,7 +22939,7 @@ snapshots:
 
   agent-base@6.0.2:
     dependencies:
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
@@ -22831,7 +22947,7 @@ snapshots:
 
   agentkeepalive@4.3.0:
     dependencies:
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       depd: 2.0.0
       humanize-ms: 1.2.1
     transitivePeerDependencies:
@@ -24837,6 +24953,10 @@ snapshots:
     dependencies:
       ms: 2.0.0
 
+  debug@3.2.7:
+    dependencies:
+      ms: 2.1.3
+
   debug@3.2.7(supports-color@5.5.0):
     dependencies:
       ms: 2.1.3
@@ -24853,6 +24973,10 @@ snapshots:
     dependencies:
       ms: 2.1.2
 
+  debug@4.4.0:
+    dependencies:
+      ms: 2.1.3
+
   debug@4.4.0(supports-color@8.1.1):
     dependencies:
       ms: 2.1.3
@@ -25477,7 +25601,7 @@ snapshots:
 
   esbuild-register@3.5.0(esbuild@0.18.20):
     dependencies:
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       esbuild: 0.18.20
     transitivePeerDependencies:
       - supports-color
@@ -25582,7 +25706,7 @@ snapshots:
 
   eslint-import-resolver-node@0.3.9:
     dependencies:
-      debug: 3.2.7(supports-color@5.5.0)
+      debug: 3.2.7
       is-core-module: 2.13.1
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -25590,7 +25714,7 @@ snapshots:
 
   eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
     dependencies:
-      debug: 3.2.7(supports-color@5.5.0)
+      debug: 3.2.7
     optionalDependencies:
       '@typescript-eslint/parser': 7.1.1(eslint@8.57.0)(typescript@4.9.5)
       eslint: 8.57.0
@@ -25632,7 +25756,7 @@ snapshots:
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
       array.prototype.flatmap: 1.3.2
-      debug: 3.2.7(supports-color@5.5.0)
+      debug: 3.2.7
       doctrine: 2.1.0
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
@@ -25653,7 +25777,7 @@ snapshots:
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.1.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(jest@29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@4.9.5)))(typescript@4.9.5):
+  eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.1.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(jest@29.7.0)(typescript@4.9.5):
     dependencies:
       '@typescript-eslint/utils': 7.1.1(eslint@8.57.0)(typescript@4.9.5)
       eslint: 8.57.0
@@ -26980,14 +27104,14 @@ snapshots:
     dependencies:
       '@tootallnate/once': 2.0.0
       agent-base: 6.0.2
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
   http-proxy-agent@7.0.2:
     dependencies:
       agent-base: 7.1.3
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
@@ -27009,14 +27133,14 @@ snapshots:
   https-proxy-agent@5.0.1:
     dependencies:
       agent-base: 6.0.2
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
   https-proxy-agent@7.0.6:
     dependencies:
       agent-base: 7.1.3
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
     transitivePeerDependencies:
       - supports-color
 
@@ -27578,7 +27702,7 @@ snapshots:
 
   istanbul-lib-source-maps@4.0.1:
     dependencies:
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       istanbul-lib-coverage: 3.2.0
       source-map: 0.6.1
     transitivePeerDependencies:
@@ -28520,7 +28644,7 @@ snapshots:
     dependencies:
       chalk: 5.4.1
       commander: 13.1.0
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       execa: 8.0.1
       lilconfig: 3.1.3
       listr2: 8.2.5
@@ -28949,7 +29073,7 @@ snapshots:
 
   micromark@2.11.4:
     dependencies:
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       parse-entities: 2.0.0
     transitivePeerDependencies:
       - supports-color
@@ -32163,7 +32287,7 @@ snapshots:
   socks-proxy-agent@8.0.5:
     dependencies:
       agent-base: 7.1.3
-      debug: 4.4.0(supports-color@8.1.1)
+      debug: 4.4.0
       socks: 2.8.3
     transitivePeerDependencies:
       - supports-color


Reviews:
user: login: greptile-apps[bot]
body: 
## PR Summary

This PR enhances the LemonTree component's UX by consolidating selection modes and improving visual feedback.

- Merged `folderSelectMode` and `enableMultiSelection` into single `selectMode` prop with values 'default' | 'multi' | 'folder-only' in `/frontend/src/lib/lemon-ui/LemonTree/LemonTree.tsx`
- Removed confusing initial hover checkbox behavior, now triggered via toggle button or menu selection
- Added checkbox animation/transition effects to prevent abrupt appearance
- Fixed styling issues with `gap-y-px` spacing and restored `selectedId` highlighting
- Updated menu separators to prevent floating lines and improved overall visual consistency

<sub>7 file(s) reviewed, 2 comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=posthog_31861)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T10:34:59Z
user: login: mariusandra
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-02T11:01:25Z
user: login: adamleithp
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T11:23:32Z

Review comments:
diff_hunk: @@ -125,7 +125,6 @@ export function FolderSelect({ value, onChange, className }: FolderSelectProps):
                     setLocalEditingId('')
                 }}
                 // handleStartEditing={(itemId) => setLocalEditingId(itemId) }
path: frontend/src/lib/components/FolderSelect/FolderSelect.tsx
user: login: greptile-apps[bot]
body: style: commented-out code should be removed

```suggestion

```
created_at: 2025-05-02T10:33:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1050,6 +1057,12 @@ export const projectTreeLogic = kea<projectTreeLogicType>([
 
             actions.setLastCheckedItem(id, checked, shift)
             actions.setCheckedItems(checkedItems)
+
+            // If any items are checked, set the select mode to multi
+            // We don't do the inverse because we don't want to set the select mode to default when deselecting all
+            if (Object.values(checkedItems).some((v) => !!v)) {
+                actions.setSelectMode('multi')
+            }
path: frontend/src/layout/panel-layout/ProjectTree/projectTreeLogic.tsx
user: login: greptile-apps[bot]
body: style: Consider adding a check for empty checkedItems to revert back to default mode when all items are unchecked. This would make the behavior more symmetric.
created_at: 2025-05-02T10:33:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1050,6 +1057,12 @@ export const projectTreeLogic = kea<projectTreeLogicType>([
 
             actions.setLastCheckedItem(id, checked, shift)
             actions.setCheckedItems(checkedItems)
+
+            // If any items are checked, set the select mode to multi
+            // We don't do the inverse because we don't want to set the select mode to default when deselecting all
+            if (Object.values(checkedItems).some((v) => !!v)) {
+                actions.setSelectMode('multi')
+            }
path: frontend/src/layout/panel-layout/ProjectTree/projectTreeLogic.tsx
user: login: adamleithp
body: this is on purpose thanks
created_at: 2025-05-02T11:23:32Z
author_association: CONTRIBUTOR

PR comments:
user: login: posthog-bot
created_at: 2025-05-02T11:32:20Z
body: ## 📸 UI snapshots have been updated

**2** snapshot changes in total. **0** added, **2** modified, **0** deleted:

- **`chromium`**: **0** added, **2** modified, **0** deleted ([diff for shard 3](https://github.com/PostHog/posthog/pull/31861/commits/493287ce2ab3f755587f7223e6a27b58cb1c60c5))
- `webkit`: **0** added, **0** modified, **0** deleted

_Triggered by [this commit](https://github.com/PostHog/posthog/pull/31861/commits/2a4dc3b8e68cf4dc688a87575932023c41c0d0b4)._

👉 **[Review this PR's diff of snapshots.](https://github.com/PostHog/posthog/pull/31861/files#:~:text=frontend/__snapshots__/)**
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-02T11:42:07Z
body: **Size Change:** -240 B (-0.01%) 

**Total Size:** 3.72 MB



<details><summary>ℹ️ <strong>View Unchanged</strong></summary>

| Filename | Size | Change |
| :--- | :---: | :---: |
| `frontend/dist/toolbar.js` | 3.72 MB | -240 B (-0.01%) |

</details>



<a href="https://github.com/preactjs/compressed-size-action"><sub>compressed-size-action</sub></a>
author_association: CONTRIBUTOR