Skip to content
Open
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
39 changes: 39 additions & 0 deletions packages/core/src/command-center/cells.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { Task } from "@posthog/shared/domain-types";
import { describe, expect, it } from "vitest";
import { buildCommandCenterCells } from "./cells";
import { BRAINROT_CELL } from "./grid";

const EMPTY_INPUT = {
taskById: new Map<string, Task>(),
sessionByTaskId: new Map(),
workspaces: undefined,
};

describe("buildCommandCenterCells", () => {
it.each([
{
name: "brainrot sentinel cell has isBrainrot and no task",
input: BRAINROT_CELL,
expected: {
cellIndex: 0,
isBrainrot: true,
taskId: null,
task: undefined,
status: "idle",
},
},
{
name: "empty cell is a non-brainrot empty slot",
input: null,
expected: { isBrainrot: false, taskId: null },
},
{
name: "unknown task id is a non-brainrot cell",
input: "missing",
expected: { isBrainrot: false, taskId: "missing" },
},
])("$name", ({ input, expected }) => {
const [cell] = buildCommandCenterCells([input], EMPTY_INPUT);
expect(cell).toMatchObject(expected);
});
});
Comment thread
richardsolomou marked this conversation as resolved.
20 changes: 19 additions & 1 deletion packages/core/src/command-center/cells.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { AgentSession, WorkspaceMode } from "@posthog/shared";
import type { Task } from "@posthog/shared/domain-types";
import { isBrainrotCell } from "./grid";
import { type CellStatus, deriveStatus, getRepoName } from "./status";

