From ed553008989978a12a3164ef79c15d1f7ba0ec3c Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 16 Nov 2025 11:51:03 -0500 Subject: [PATCH 1/3] Add stories for the review panel --- .../CodeReview/ReviewPanel.stories.tsx | 491 ++++++++++++++++++ src/node/services/ptyService.ts | 2 +- 2 files changed, 492 insertions(+), 1 deletion(-) create mode 100644 src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx diff --git a/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx b/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx new file mode 100644 index 0000000000..7a2f3c9db6 --- /dev/null +++ b/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx @@ -0,0 +1,491 @@ +import React, { useRef } from "react"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { ReviewPanel } from "./ReviewPanel"; +import type { IPCApi } from "@/common/types/ipc"; +import { deleteWorkspaceStorage } from "@/common/constants/storage"; +import type { BashToolResult } from "@/common/types/tools"; +import type { Result } from "@/common/types/result"; + +type ScenarioName = "rich" | "empty" | "truncated"; + +interface ScenarioConfig { + name: ScenarioName; + workspaceId: string; + workspacePath: string; + diffByFile: Record; + numstatOutput: string; + untrackedFiles: string[]; + truncated?: { + reason: string; + totalLines: number; + }; +} + +const joinLines = (lines: string[]): string => lines.join("\n"); + +const reviewPanelDiff = joinLines([ + "diff --git a/src/browser/components/RightSidebar/CodeReview/ReviewPanel.tsx b/src/browser/components/RightSidebar/CodeReview/ReviewPanel.tsx", + "index 4b825dc..1c002b1 100644", + "--- a/src/browser/components/RightSidebar/CodeReview/ReviewPanel.tsx", + "+++ b/src/browser/components/RightSidebar/CodeReview/ReviewPanel.tsx", + "@@ -123,6 +123,13 @@ export const ReviewPanel = ({", + " const [selectedHunkId, setSelectedHunkId] = useState(null);", + " const [isLoadingHunks, setIsLoadingHunks] = useState(true);", + " const [isLoadingTree, setIsLoadingTree] = useState(true);", + "+ const [lastInteractionTimestamp, setLastInteractionTimestamp] = useState(() => Date.now());", + "+", + "+ useEffect(() => {", + "+ setLastInteractionTimestamp(Date.now());", + "+ }, [selectedFilePath, debouncedSearchTerm]);", + "+", + "+ const idleForMs = Date.now() - lastInteractionTimestamp;", + " const [error, setError] = useState(null);", + "", + "@@ -410,7 +417,17 @@ export const ReviewPanel = ({", + " const handleRefresh = () => {", + "- setRefreshTrigger((prev) => prev + 1);", + "+ setRefreshTrigger((prev) => prev + 1);", + "+ if (idleForMs > 5000) {", + "+ console.debug(\"ReviewPanel idle refresh\", { workspaceId, idleForMs });", + "+ }", + "+ if (typeof window !== \"undefined\") {", + "+ window.dispatchEvent(", + "+ new CustomEvent(\"review-panel:refresh\", { detail: { workspaceId, idleForMs } })", + "+ );", + "+ }", + " };", + "", + "@@ -642,6 +656,14 @@ export const ReviewPanel = ({", + "-
", + "+
", + "
", + "+ ", + "+ Search", + "+ ", + " ", + " ", + "
", + " )}", +]); + +const hunkViewerDiff = joinLines([ + "diff --git a/src/browser/components/RightSidebar/CodeReview/HunkViewer.tsx b/src/browser/components/RightSidebar/CodeReview/HunkViewer.tsx", + "index 6c1d2e3..9f0a1b2 100644", + "--- a/src/browser/components/RightSidebar/CodeReview/HunkViewer.tsx", + "+++ b/src/browser/components/RightSidebar/CodeReview/HunkViewer.tsx", + "@@ -49,6 +49,7 @@ export const HunkViewer = React.memo(", + " // Track if hunk is visible in viewport for lazy syntax highlighting", + " const isVisibleRef = React.useRef(true); // Start visible to avoid flash", + " const [isVisible, setIsVisible] = React.useState(true);", + "+ const [isPinned, setIsPinned] = React.useState(false);", + "", + "@@ -150,6 +151,13 @@ export const HunkViewer = React.memo(", + " const handleToggleExpand = React.useCallback(", + " (e?: React.MouseEvent) => {", + " e?.stopPropagation();", + " const newExpandState = !isExpanded;", + " setIsExpanded(newExpandState);", + " // Persist manual expand/collapse choice", + " setExpandStateMap((prev) => ({", + " ...prev,", + " [hunkId]: newExpandState,", + " }));", + " },", + " [isExpanded, hunkId, setExpandStateMap]", + " );", + "+", + "+ const handlePinToggle = React.useCallback((e: React.MouseEvent) => {", + "+ e.stopPropagation();", + "+ setIsPinned((prev) => !prev);", + "+ }, []);", + "", + "@@ -182,7 +190,8 @@ export const HunkViewer = React.memo(", + " className={cn(", + " \"bg-dark border rounded mb-3 overflow-hidden cursor-pointer transition-all duration-200\",", + " \"focus:outline-none focus-visible:outline-none\",", + " isRead ? \"border-read\" : \"border-border-light\",", + "- isSelected && \"border-review-accent shadow-[0_0_0_1px_var(--color-review-accent)]\"", + "+ isSelected && \"border-review-accent shadow-[0_0_0_1px_var(--color-review-accent)]\",", + "+ isPinned && \"ring-1 ring-review-accent/70\"", + " )}", + "", + "@@ -206,6 +215,18 @@ export const HunkViewer = React.memo(", + "
", + "+ ", + "+ ", + "+ {isPinned ? \"Pinned\" : \"Pin\"}", + "+ ", + "+ ", + "+ Keep this hunk expanded while scrolling", + "+ ", + "+ ", + " {!isPureRename && (", +]); + +const useReviewStateDiff = joinLines([ + "diff --git a/src/browser/hooks/useReviewState.ts b/src/browser/hooks/useReviewState.ts", + "index 1234567..89abcde 100644", + "--- a/src/browser/hooks/useReviewState.ts", + "+++ b/src/browser/hooks/useReviewState.ts", + "@@ -43,6 +43,7 @@ export interface UseReviewStateReturn {", + " /** Mark one or more hunks as read */", + " markAsRead: (hunkIds: string | string[]) => void;", + " /** Mark a hunk as unread */", + " markAsUnread: (hunkId: string) => void;", + "+ /** Mark several hunks as unread without multiple re-renders */", + "+ markManyAsUnread: (hunkIds: string[]) => void;", + " /** Toggle read state of a hunk */", + " toggleRead: (hunkId: string) => void;", + "", + "@@ -133,6 +134,33 @@ export function useReviewState(workspaceId: string): UseReviewStateReturn {", + " const markAsUnread = useCallback(", + " (hunkId: string) => {", + " setReviewState((prev) => {", + " // Early return if not currently read", + " if (!prev.readState[hunkId]) return prev;", + "", + " const { [hunkId]: _, ...rest } = prev.readState;", + " return {", + " ...prev,", + " readState: rest,", + " lastUpdated: Date.now(),", + " };", + " });", + " },", + " [setReviewState]", + " );", + "+", + "+ const markManyAsUnread = useCallback(", + "+ (hunkIds: string[]) => {", + "+ if (hunkIds.length === 0) return;", + "+ setReviewState((prev) => {", + "+ const nextState = { ...prev.readState };", + "+ let changed = false;", + "+ for (const id of hunkIds) {", + "+ if (nextState[id]) {", + "+ delete nextState[id];", + "+ changed = true;", + "+ }", + "+ }", + "+ if (!changed) return prev;", + "+ return {", + "+ ...prev,", + "+ readState: nextState,", + "+ lastUpdated: Date.now(),", + "+ };", + "+ });", + "+ },", + "+ [setReviewState]", + "+ );", + "", + "@@ -183,6 +211,7 @@ export function useReviewState(workspaceId: string): UseReviewStateReturn {", + " return {", + " isRead,", + " markAsRead,", + " markAsUnread,", + "+ markManyAsUnread,", + " toggleRead,", + " clearAll,", + " readCount,", + " };", +]); + +const codeReviewCssDiff = joinLines([ + "diff --git a/src/styles/codeReview.css b/src/styles/codeReview.css", + "index 13579bd..2468ace 100644", + "--- a/src/styles/codeReview.css", + "+++ b/src/styles/codeReview.css", + "@@ -12,6 +12,24 @@ .code-review-panel {", + " scrollbar-color: var(--color-border-light) transparent;", + " }", + "+", + "+.code-review-panel .search-header {", + "+ position: sticky;", + "+ top: 0;", + "+ z-index: 10;", + "+ padding-block: 6px;", + "+ background: linear-gradient(180deg, rgba(12, 13, 17, 0.95), rgba(12, 13, 17, 0.6));", + "+ backdrop-filter: blur(12px);", + "+}", + "+", + "+.code-review-panel .file-tree-row.is-filtered {", + "+ color: var(--color-review-accent);", + "+ font-weight: 600;", + "+}", + "+", + "+.code-review-panel .hunk-pinned {", + "+ outline: 1px solid var(--color-review-accent);", + "+ outline-offset: 2px;", + "+ background-color: rgba(77, 184, 255, 0.05);", + "+}", +]); + +const docChecklistDiff = joinLines([ + "diff --git a/docs/review-checklist.md b/docs/review-checklist.md", + "new file mode 100644", + "index 0000000..1f5e3d4", + "--- /dev/null", + "+++ b/docs/review-checklist.md", + "@@ -0,0 +1,24 @@", + "+# Code Review Checklist", + "+", + "+Use this checklist when triaging large diffs inside the mux Code Review panel:", + "+", + "+1. Confirm keyboard shortcuts render in tooltips for every actionable item.", + "+2. Verify truncation warnings show up before the hunks and provide remediation steps.", + "+3. Ensure pinned hunks retain their state after scrolling or reloading.", + "+4. Confirm file filters highlight the active directory inside the tree.", + "+5. Skim docs and tests for every file with 50+ additions.", + "+", + "+## Reviewer Notes", + "+", + "+- Capture high-level summary of risky areas.", + "+- Note any follow-up tasks for documentation or monitoring.", + "+- Cross-link related tickets or incidents.", + "+", + "+## Sign-Off", + "+", + "+- [ ] Tests added or explicitly deemed unnecessary.", + "+- [ ] Accessibility impact reviewed.", + "+- [ ] Performance implications measured for hot paths.", + "+- [ ] Rollback plan documented.", +]); + +const themeRenameDiff = joinLines([ + "diff --git a/src/styles/theme.css b/src/styles/theme.scss", + "index 5c4b3a2..5c4b3a2 100644", + "similarity index 100%", + "rename from src/styles/theme.css", + "rename to src/styles/theme.scss", + "@@ -0,0 +0,0 @@", +]); + +const packageJsonDiff = joinLines([ + "diff --git a/package.json b/package.json", + "index a1b2c3d..d4c3b2a 100644", + "--- a/package.json", + "+++ b/package.json", + "@@ -5,7 +5,8 @@", + " \"name\": \"mux\",", + " \"scripts\": {", + "- \"storybook\": \"storybook dev -p 6006\"", + "+ \"storybook\": \"storybook dev -p 6006\",", + "+ \"storybook:code-review\": \"storybook dev -p 6006 --docs\"", + " },", + " \"devDependencies\": {", + "@@ -23,4 +24,5 @@", + " \"lint-staged\": {},", + "+ \"codeReviewPanel\": \"storybook\"", + " }", +]); + +const richDiffByFile: Record = { + "src/browser/components/RightSidebar/CodeReview/ReviewPanel.tsx": reviewPanelDiff, + "src/browser/components/RightSidebar/CodeReview/HunkViewer.tsx": hunkViewerDiff, + "src/browser/hooks/useReviewState.ts": useReviewStateDiff, + "src/styles/codeReview.css": codeReviewCssDiff, + "docs/review-checklist.md": docChecklistDiff, + "src/styles/theme.scss": themeRenameDiff, + "package.json": packageJsonDiff, +}; + +const richNumstat = [ + "72\t14\tsrc/browser/components/RightSidebar/CodeReview/ReviewPanel.tsx", + "34\t6\tsrc/browser/components/RightSidebar/CodeReview/HunkViewer.tsx", + "24\t3\tsrc/browser/hooks/useReviewState.ts", + "27\t0\tsrc/styles/codeReview.css", + "48\t0\tdocs/review-checklist.md", + "0\t0\tsrc/styles/{theme.css => theme.scss}", + "4\t1\tpackage.json", +].join("\n"); + +const WORKSPACE_PATH = "/home/user/projects/mux"; + +const scenarioConfigs: Record = { + rich: { + name: "rich", + workspaceId: "storybook-review-rich", + workspacePath: WORKSPACE_PATH, + diffByFile: richDiffByFile, + numstatOutput: richNumstat, + untrackedFiles: ["notes/review-followups.md", "scripts/smoke-review.sh"], + }, + truncated: { + name: "truncated", + workspaceId: "storybook-review-truncated", + workspacePath: WORKSPACE_PATH, + diffByFile: richDiffByFile, + numstatOutput: richNumstat, + untrackedFiles: ["notes/review-followups.md"], + truncated: { + reason: "terminal buffer limit (storybook fixture)", + totalLines: 2400, + }, + }, + empty: { + name: "empty", + workspaceId: "storybook-review-empty", + workspacePath: WORKSPACE_PATH, + diffByFile: {}, + numstatOutput: "", + untrackedFiles: ["playground/spike.ts"], + }, +}; + +function createSuccessResult( + output: string, + overrides?: { truncated?: { reason: string; totalLines: number } } +): Result { + return { + success: true as const, + data: { + success: true as const, + output, + exitCode: 0, + wall_duration_ms: 5, + ...overrides, + }, + }; +} + +function setupCodeReviewMocks(config: ScenarioConfig) { + const executeBash: IPCApi["workspace"]["executeBash"] = async (_workspaceId, command, _options) => { + if (command.includes("git ls-files --others --exclude-standard")) { + return createSuccessResult(config.untrackedFiles.join("\n")); + } + + if (command.includes("--numstat")) { + return createSuccessResult(config.numstatOutput); + } + + if (command.includes("git add --")) { + return createSuccessResult(""); + } + + if (command.startsWith("git diff") || command.includes("git diff ")) { + const pathMatch = command.match(/ -- "([^"]+)"/); + const pathFilter = pathMatch?.[1]; + const diffOutput = pathFilter + ? config.diffByFile[pathFilter] ?? "" + : Object.values(config.diffByFile) + .filter(Boolean) + .join("\n\n"); + + const truncated = !pathFilter && config.truncated ? { truncated: config.truncated } : undefined; + return createSuccessResult(diffOutput, truncated); + } + + return createSuccessResult(""); + }; + + const mockApi = { + workspace: { + executeBash, + }, + platform: "browser", + versions: { + node: "18.18.0", + chrome: "120.0.0.0", + electron: "28.0.0", + }, + } as unknown as IPCApi; + + // @ts-expect-error - mockApi is not typed correctly + // eslint-disable-next-line no-restricted-syntax + window.api = mockApi; + + deleteWorkspaceStorage(config.workspaceId); + localStorage.removeItem(`review-diff-base:${config.workspaceId}`); + localStorage.removeItem(`review-file-filter:${config.workspaceId}`); + localStorage.setItem("review-default-base", "HEAD"); + localStorage.setItem("review-include-uncommitted", "false"); + localStorage.setItem("review-show-read", "true"); +} + +const ReviewPanelStoryWrapper: React.FC<{ scenario: ScenarioName }> = ({ scenario }) => { + const initialized = useRef(false); + const config = scenarioConfigs[scenario]; + + if (!initialized.current) { + setupCodeReviewMocks(config); + initialized.current = true; + } + + return ( +
+ +
+ ); +}; + +const meta = { + title: "Panels/Code Review/ReviewPanel", + component: ReviewPanel, + parameters: { + layout: "fullscreen", + backgrounds: { + default: "panel", + values: [{ name: "panel", value: "#050505" }], + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const RichContent: Story = { + name: "Rich Content", + args: { + workspaceId: scenarioConfigs.rich.workspaceId, + workspacePath: scenarioConfigs.rich.workspacePath, + }, + render: () => , +}; + +export const TruncatedDiff: Story = { + name: "Truncated Diff", + args: { + workspaceId: scenarioConfigs.truncated.workspaceId, + workspacePath: scenarioConfigs.truncated.workspacePath, + }, + render: () => , +}; + +export const EmptyState: Story = { + name: "Empty State", + args: { + workspaceId: scenarioConfigs.empty.workspaceId, + workspacePath: scenarioConfigs.empty.workspacePath, + }, + render: () => , +}; + diff --git a/src/node/services/ptyService.ts b/src/node/services/ptyService.ts index 2c1994cced..81049215d3 100644 --- a/src/node/services/ptyService.ts +++ b/src/node/services/ptyService.ts @@ -12,7 +12,7 @@ import type { TerminalCreateParams, TerminalResizeParams, } from "@/common/types/terminal"; -import type { IPty } from "node-pty"; +import type { IPty } from "@homebridge/node-pty-prebuilt-multiarch"; import { SSHRuntime, type SSHRuntimeConfig } from "@/node/runtime/SSHRuntime"; import { LocalRuntime } from "@/node/runtime/LocalRuntime"; import { access } from "fs/promises"; From 5ec6d81631effdd049e5f487eb4d0385338a5fea Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 16 Nov 2025 12:00:30 -0500 Subject: [PATCH 2/3] Fix linting --- .../CodeReview/ReviewPanel.stories.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx b/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx index 7a2f3c9db6..c7904d7181 100644 --- a/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx +++ b/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx @@ -370,21 +370,22 @@ function createSuccessResult( } function setupCodeReviewMocks(config: ScenarioConfig) { - const executeBash: IPCApi["workspace"]["executeBash"] = async (_workspaceId, command, _options) => { + const executeBash: IPCApi["workspace"]["executeBash"] = (_workspaceId, command) => { if (command.includes("git ls-files --others --exclude-standard")) { - return createSuccessResult(config.untrackedFiles.join("\n")); + return Promise.resolve(createSuccessResult(config.untrackedFiles.join("\n"))); } if (command.includes("--numstat")) { - return createSuccessResult(config.numstatOutput); + return Promise.resolve(createSuccessResult(config.numstatOutput)); } if (command.includes("git add --")) { - return createSuccessResult(""); + return Promise.resolve(createSuccessResult("")); } if (command.startsWith("git diff") || command.includes("git diff ")) { - const pathMatch = command.match(/ -- "([^"]+)"/); + const pathRegex = / -- "([^"]+)"/; + const pathMatch = pathRegex.exec(command); const pathFilter = pathMatch?.[1]; const diffOutput = pathFilter ? config.diffByFile[pathFilter] ?? "" @@ -393,10 +394,10 @@ function setupCodeReviewMocks(config: ScenarioConfig) { .join("\n\n"); const truncated = !pathFilter && config.truncated ? { truncated: config.truncated } : undefined; - return createSuccessResult(diffOutput, truncated); + return Promise.resolve(createSuccessResult(diffOutput, truncated)); } - return createSuccessResult(""); + return Promise.resolve(createSuccessResult("")); }; const mockApi = { @@ -412,7 +413,6 @@ function setupCodeReviewMocks(config: ScenarioConfig) { } as unknown as IPCApi; // @ts-expect-error - mockApi is not typed correctly - // eslint-disable-next-line no-restricted-syntax window.api = mockApi; deleteWorkspaceStorage(config.workspaceId); @@ -463,7 +463,6 @@ export default meta; type Story = StoryObj; export const RichContent: Story = { - name: "Rich Content", args: { workspaceId: scenarioConfigs.rich.workspaceId, workspacePath: scenarioConfigs.rich.workspacePath, @@ -472,7 +471,6 @@ export const RichContent: Story = { }; export const TruncatedDiff: Story = { - name: "Truncated Diff", args: { workspaceId: scenarioConfigs.truncated.workspaceId, workspacePath: scenarioConfigs.truncated.workspacePath, @@ -481,7 +479,6 @@ export const TruncatedDiff: Story = { }; export const EmptyState: Story = { - name: "Empty State", args: { workspaceId: scenarioConfigs.empty.workspaceId, workspacePath: scenarioConfigs.empty.workspacePath, From f7b86b915bc8c2159bcbf93222ed315df252ea6c Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 16 Nov 2025 12:02:47 -0500 Subject: [PATCH 3/3] Fix fmt --- .../CodeReview/ReviewPanel.stories.tsx | 70 +++++++++---------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx b/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx index c7904d7181..67f5a9794a 100644 --- a/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx +++ b/src/browser/components/RightSidebar/CodeReview/ReviewPanel.stories.tsx @@ -46,34 +46,34 @@ const reviewPanelDiff = joinLines([ "- setRefreshTrigger((prev) => prev + 1);", "+ setRefreshTrigger((prev) => prev + 1);", "+ if (idleForMs > 5000) {", - "+ console.debug(\"ReviewPanel idle refresh\", { workspaceId, idleForMs });", + '+ console.debug("ReviewPanel idle refresh", { workspaceId, idleForMs });', "+ }", - "+ if (typeof window !== \"undefined\") {", + '+ if (typeof window !== "undefined") {', "+ window.dispatchEvent(", - "+ new CustomEvent(\"review-panel:refresh\", { detail: { workspaceId, idleForMs } })", + '+ new CustomEvent("review-panel:refresh", { detail: { workspaceId, idleForMs } })', "+ );", "+ }", " };", "", "@@ -642,6 +656,14 @@ export const ReviewPanel = ({", - "-
", - "+
", - "
", - "+ ", + '-
', + '+
', + '
', + '+ ', "+ Search", "+ ", " ", + '
', " ", @@ -114,29 +114,29 @@ const hunkViewerDiff = joinLines([ "", "@@ -182,7 +190,8 @@ export const HunkViewer = React.memo(", " className={cn(", - " \"bg-dark border rounded mb-3 overflow-hidden cursor-pointer transition-all duration-200\",", - " \"focus:outline-none focus-visible:outline-none\",", - " isRead ? \"border-read\" : \"border-border-light\",", - "- isSelected && \"border-review-accent shadow-[0_0_0_1px_var(--color-review-accent)]\"", - "+ isSelected && \"border-review-accent shadow-[0_0_0_1px_var(--color-review-accent)]\",", - "+ isPinned && \"ring-1 ring-review-accent/70\"", + ' "bg-dark border rounded mb-3 overflow-hidden cursor-pointer transition-all duration-200",', + ' "focus:outline-none focus-visible:outline-none",', + ' isRead ? "border-read" : "border-border-light",', + '- isSelected && "border-review-accent shadow-[0_0_0_1px_var(--color-review-accent)]"', + '+ isSelected && "border-review-accent shadow-[0_0_0_1px_var(--color-review-accent)]",', + '+ isPinned && "ring-1 ring-review-accent/70"', " )}", "", "@@ -206,6 +215,18 @@ export const HunkViewer = React.memo(", - "
", + '
', "+ ", "+ ", - "+ {isPinned ? \"Pinned\" : \"Pin\"}", + '+ {isPinned ? "Pinned" : "Pin"}', "+ ", - "+ ", + '+ ', "+ Keep this hunk expanded while scrolling", "+ ", "+ ", @@ -287,16 +287,16 @@ const packageJsonDiff = joinLines([ "--- a/package.json", "+++ b/package.json", "@@ -5,7 +5,8 @@", - " \"name\": \"mux\",", - " \"scripts\": {", - "- \"storybook\": \"storybook dev -p 6006\"", - "+ \"storybook\": \"storybook dev -p 6006\",", - "+ \"storybook:code-review\": \"storybook dev -p 6006 --docs\"", + ' "name": "mux",', + ' "scripts": {', + '- "storybook": "storybook dev -p 6006"', + '+ "storybook": "storybook dev -p 6006",', + '+ "storybook:code-review": "storybook dev -p 6006 --docs"', " },", - " \"devDependencies\": {", + ' "devDependencies": {', "@@ -23,4 +24,5 @@", - " \"lint-staged\": {},", - "+ \"codeReviewPanel\": \"storybook\"", + ' "lint-staged": {},', + '+ "codeReviewPanel": "storybook"', " }", ]); @@ -388,12 +388,11 @@ function setupCodeReviewMocks(config: ScenarioConfig) { const pathMatch = pathRegex.exec(command); const pathFilter = pathMatch?.[1]; const diffOutput = pathFilter - ? config.diffByFile[pathFilter] ?? "" - : Object.values(config.diffByFile) - .filter(Boolean) - .join("\n\n"); + ? (config.diffByFile[pathFilter] ?? "") + : Object.values(config.diffByFile).filter(Boolean).join("\n\n"); - const truncated = !pathFilter && config.truncated ? { truncated: config.truncated } : undefined; + const truncated = + !pathFilter && config.truncated ? { truncated: config.truncated } : undefined; return Promise.resolve(createSuccessResult(diffOutput, truncated)); } @@ -485,4 +484,3 @@ export const EmptyState: Story = { }, render: () => , }; -