Skip to content
Merged
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
43 changes: 8 additions & 35 deletions src/browser/stories/App.demo.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,49 +17,22 @@ import {
createStatusTool,
createStaticChatHandler,
createStreamingChatHandler,
createGitStatusOutput,
type GitStatusFixture,
} from "./mockFactory";
import { selectWorkspace, setWorkspaceInput, setWorkspaceModel } from "./storyHelpers";
import {
createGitStatusExecutor,
createOnChatAdapter,
type ChatHandler,
selectWorkspace,
setWorkspaceInput,
setWorkspaceModel,
} from "./storyHelpers";
import { createMockORPCClient } from "../../../.storybook/mocks/orpc";
import type { WorkspaceChatMessage } from "@/common/orpc/types";

export default {
...appMeta,
title: "App/Demo",
};

type ChatHandler = (callback: (event: WorkspaceChatMessage) => void) => () => void;

/** Adapts callback-based chat handlers to ORPC onChat format */
function createOnChatAdapter(chatHandlers: Map<string, ChatHandler>) {
return (workspaceId: string, emit: (msg: WorkspaceChatMessage) => void) => {
const handler = chatHandlers.get(workspaceId);
if (handler) {
return handler(emit);
}
queueMicrotask(() => emit({ type: "caught-up" }));
return undefined;
};
}

/** Creates an executeBash function that returns git status output for workspaces */
function createGitStatusExecutor(gitStatus: Map<string, GitStatusFixture>) {
return (workspaceId: string, script: string) => {
if (script.includes("git status") || script.includes("git show-branch")) {
const status = gitStatus.get(workspaceId) ?? {};
const output = createGitStatusOutput(status);
return Promise.resolve({ success: true as const, output, exitCode: 0, wall_duration_ms: 50 });
}
return Promise.resolve({
success: true as const,
output: "",
exitCode: 0,
wall_duration_ms: 0,
});
};
}

/**
* Comprehensive story showing all sidebar indicators and chat features.
*
Expand Down
30 changes: 13 additions & 17 deletions src/browser/stories/App.sidebar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,29 @@ import {
createGitStatusOutput,
type GitStatusFixture,
} from "./mockFactory";
import { expandProjects } from "./storyHelpers";
import {
clearWorkspaceSelection,
createOnChatAdapter,
type ChatHandler,
expandProjects,
} from "./storyHelpers";
import { GIT_STATUS_INDICATOR_MODE_KEY } from "@/common/constants/storage";
import { within, userEvent, waitFor } from "@storybook/test";

import { createMockORPCClient } from "../../../.storybook/mocks/orpc";

import type { WorkspaceChatMessage } from "@/common/orpc/types";

export default {
...appMeta,
title: "App/Sidebar",
decorators: [
(Story: () => JSX.Element) => {
// Sidebar stories are about list organization; keep the main panel unselected.
clearWorkspaceSelection();
return <Story />;
},
],
};

type ChatHandler = (callback: (event: WorkspaceChatMessage) => void) => () => void;

/** Adapts callback-based chat handlers to ORPC onChat format */
function createOnChatAdapter(chatHandlers: Map<string, ChatHandler>) {
return (workspaceId: string, emit: (msg: WorkspaceChatMessage) => void) => {
const handler = chatHandlers.get(workspaceId);
if (handler) {
return handler(emit);
}
queueMicrotask(() => emit({ type: "caught-up" }));
return undefined;
};
}

/**
* Creates an executeBash function that returns deterministic git outputs for Storybook.
*
Expand Down
11 changes: 8 additions & 3 deletions src/browser/stories/storyHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export function selectWorkspace(workspace: FrontendWorkspaceMetadata): void {
);
}

/** Clear workspace selection from localStorage (for sidebar-focused stories) */
export function clearWorkspaceSelection(): void {
localStorage.removeItem(SELECTED_WORKSPACE_KEY);
}

/** Set input text for a workspace */
export function setWorkspaceInput(workspaceId: string, text: string): void {
localStorage.setItem(getInputKey(workspaceId), text);
Expand Down Expand Up @@ -98,7 +103,7 @@ export function createReview(
// ═══════════════════════════════════════════════════════════════════════════════

/** Creates an executeBash function that returns git status output for workspaces */
function createGitStatusExecutor(gitStatus?: Map<string, GitStatusFixture>) {
export function createGitStatusExecutor(gitStatus?: Map<string, GitStatusFixture>) {
return (workspaceId: string, script: string) => {
if (script.includes("git status") || script.includes("git show-branch")) {
const status = gitStatus?.get(workspaceId) ?? {};
Expand All @@ -118,10 +123,10 @@ function createGitStatusExecutor(gitStatus?: Map<string, GitStatusFixture>) {
// CHAT HANDLER ADAPTER
// ═══════════════════════════════════════════════════════════════════════════════

type ChatHandler = (callback: (event: WorkspaceChatMessage) => void) => () => void;
export type ChatHandler = (callback: (event: WorkspaceChatMessage) => void) => () => void;

/** Adapts callback-based chat handlers to ORPC onChat format */
function createOnChatAdapter(chatHandlers: Map<string, ChatHandler>) {
export function createOnChatAdapter(chatHandlers: Map<string, ChatHandler>) {
return (workspaceId: string, emit: (msg: WorkspaceChatMessage) => void) => {
const handler = chatHandlers.get(workspaceId);
if (handler) {
Expand Down