mirror of
https://github.com/block/buzz.git
synced 2026-10-28 21:11:48 +00:00
Show task summary above conversation and distinguish sidebar PRs
Signed-off-by: John Tennant <jtennant@squareup.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import * as React from "react";
|
||||
import type * as React from "react";
|
||||
import { toast } from "sonner";
|
||||
import { useCanvasQuery } from "@/features/channels/hooks";
|
||||
import {
|
||||
@@ -68,49 +68,26 @@ export function TaskChannelWorkspace({
|
||||
}) {
|
||||
const canvas = useCanvasQuery(channelId, channelId !== null);
|
||||
const task = parseChannelBackedTask(canvas.data?.content);
|
||||
const [view, setView] = React.useState<"overview" | "conversation">(
|
||||
"overview",
|
||||
);
|
||||
if (!task) return children;
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 flex-col">
|
||||
<nav
|
||||
aria-label="Task views"
|
||||
<div
|
||||
className={cn(
|
||||
channelChrome.contentPadding,
|
||||
"flex gap-1 border-b px-5 pb-2",
|
||||
"shrink-0 border-b px-5 pb-3",
|
||||
)}
|
||||
data-testid="task-view-tabs"
|
||||
>
|
||||
{(["overview", "conversation"] as const).map((item) => (
|
||||
<button
|
||||
key={item}
|
||||
type="button"
|
||||
aria-current={view === item ? "page" : undefined}
|
||||
data-testid={`task-view-${item}`}
|
||||
className={cn(
|
||||
"rounded-full px-3 py-1.5 text-xs capitalize hover:bg-muted",
|
||||
view === item && "bg-muted",
|
||||
)}
|
||||
onClick={() => setView(item)}
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
{view === "conversation" ? (
|
||||
children
|
||||
) : (
|
||||
<div className="min-h-0 flex-1 overflow-y-auto">
|
||||
<details open data-testid="task-overview">
|
||||
<summary className="cursor-pointer text-sm font-medium">
|
||||
{task.task.title}
|
||||
</summary>
|
||||
<div
|
||||
className="mx-auto max-w-3xl space-y-6 px-6 py-8"
|
||||
data-testid="task-overview"
|
||||
className="mt-2 max-h-48 space-y-2 overflow-y-auto text-sm"
|
||||
data-testid="task-overview-details"
|
||||
>
|
||||
<h2 className="text-xl font-semibold">{task.task.title}</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{task.task.description}
|
||||
</p>
|
||||
<div className="space-y-2 text-sm">
|
||||
<p className="text-muted-foreground">{task.task.description}</p>
|
||||
{task.branch && <BranchStatus branch={task.branch} />}
|
||||
<div className="flex flex-wrap gap-x-6 gap-y-2 text-xs">
|
||||
<div>
|
||||
Origin <Markdown content={task.originatingThread} />
|
||||
</div>
|
||||
@@ -118,10 +95,17 @@ export function TaskChannelWorkspace({
|
||||
Parent <Markdown content={task.parentChannel} />
|
||||
</div>
|
||||
</div>
|
||||
{task.branch && <BranchStatus branch={task.branch} />}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</details>
|
||||
</div>
|
||||
<div
|
||||
className="flex min-h-0 flex-1 flex-col"
|
||||
style={
|
||||
{ "--buzz-channel-content-top-padding": "0px" } as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useContext } from "react";
|
||||
import { CircleAlert } from "lucide-react";
|
||||
import { TaskChannelBranches } from "@/features/channels/useChannelBackedTaskParents";
|
||||
import { useTaskBranchStatus } from "@/features/channels/useTaskBranchStatus";
|
||||
import type { ChannelBackedTask } from "@/features/channels/lib/channelBackedTask";
|
||||
@@ -17,11 +18,36 @@ function BranchGlyph({
|
||||
className="ml-auto flex shrink-0"
|
||||
data-testid="task-channel-status"
|
||||
>
|
||||
<Icon
|
||||
role="img"
|
||||
aria-label={description}
|
||||
className={cn("h-3.5 w-3.5", color)}
|
||||
/>
|
||||
{query.error && !pr ? (
|
||||
<CircleAlert
|
||||
role="img"
|
||||
aria-label={description}
|
||||
className="h-3.5 w-3.5 text-muted-foreground"
|
||||
/>
|
||||
) : pr ? (
|
||||
<Icon
|
||||
role="img"
|
||||
aria-label={description}
|
||||
className={cn("h-3.5 w-3.5", color)}
|
||||
/>
|
||||
) : (
|
||||
<svg
|
||||
role="img"
|
||||
aria-label={description}
|
||||
className={cn("h-3.5 w-3.5", color)}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
>
|
||||
<path d="M6 7v10" />
|
||||
<path d="M18 4v13" strokeDasharray="1 4" />
|
||||
<circle cx="6" cy="4" r="2.5" />
|
||||
<circle cx="6" cy="20" r="2.5" />
|
||||
<circle cx="18" cy="20" r="2.5" />
|
||||
</svg>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ branch:
|
||||
name: "jtennant/berd-voice-status-sounds"
|
||||
---`;
|
||||
|
||||
test("a task canvas exposes overview and conversation only", async ({
|
||||
test("a task summary sits above the conversation and can collapse", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, { canvasContent: TASK_CANVAS });
|
||||
@@ -30,8 +30,13 @@ test("a task canvas exposes overview and conversation only", async ({
|
||||
"jtennant/berd-voice-status-sounds",
|
||||
);
|
||||
await expect(page.getByTestId("task-work-tree")).toHaveCount(0);
|
||||
await page.getByTestId("task-view-conversation").click();
|
||||
await expect(page.getByTestId("task-view-tabs")).toHaveCount(0);
|
||||
await expect(page.getByTestId("message-timeline")).toBeVisible();
|
||||
await page.getByTestId("task-overview").locator("summary").click();
|
||||
await expect(page.getByTestId("task-overview-details")).toBeHidden();
|
||||
await expect(page.getByTestId("message-timeline")).toBeVisible();
|
||||
await page.getByTestId("task-overview").locator("summary").press("Enter");
|
||||
await expect(page.getByTestId("task-overview-details")).toBeVisible();
|
||||
await expect(page.getByTestId("task-work-navigator")).toHaveCount(0);
|
||||
});
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ Conversation → local task + new channel → implementation branch → pull req
|
||||
- A branch created outside this workflow can remain unbound temporarily and should be flagged for resolution. A person or agent can attach it to an appropriate channel or create a new one.
|
||||
- Rebinding an established branch is for correcting mistakes, not routine restructuring.
|
||||
- The branch, pull request, CI, and review use the task's channel.
|
||||
- The client recognizes the canvas schema and adds Overview and Conversation views. Overview shows the selected task and its branch or pull request. The nested left sidebar shows branch and PR status glyphs, using the local `gh` login to discover pull requests.
|
||||
- The client recognizes the canvas schema and shows a collapsible task summary above the conversation, including its branch or pull request. The nested left sidebar shows branch and PR status glyphs, using the local `gh` login to discover pull requests.
|
||||
|
||||
## Canvas data model
|
||||
|
||||
|
||||
Reference in New Issue
Block a user