Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.
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
34 changes: 28 additions & 6 deletions packages/ui/src/features/canvas/components/WebsiteChannelLoops.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import {
} from "../../loops/components/LoopFallbacks";
import { LoopRow } from "../../loops/components/LoopRow";
import { LoopsEmptyState } from "../../loops/components/LoopsEmptyState";
import { LoopTemplatesSection } from "../../loops/components/LoopTemplatesSection";
import { useLoopLimits, useLoops } from "../../loops/hooks/useLoops";
import { useLoopDraftStore } from "../../loops/loopDraftStore";
import { defaultLoopContextOutputs } from "../../loops/loopFormTypes";
import type { LoopTemplate } from "../../loops/loopTemplates";
import { useChannels } from "../hooks/useChannels";
import { useOrgMembers } from "../hooks/useOrgMembers";
import { PERSONAL_CHANNEL_NAME } from "../hooks/useTaskChannels";

function contextQuickStarts(name: string): { label: string; prompt: string }[] {
return [
Expand Down Expand Up @@ -52,6 +55,7 @@ export function WebsiteChannelLoops({ channelId }: { channelId: string }) {
const { channels } = useChannels();
const channel = channels.find((c) => c.id === channelId);
const contextName = channel?.name ?? channelId;
const isPersonal = contextName === PERSONAL_CHANNEL_NAME;

useSetHeaderContent(
useMemo(() => <ChannelHeader channelId={channelId} />, [channelId]),
Expand Down Expand Up @@ -82,13 +86,26 @@ export function WebsiteChannelLoops({ channelId }: { channelId: string }) {
navigateToNewLoop();
};

const startFromTemplate = (template: LoopTemplate) => {
useLoopDraftStore.getState().setPrefill({
description: template.description,
...template.build(),
contextTarget: {
folderId: channelId,
name: contextName,
outputs: defaultLoopContextOutputs(),
},
});
navigateToNewLoop();
};

return (
<Flex direction="column" className="h-full min-h-0">
<div className="min-h-0 flex-1 overflow-auto">
<Flex
direction="column"
gap="6"
className="@container mx-auto w-full max-w-3xl px-8 py-8"
className="@container mx-auto w-full max-w-5xl px-8 py-8"
>
<div className="flex @min-[640px]:flex-row flex-col items-start @min-[640px]:items-center justify-between gap-3">
<Flex
Expand All @@ -98,7 +115,7 @@ export function WebsiteChannelLoops({ channelId }: { channelId: string }) {
>
<Flex align="center" gap="2" wrap="wrap">
<Heading className="font-bold text-2xl">
Automate #{contextName}
{isPersonal ? "Loops" : `Automate #${contextName}`}
</Heading>
<Flex
align="center"
Expand All @@ -115,8 +132,9 @@ export function WebsiteChannelLoops({ channelId }: { channelId: string }) {
</Flex>
</Flex>
<Text color="gray" className="text-sm">
Build a loop that posts its runs to this context's feed, or
keeps its context.md or a canvas up to date.
Put your work on autopilot. Loops run on a schedule, on an API
call, or when something happens on GitHub. You can finally close
the laptop!
</Text>
</Flex>
<Button
Expand Down Expand Up @@ -160,16 +178,20 @@ export function WebsiteChannelLoops({ channelId }: { channelId: string }) {
</Flex>
</Flex>
) : (
<LoopsEmptyState contextName={contextName} />
<LoopsEmptyState
contextName={isPersonal ? undefined : contextName}
/>
)}

<LoopTemplatesSection onSelect={startFromTemplate} />
</Flex>
</div>

<div className="shrink-0">
<Flex
direction="column"
gap="2"
className="mx-auto w-full max-w-3xl px-8 pt-3 pb-6"
className="mx-auto w-full max-w-5xl px-8 pt-3 pb-6"
>
<LoopBuilderComposer
context={{ folderId: channelId, name: contextName }}
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/features/loops/components/LoopsEmptyState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ const GETTING_STARTED_STEPS = [
export function LoopsEmptyState({ contextName }: { contextName?: string }) {
return (
<div className="@container">
<div className="flex @min-[720px]:flex-row flex-col items-center @min-[720px]:gap-0 gap-6 rounded-(--radius-3) border border-gray-6 border-dashed @min-[720px]:px-8 px-5 py-8">
<Flex justify="center" className="@min-[720px]:w-2/5 w-full shrink-0">
<div className="flex @min-[560px]:flex-row flex-col items-center @min-[560px]:gap-0 gap-6 rounded-(--radius-3) border border-gray-6 border-dashed @min-[560px]:px-8 px-5 py-8">
<Flex justify="center" className="@min-[560px]:w-2/5 w-full shrink-0">
<img src={loopHog} alt="" className="h-auto w-52 object-contain" />
</Flex>
<Flex
Expand Down
Loading