Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions apps/app/src/components/sidebar/PinnedThreadTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ interface SortablePinnedRootItemProps {
selectedThreadId?: string;
}

interface PinnedRootItemProps extends Omit<SortablePinnedRootItemProps, "disabled"> {
interface PinnedRootItemProps extends Omit<
SortablePinnedRootItemProps,
"disabled"
> {
consumeClickSuppression?: () => boolean;
}

Expand Down Expand Up @@ -171,6 +174,7 @@ const PinnedRootItem = memo(function PinnedRootItem({
selectedThreadId={selectedThreadId}
variant="section"
isManagerCollapsed={collapsedManagerIds.has(item.group.managerThread.id)}
collapsedManagerIds={collapsedManagerIds}
collapsedEnvironmentIds={collapsedEnvironmentIds}
onProjectSelect={onProjectSelect}
onToggleManagerCollapsed={onToggleManagerCollapsed}
Expand Down Expand Up @@ -242,6 +246,7 @@ const SortablePinnedRootItem = memo(function SortablePinnedRootItem({
selectedThreadId={selectedThreadId}
variant="section"
isManagerCollapsed={collapsedManagerIds.has(item.group.managerThread.id)}
collapsedManagerIds={collapsedManagerIds}
collapsedEnvironmentIds={collapsedEnvironmentIds}
onProjectSelect={onProjectSelect}
onToggleManagerCollapsed={onToggleManagerCollapsed}
Expand All @@ -265,8 +270,9 @@ export const PinnedThreadTree = memo(function PinnedThreadTree({
isPinnedReorderPending = false,
onReorderPinnedRoot,
}: PinnedThreadTreeProps) {
const [optimisticPinnedRootOrder, setOptimisticPinnedRootOrder] =
useState<PinnedRootOrderEntry[] | null>(null);
const [optimisticPinnedRootOrder, setOptimisticPinnedRootOrder] = useState<
PinnedRootOrderEntry[] | null
>(null);
const renderedRootItems = useMemo(() => {
if (!optimisticPinnedRootOrder) {
return rootItems;
Expand All @@ -289,7 +295,9 @@ export const PinnedThreadTree = memo(function PinnedThreadTree({
[renderedRootItems],
);
const reorderDisabled =
isPinnedReorderPending || !onReorderPinnedRoot || renderedRootItems.length < 2;
isPinnedReorderPending ||
!onReorderPinnedRoot ||
renderedRootItems.length < 2;
const {
beginDragClickSuppression,
clearDragClickSuppressionSoon,
Expand All @@ -306,9 +314,12 @@ export const PinnedThreadTree = memo(function PinnedThreadTree({
coordinateGetter: sortableKeyboardCoordinates,
}),
);
const handleDragStart = useCallback((_event: DragStartEvent) => {
beginDragClickSuppression();
}, [beginDragClickSuppression]);
const handleDragStart = useCallback(
(_event: DragStartEvent) => {
beginDragClickSuppression();
},
[beginDragClickSuppression],
);
const handleDragCancel = useCallback(() => {
clearDragClickSuppressionSoon();
}, [clearDragClickSuppressionSoon]);
Expand Down
150 changes: 134 additions & 16 deletions apps/app/src/components/sidebar/ProjectRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,17 @@ import {
} from "./projectThreadGroups";
import {
SIDEBAR_MANAGED_ENV_GROUP_LINE_CLASS,
SIDEBAR_MANAGER_CHILD_ROW_PADDING_CLASS,
SIDEBAR_MANAGER_GROUP_LINE_CLASS,
SIDEBAR_MANAGER_LINE_CONTINUATION_CLASS,
SIDEBAR_MANAGER_ROW_PADDING_CLASS,
SIDEBAR_PROJECT_GROUP_LINE_CLASS,
SIDEBAR_PROJECT_THREAD_ROW_PADDING_CLASS,
SIDEBAR_ROW_BASE_CLASS,
SIDEBAR_ROW_INTERACTIVE_STATE_CLASS,
SIDEBAR_SECTION_GROUP_LINE_CLASS,
SIDEBAR_SECTION_LINE_CONTINUATION_CLASS,
SIDEBAR_STANDARD_ROW_PADDING_CLASS,
getSidebarThreadRowPaddingClass,
type SidebarThreadRowIndent,
} from "./sidebarRowClasses";
import { SIDEBAR_SORTABLE_TRANSITION } from "./sortableMotion";
import {
Expand Down Expand Up @@ -225,6 +225,8 @@ type ProjectThreadListClickCaptureHandler = MouseEventHandler<HTMLDivElement>;
const EMPTY_PROJECT_THREADS: ThreadListEntry[] = [];
const PROJECT_ROW_LEADING_SLOT_CLASS =
"h-7 w-8 max-md:pointer-coarse:h-10 max-md:pointer-coarse:w-10";
const SIDEBAR_DEEP_MANAGER_LINE_CONTINUATION_CLASS =
"pointer-events-none absolute -bottom-0.5 left-16 top-0 z-[1] w-px bg-border-hairline";

interface ProjectThreadTreeGroupProps {
children: ReactNode;
Expand Down Expand Up @@ -311,12 +313,44 @@ function getProjectThreadTreeDefaultThreadOptions(
: THREAD_ROW_PROJECT_DEFAULT_OPTIONS;
}

type ManagerThreadGroupPlacement = "root" | "managed-child";

function getProjectThreadTreeManagedChildIndent(
variant: ProjectThreadTreeVariant,
placement: ManagerThreadGroupPlacement,
): SidebarThreadRowIndent {
if (placement === "managed-child") {
return variant === "section" ? "nested-child" : "deep-child";
}

return variant === "section" ? "project-child" : "nested-child";
}

function getProjectThreadTreeManagerIndent(
variant: ProjectThreadTreeVariant,
placement: ManagerThreadGroupPlacement,
): SidebarThreadRowIndent {
if (placement === "managed-child") {
return getProjectThreadTreeManagedChildIndent(variant, "root");
}

return variant === "section" ? "root" : "project-child";
}

function getProjectThreadTreeManagedChildOptions(
variant: ProjectThreadTreeVariant,
placement: ManagerThreadGroupPlacement,
): ThreadRowOptions {
return variant === "section"
? THREAD_ROW_SECTION_MANAGED_CHILD_OPTIONS
: THREAD_ROW_PROJECT_MANAGED_CHILD_OPTIONS;
if (placement === "root") {
return variant === "section"
? THREAD_ROW_SECTION_MANAGED_CHILD_OPTIONS
: THREAD_ROW_PROJECT_MANAGED_CHILD_OPTIONS;
}

return {
kind: "managed-child",
indent: getProjectThreadTreeManagedChildIndent(variant, placement),
};
}

function getProjectThreadTreeEnvGroupedChildOptions(
Expand All @@ -329,39 +363,67 @@ function getProjectThreadTreeEnvGroupedChildOptions(

function getProjectThreadTreeEnvGroupedManagedChildOptions(
variant: ProjectThreadTreeVariant,
placement: ManagerThreadGroupPlacement,
): ThreadRowOptions {
return variant === "section"
? THREAD_ROW_SECTION_ENV_GROUPED_MANAGED_CHILD_OPTIONS
: THREAD_ROW_PROJECT_ENV_GROUPED_MANAGED_CHILD_OPTIONS;
if (placement === "root") {
return variant === "section"
? THREAD_ROW_SECTION_ENV_GROUPED_MANAGED_CHILD_OPTIONS
: THREAD_ROW_PROJECT_ENV_GROUPED_MANAGED_CHILD_OPTIONS;
}

return {
kind: "env-grouped-managed-child",
indent: "deep-child",
};
}

function getProjectThreadTreeManagedEnvHeaderPaddingClass(
variant: ProjectThreadTreeVariant,
placement: ManagerThreadGroupPlacement,
): string {
return variant === "section"
? SIDEBAR_PROJECT_THREAD_ROW_PADDING_CLASS
: SIDEBAR_MANAGER_CHILD_ROW_PADDING_CLASS;
return getSidebarThreadRowPaddingClass(
getProjectThreadTreeManagedChildIndent(variant, placement),
);
}

function getProjectThreadTreeChildGroupLineClassName(
variant: ProjectThreadTreeVariant,
placement: ManagerThreadGroupPlacement = "root",
): string {
if (placement === "managed-child") {
return variant === "section"
? SIDEBAR_MANAGER_GROUP_LINE_CLASS
: SIDEBAR_MANAGED_ENV_GROUP_LINE_CLASS;
}

return variant === "section"
? SIDEBAR_SECTION_GROUP_LINE_CLASS
: SIDEBAR_MANAGER_GROUP_LINE_CLASS;
}

function getProjectThreadTreeManagedEnvGroupLineClassName(
variant: ProjectThreadTreeVariant,
placement: ManagerThreadGroupPlacement,
): string {
if (placement === "managed-child") {
return SIDEBAR_MANAGED_ENV_GROUP_LINE_CLASS;
}

return variant === "section"
? SIDEBAR_MANAGER_GROUP_LINE_CLASS
: SIDEBAR_MANAGED_ENV_GROUP_LINE_CLASS;
}

function getProjectThreadTreeManagerLineContinuationClassName(
variant: ProjectThreadTreeVariant,
placement: ManagerThreadGroupPlacement,
): string {
if (placement === "managed-child") {
return variant === "section"
? SIDEBAR_MANAGER_LINE_CONTINUATION_CLASS
: SIDEBAR_DEEP_MANAGER_LINE_CONTINUATION_CLASS;
}

return variant === "section"
? SIDEBAR_SECTION_LINE_CONTINUATION_CLASS
: SIDEBAR_MANAGER_LINE_CONTINUATION_CLASS;
Expand Down Expand Up @@ -391,8 +453,10 @@ export interface ManagerThreadGroupRowProps {
managerThreadGroup: ManagerThreadGroup;
selectedThreadId?: string;
isManagerCollapsed: boolean;
collapsedManagerIds: Set<string>;
collapsedEnvironmentIds: Set<string>;
variant: ProjectThreadTreeVariant;
placement?: ManagerThreadGroupPlacement;
onProjectSelect?: () => void;
onToggleManagerCollapsed: (threadId: string) => void;
onToggleEnvironmentCollapsed: (environmentId: string) => void;
Expand Down Expand Up @@ -762,6 +826,7 @@ interface ManagedEnvironmentThreadSubGroupProps {
selectedThreadId?: string;
isCollapsed: boolean;
variant: ProjectThreadTreeVariant;
managerPlacement: ManagerThreadGroupPlacement;
onProjectSelect?: () => void;
onToggleEnvironmentCollapsed: (environmentId: string) => void;
}
Expand All @@ -772,6 +837,7 @@ function ManagedEnvironmentThreadSubGroup({
selectedThreadId,
isCollapsed,
variant,
managerPlacement,
onProjectSelect,
onToggleEnvironmentCollapsed,
}: ManagedEnvironmentThreadSubGroupProps) {
Expand All @@ -795,10 +861,14 @@ function ManagedEnvironmentThreadSubGroup({
<EnvironmentThreadGroupHeader
environmentId={environmentId}
representativeThread={threads[0]}
paddingClass={getProjectThreadTreeManagedEnvHeaderPaddingClass(variant)}
paddingClass={getProjectThreadTreeManagedEnvHeaderPaddingClass(
variant,
managerPlacement,
)}
stickyTier="environment"
parentLineClass={getProjectThreadTreeManagerLineContinuationClassName(
variant,
managerPlacement,
)}
childCount={threads.length}
childActivity={getCollapsedChildActivity(threads)}
Expand All @@ -812,7 +882,10 @@ function ManagedEnvironmentThreadSubGroup({
<div
className={cn(
"relative space-y-px",
getProjectThreadTreeManagedEnvGroupLineClassName(variant),
getProjectThreadTreeManagedEnvGroupLineClassName(
variant,
managerPlacement,
),
)}
>
{threads.map((thread) => (
Expand All @@ -824,6 +897,7 @@ function ManagedEnvironmentThreadSubGroup({
onProjectSelect={onProjectSelect}
options={getProjectThreadTreeEnvGroupedManagedChildOptions(
variant,
managerPlacement,
)}
/>
))}
Expand All @@ -838,8 +912,10 @@ export const ManagerThreadGroupRow = memo(function ManagerThreadGroupRow({
managerThreadGroup,
selectedThreadId,
isManagerCollapsed,
collapsedManagerIds,
collapsedEnvironmentIds,
variant,
placement = "root",
onProjectSelect,
onToggleManagerCollapsed,
onToggleEnvironmentCollapsed,
Expand All @@ -854,7 +930,7 @@ export const ManagerThreadGroupRow = memo(function ManagerThreadGroupRow({
const managerOptions = useMemo<ThreadRowOptions>(
() => ({
kind: "manager",
indent: variant === "section" ? "root" : "project-child",
indent: getProjectThreadTreeManagerIndent(variant, placement),
isCollapsed: isManagerCollapsed,
nestedChildCount,
managedChildActivity: stats.managedChildActivity,
Expand All @@ -868,6 +944,7 @@ export const ManagerThreadGroupRow = memo(function ManagerThreadGroupRow({
isManagerCollapsed,
nestedChildCount,
onToggleManagerCollapsed,
placement,
stats.managedChildActivity,
variant,
],
Expand All @@ -893,7 +970,7 @@ export const ManagerThreadGroupRow = memo(function ManagerThreadGroupRow({
<div
className={cn(
"relative space-y-px",
getProjectThreadTreeChildGroupLineClassName(variant),
getProjectThreadTreeChildGroupLineClassName(variant, placement),
)}
>
{managedItems.map((item) =>
Expand All @@ -904,7 +981,28 @@ export const ManagerThreadGroupRow = memo(function ManagerThreadGroupRow({
thread={item.thread}
isActive={selectedThreadId === item.thread.id}
onProjectSelect={onProjectSelect}
options={getProjectThreadTreeManagedChildOptions(variant)}
options={getProjectThreadTreeManagedChildOptions(
variant,
placement,
)}
/>
) : item.kind === "manager" ? (
<ManagerThreadGroupRow
key={`manager:${item.group.managerThread.id}`}
projectId={projectId}
managerThreadGroup={item.group}
selectedThreadId={selectedThreadId}
isManagerCollapsed={collapsedManagerIds.has(
item.group.managerThread.id,
)}
collapsedManagerIds={collapsedManagerIds}
collapsedEnvironmentIds={collapsedEnvironmentIds}
variant={variant}
placement="managed-child"
onProjectSelect={onProjectSelect}
onToggleManagerCollapsed={onToggleManagerCollapsed}
onToggleEnvironmentCollapsed={onToggleEnvironmentCollapsed}
consumeClickSuppression={consumeClickSuppression}
/>
) : (
<ManagedEnvironmentThreadSubGroup
Expand All @@ -916,6 +1014,7 @@ export const ManagerThreadGroupRow = memo(function ManagerThreadGroupRow({
item.group.environmentId,
)}
variant={variant}
managerPlacement={placement}
onProjectSelect={onProjectSelect}
onToggleEnvironmentCollapsed={onToggleEnvironmentCollapsed}
/>
Expand Down Expand Up @@ -1185,6 +1284,7 @@ export const ProjectThreadTree = memo(function ProjectThreadTree({
isManagerCollapsed={collapsedManagerIds.has(
managerThreadGroup.managerThread.id,
)}
collapsedManagerIds={collapsedManagerIds}
collapsedEnvironmentIds={collapsedEnvironmentIds}
onProjectSelect={onProjectSelect}
onToggleManagerCollapsed={onToggleManagerCollapsed}
Expand All @@ -1205,6 +1305,7 @@ export const ProjectThreadTree = memo(function ProjectThreadTree({
isManagerCollapsed={collapsedManagerIds.has(
managerThreadGroup.managerThread.id,
)}
collapsedManagerIds={collapsedManagerIds}
collapsedEnvironmentIds={collapsedEnvironmentIds}
onProjectSelect={onProjectSelect}
onToggleManagerCollapsed={onToggleManagerCollapsed}
Expand All @@ -1223,6 +1324,23 @@ export const ProjectThreadTree = memo(function ProjectThreadTree({
onProjectSelect={onProjectSelect}
options={getProjectThreadTreeDefaultThreadOptions(variant)}
/>
) : item.kind === "manager" ? (
<ManagerThreadGroupRow
key={`manager:${item.group.managerThread.id}`}
projectId={projectId}
managerThreadGroup={item.group}
selectedThreadId={selectedThreadId}
isManagerCollapsed={collapsedManagerIds.has(
item.group.managerThread.id,
)}
collapsedManagerIds={collapsedManagerIds}
collapsedEnvironmentIds={collapsedEnvironmentIds}
variant={variant}
onProjectSelect={onProjectSelect}
onToggleManagerCollapsed={onToggleManagerCollapsed}
onToggleEnvironmentCollapsed={onToggleEnvironmentCollapsed}
consumeClickSuppression={consumeManagerClickSuppression}
/>
) : (
<EnvironmentThreadGroupRow
key={`env:${item.group.environmentId}`}
Expand Down
Loading