export interface CommandCenterCellData {
Expand All @@ -10,6 +11,8 @@ export interface CommandCenterCellData {
status: CellStatus;
repoName: string | null;
workspaceMode: WorkspaceMode | null;
// Brainrot: a looping video slot rather than a task.
isBrainrot: boolean;
}

export interface BuildCellsInput {
Expand All @@ -23,7 +26,21 @@ export function buildCommandCenterCells(
input: BuildCellsInput,
): CommandCenterCellData[] {
const { taskById, sessionByTaskId, workspaces } = input;
return storeCells.map((taskId, cellIndex) => {
return storeCells.map((cellValue, cellIndex) => {
if (isBrainrotCell(cellValue)) {
return {
cellIndex,
taskId: null,
task: undefined,
session: undefined,
status: "idle" as const,
repoName: null,
workspaceMode: null,
isBrainrot: true,
};
}

const taskId = cellValue;
const task = taskId ? taskById.get(taskId) : undefined;
const session = taskId ? sessionByTaskId.get(taskId) : undefined;
const status = taskId ? deriveStatus(session) : "idle";
Expand All @@ -38,6 +55,7 @@ export function buildCommandCenterCells(
status,
repoName,
workspaceMode,
isBrainrot: false,
};
});
}
12 changes: 12 additions & 0 deletions packages/core/src/command-center/grid.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { describe, expect, it } from "vitest";
import {
BRAINROT_CELL,
clampZoom,
getCellCount,
getCellSessionId,
getGridDimensions,
isBrainrotCell,
resizeCells,
} from "./grid";

Expand Down Expand Up @@ -51,6 +53,16 @@ describe("clampZoom", () => {
});
});

describe("isBrainrotCell", () => {
it.each([
{ value: BRAINROT_CELL, expected: true },
{ value: "some-task-uuid", expected: false },
{ value: null, expected: false },
])("$value -> $expected", ({ value, expected }) => {
expect(isBrainrotCell(value)).toBe(expected);
});
});

describe("getCellSessionId", () => {
it("formats the cell session id", () => {
expect(getCellSessionId(2)).toBe("cc-cell-2");
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/command-center/grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ export const ZOOM_MIN = 0.5;
export const ZOOM_MAX = 1.5;
export const ZOOM_STEP = 0.1;

// Reserved cell value for the Brainrot video slot instead of a task. Real task
// ids are uuids, so this never collides with one.
export const BRAINROT_CELL = "__brainrot__";

export function isBrainrotCell(value: string | null): boolean {
return value === BRAINROT_CELL;
}

export function getGridDimensions(preset: LayoutPreset): GridDimensions {
const [cols, rows] = preset.split("x").map(Number);
return { cols, rows };
Expand Down
9 changes: 9 additions & 0 deletions packages/shared/src/analytics-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ export interface CommandMenuActionProperties {
channel_id?: string;
}

export interface BrainrotActivatedProperties {
/** Grid layout preset, e.g. "2x2". */
layout: string;
/** Cells already holding a task when Brainrot was chosen. */
filled_cells: number;
}

export interface SkillButtonTriggeredProperties {
task_id: string;
button_id: SkillButtonId;
Expand Down Expand Up @@ -961,6 +968,7 @@ export const ANALYTICS_EVENTS = {
COMMAND_MENU_OPENED: "Command menu opened",
COMMAND_MENU_ACTION: "Command menu action",
COMMAND_CENTER_VIEWED: "Command center viewed",
BRAINROT_ACTIVATED: "Brainrot activated",
SKILL_BUTTON_TRIGGERED: "Skill button triggered",
POSTHOG_WEB_OPENED: "PostHog web opened",

Expand Down Expand Up @@ -1104,6 +1112,7 @@ export type EventPropertyMap = {
[ANALYTICS_EVENTS.COMMAND_MENU_OPENED]: never;
[ANALYTICS_EVENTS.COMMAND_MENU_ACTION]: CommandMenuActionProperties;
[ANALYTICS_EVENTS.COMMAND_CENTER_VIEWED]: never;
[ANALYTICS_EVENTS.BRAINROT_ACTIVATED]: BrainrotActivatedProperties;
[ANALYTICS_EVENTS.SKILL_BUTTON_TRIGGERED]: SkillButtonTriggeredProperties;
[ANALYTICS_EVENTS.POSTHOG_WEB_OPENED]: never;

Expand Down
5 changes: 5 additions & 0 deletions packages/ui/src/assets.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ declare module "*.mp3" {
const src: string;
export default src;
}

declare module "*.mp4" {
const src: string;
export default src;
}
Binary file not shown.
Binary file not shown.
45 changes: 45 additions & 0 deletions packages/ui/src/features/command-center/commandCenterStore.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { BRAINROT_CELL } from "@posthog/core/command-center/grid";
import { beforeEach, describe, expect, it, vi } from "vitest";

vi.mock("@posthog/ui/shell/rendererStorage", () => ({
Expand Down Expand Up @@ -87,6 +88,50 @@ describe("commandCenterStore", () => {
});
});

describe("setBrainrotCell", () => {
it("marks the target cell as brainrot without disturbing others", () => {
useCommandCenterStore.setState({ cells: ["t1", null, null, null] });
useCommandCenterStore.getState().setBrainrotCell(2);
expect(useCommandCenterStore.getState().cells).toEqual([
"t1",
null,
BRAINROT_CELL,
null,
]);
});

it("does not dedupe, so multiple cells can be brainrot", () => {
useCommandCenterStore.getState().setBrainrotCell(0);
useCommandCenterStore.getState().setBrainrotCell(1);
expect(useCommandCenterStore.getState().cells).toEqual([
BRAINROT_CELL,
BRAINROT_CELL,
null,
null,
]);
});

it("focuses the cell, clears its creating state, and marks the grid curated", () => {
useCommandCenterStore.setState({ creatingCells: [3] });
useCommandCenterStore.getState().setBrainrotCell(3);
const state = useCommandCenterStore.getState();
expect(state.activeCellIndex).toBe(3);
expect(state.activeTaskId).toBeNull();
expect(state.creatingCells).toEqual([]);
expect(state.hasAutofilled).toBe(true);
});

it("ignores out-of-range indices", () => {
useCommandCenterStore.getState().setBrainrotCell(9);
expect(useCommandCenterStore.getState().cells).toEqual([
null,
null,
null,
null,
]);
});
});

describe("hasAutofilled", () => {
it("assigning a task marks the grid as curated", () => {
useCommandCenterStore.getState().assignTask(0, "t1");
Expand Down
16 changes: 16 additions & 0 deletions packages/ui/src/features/command-center/commandCenterStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
BRAINROT_CELL,
clampZoom,
getCellCount,
type LayoutPreset,
Expand Down Expand Up @@ -31,6 +32,7 @@ interface CommandCenterStoreActions {
setActiveTask: (taskId: string | null) => void;
setActiveCell: (cellIndex: number | null) => void;
assignTask: (cellIndex: number, taskId: string) => void;
setBrainrotCell: (cellIndex: number) => void;
autofillCells: (taskIds: string[]) => void;
removeTask: (cellIndex: number) => void;
removeTaskById: (taskId: string) => void;
Expand Down Expand Up @@ -100,6 +102,20 @@ export const useCommandCenterStore = create<CommandCenterStore>()(
};
}),

setBrainrotCell: (cellIndex) =>
set((state) => {
if (cellIndex < 0 || cellIndex >= state.cells.length) return state;
const cells = [...state.cells];
cells[cellIndex] = BRAINROT_CELL;
return {
cells,
activeTaskId: null,
activeCellIndex: cellIndex,
creatingCells: state.creatingCells.filter((i) => i !== cellIndex),
hasAutofilled: true,
};
}),

autofillCells: (taskIds) =>
set((state) => {
// Grid already full: nothing to place, but the bootstrap is done.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ import {
Desktop,
Folder,
GitFork,
Lightning,
Plus,
X,
} from "@phosphor-icons/react";
import type { WorkspaceMode } from "@posthog/shared";
import { isBrainrotCell } from "@posthog/core/command-center/grid";
import { ANALYTICS_EVENTS, type WorkspaceMode } from "@posthog/shared";
import type { Task } from "@posthog/shared/domain-types";
import { openTask } from "@posthog/ui/router/useOpenTask";
import { track } from "@posthog/ui/shell/analytics";
import { Flex, Text } from "@radix-ui/themes";
import { useCallback, useEffect, useRef, useState } from "react";
import brainrotLandscape from "../../../assets/videos/brainrot-landscape.mp4";
import brainrotPortrait from "../../../assets/videos/brainrot-portrait.mp4";
import { useCloudPrUrl } from "../../git-interaction/useCloudPrUrl";
import { useDraftStore } from "../../message-editor/draftStore";
import { EmbeddedSessionView } from "../../sessions/components/EmbeddedSessionView";
Expand All @@ -23,6 +28,7 @@ import type {
CellStatus,
CommandCenterCellData,
} from "../hooks/useCommandCenterData";
import { useElementOrientation } from "../hooks/useElementOrientation";
import { CommandCenterPRButton } from "./CommandCenterPRButton";
import { TaskSelector } from "./TaskSelector";

Expand Down Expand Up @@ -103,12 +109,23 @@ function EmptyCell({ cellIndex }: { cellIndex: number }) {
s.creatingCells.includes(cellIndex),
);
const assignTask = useCommandCenterStore((s) => s.assignTask);
const setBrainrotCell = useCommandCenterStore((s) => s.setBrainrotCell);
const startCreating = useCommandCenterStore((s) => s.startCreating);
const stopCreating = useCommandCenterStore((s) => s.stopCreating);
const layout = useCommandCenterStore((s) => s.layout);
const cells = useCommandCenterStore((s) => s.cells);
const clearDraft = useDraftStore((s) => s.actions.setDraft);

const sessionId = getCellSessionId(cellIndex);

const handleBrainrot = useCallback(() => {
track(ANALYTICS_EVENTS.BRAINROT_ACTIVATED, {
layout,
filled_cells: cells.filter((c) => c && !isBrainrotCell(c)).length,
});
setBrainrotCell(cellIndex);
}, [layout, cells, setBrainrotCell, cellIndex]);

const handleTaskCreated = useCallback(
(task: Task) => {
assignTask(cellIndex, task.id);
Expand Down Expand Up @@ -167,6 +184,7 @@ function EmptyCell({ cellIndex }: { cellIndex: number }) {
open={selectorOpen}
onOpenChange={setSelectorOpen}
onNewTask={() => startCreating(cellIndex)}
onBrainrot={handleBrainrot}
>
<button
type="button"
Expand All @@ -185,6 +203,50 @@ function EmptyCell({ cellIndex }: { cellIndex: number }) {
);
}

function BrainrotCell({ cellIndex }: { cellIndex: number }) {
const removeTask = useCommandCenterStore((s) => s.removeTask);
const stageRef = useRef<HTMLDivElement>(null);
const orientation = useElementOrientation(stageRef);
const src = orientation === "portrait" ? brainrotPortrait : brainrotLandscape;

return (
<Flex direction="column" height="100%">
<Flex
align="center"
gap="2"
px="2"
py="1"
className="shrink-0 border-gray-6 border-b"
>
<Lightning size={12} weight="fill" className="shrink-0 text-amber-9" />
<Text className="min-w-0 flex-1 truncate font-medium text-[12px]">
Brainrot
</Text>
<button
type="button"
onClick={() => removeTask(cellIndex)}
className="flex h-5 w-5 shrink-0 items-center justify-center rounded text-gray-10 transition-colors hover:bg-gray-4 hover:text-gray-12"
title="Remove from grid"
>
<X size={12} />
</button>
</Flex>
<div ref={stageRef} className="min-h-0 flex-1 overflow-hidden bg-black">
<video
key={orientation}
src={src}
aria-label="Brainrot"
autoPlay
loop
muted
playsInline
className="h-full w-full object-contain"
/>
</div>
</Flex>
);
}

function PopulatedCell({
cell,
isActiveSession,
Expand Down Expand Up @@ -263,6 +325,10 @@ export function CommandCenterPanel({
cell,
isActiveSession,
}: CommandCenterPanelProps) {
if (cell.isBrainrot) {
return <BrainrotCell cellIndex={cell.cellIndex} />;
}

if (!cell.taskId || !cell.task) {
return <EmptyCell cellIndex={cell.cellIndex} />;
}
Expand Down
Loading
Loading