Skip to content

Commit 8d78a27

Browse files
committed
clean up loops wizard
1 parent 9001b17 commit 8d78a27

12 files changed

Lines changed: 464 additions & 259 deletions

packages/ui/src/features/folder-picker/GitHubRepoPicker.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export function GitHubRepoPicker({
4343
repositories,
4444
isLoading,
4545
placeholder = "Select repository...",
46+
size = "1",
4647
disabled = false,
4748
anchor,
4849
showSearchInput = true,
@@ -55,6 +56,8 @@ export function GitHubRepoPicker({
5556
hasMore: controlledHasMore,
5657
onLoadMore,
5758
}: GitHubRepoPickerProps) {
59+
const buttonSize = size === "2" ? "lg" : "sm";
60+
const buttonTextClass = size === "2" ? "text-[13px]" : "";
5861
const triggerRef = useRef<HTMLButtonElement>(null);
5962
const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
6063
const [uncontrolledSearchQuery, setUncontrolledSearchQuery] = useState("");
@@ -91,7 +94,12 @@ export function GitHubRepoPicker({
9194

9295
if (isLoading && !showInlineLoadingState) {
9396
return (
94-
<Button variant="outline" disabled size="sm">
97+
<Button
98+
variant="outline"
99+
disabled
100+
size={buttonSize}
101+
className={buttonTextClass}
102+
>
95103
<GithubLogo size={16} weight="regular" className="shrink-0" />
96104
Loading repos...
97105
</Button>
@@ -107,7 +115,12 @@ export function GitHubRepoPicker({
107115
!hasActiveRemoteSearch
108116
) {
109117
return (
110-
<Button variant="outline" disabled size="sm">
118+
<Button
119+
variant="outline"
120+
disabled
121+
size={buttonSize}
122+
className={buttonTextClass}
123+
>
111124
<GithubLogo size={16} weight="regular" className="shrink-0" />
112125
No GitHub repos
113126
</Button>
@@ -121,10 +134,10 @@ export function GitHubRepoPicker({
121134
<Button
122135
type="button"
123136
variant="outline"
124-
size="sm"
137+
size={buttonSize}
125138
disabled
126139
aria-label="Repository"
127-
className="pointer-events-none min-w-0 max-w-full cursor-default justify-start disabled:opacity-100"
140+
className={`pointer-events-none min-w-0 max-w-full cursor-default justify-start disabled:opacity-100 ${buttonTextClass}`}
128141
>
129142
<GithubLogo size={14} weight="regular" className="shrink-0" />
130143
<span className="min-w-0 truncate">{onlyRepo}</span>
@@ -165,9 +178,10 @@ export function GitHubRepoPicker({
165178
<Button
166179
ref={triggerRef}
167180
variant="outline"
168-
size="sm"
181+
size={buttonSize}
169182
disabled={disabled}
170183
aria-label="Repository"
184+
className={buttonTextClass}
171185
>
172186
<GithubLogo size={14} weight="regular" className="shrink-0" />
173187
<span className="min-w-0 truncate">{value ?? placeholder}</span>

packages/ui/src/features/loops/components/LoopBehaviorFields.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function LoopBehaviorFields({
1818
<Flex
1919
direction="column"
2020
gap="2"
21-
className="rounded-(--radius-2) border border-border bg-(--color-panel-solid) p-3"
21+
className="rounded-(--radius-2) border border-border bg-(--gray-1) p-3"
2222
>
2323
<Flex align="center" justify="between" gap="2">
2424
<Flex direction="column" gap="0">

packages/ui/src/features/loops/components/LoopContextFields.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function LoopContextFields({
5050
};
5151

5252
const contextOptions = [
53-
{ value: NOT_ATTACHED_VALUE, label: "Not attached" },
53+
{ value: NOT_ATTACHED_VALUE, label: "Not attached to a channel" },
5454
...channels.map((channel) => ({
5555
value: channel.id,
5656
label: `#${channel.name}`,
@@ -63,15 +63,16 @@ export function LoopContextFields({
6363
value={value?.folderId ?? NOT_ATTACHED_VALUE}
6464
options={contextOptions}
6565
disabled={disabled}
66-
ariaLabel="Context"
66+
size="lg"
67+
ariaLabel="Context channel"
6768
onValueChange={selectContext}
6869
/>
6970

7071
{value ? (
7172
<Flex
7273
direction="column"
7374
gap="3"
74-
className="rounded-(--radius-2) border border-border bg-(--color-panel-solid) p-3"
75+
className="rounded-(--radius-2) border border-border bg-(--gray-1) p-3"
7576
>
7677
<ToggleRow
7778
title="Show runs in the feed"
@@ -110,6 +111,7 @@ export function LoopContextFields({
110111
label: dashboard.name,
111112
}))}
112113
disabled={disabled}
114+
size="lg"
113115
ariaLabel="Canvas"
114116
onValueChange={(canvasId) =>
115117
patchOutputs({ canvas_id: canvasId })

packages/ui/src/features/loops/components/LoopForm.tsx

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import {
55
Check,
66
} from "@phosphor-icons/react";
77
import { type LoopSchemas, LoopsApiError } from "@posthog/api-client/loops";
8+
import { PROJECT_BLUEBIRD_FLAG } from "@posthog/shared";
9+
import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag";
810
import { SettingsOptionSelect } from "@posthog/ui/features/settings/SettingsOptionSelect";
11+
import { useSidebarStore } from "@posthog/ui/features/sidebar/sidebarStore";
912
import { useSetHeaderContent } from "@posthog/ui/hooks/useSetHeaderContent";
1013
import { Button } from "@posthog/ui/primitives/Button";
1114
import { toast } from "@posthog/ui/primitives/toast";
@@ -83,6 +86,17 @@ export function LoopForm({ loop }: LoopFormProps) {
8386
if (!loop) useLoopDraftStore.getState().setPrefill(null);
8487
}, [loop]);
8588

89+
// Contexts are a channels surface; hide the attachment UI when channels are
90+
// off, unless this loop is already attached so the link stays visible and
91+
// detachable.
92+
const bluebirdEnabled = useFeatureFlag(
93+
PROJECT_BLUEBIRD_FLAG,
94+
import.meta.env.DEV,
95+
);
96+
const channelsEnabled =
97+
useSidebarStore((s) => s.channelsEnabled) && bluebirdEnabled;
98+
const showContextField = channelsEnabled || !!values.contextTarget;
99+
86100
const createLoop = useCreateLoop();
87101
const updateLoop = useUpdateLoop(loop?.id ?? "");
88102
const isSubmitting = isEdit ? updateLoop.isPending : createLoop.isPending;
@@ -157,13 +171,13 @@ export function LoopForm({ loop }: LoopFormProps) {
157171
<Box className="flex h-full items-center justify-center p-6">
158172
<Flex
159173
direction="column"
160-
className="max-h-full w-full max-w-[600px] overflow-hidden rounded-(--radius-3) border border-border bg-(--color-panel-solid) shadow-xl"
174+
className="max-h-full w-full max-w-[640px] overflow-hidden rounded-(--radius-3) border border-border bg-(--color-panel-solid) shadow-xl"
161175
>
162-
<Box className="border-border border-b px-5 pt-5 pb-4">
176+
<Box className="border-border border-b px-6 pt-5 pb-4">
163177
<Stepper current={step} complete={stepComplete} onSelect={setStep} />
164178
</Box>
165179

166-
<Box className="min-h-0 flex-1 overflow-y-auto px-5 py-5">
180+
<Box className="min-h-0 flex-1 overflow-y-auto px-6 py-6">
167181
{step === 0 ? (
168182
<Step
169183
title="What should this loop do?"
@@ -203,7 +217,7 @@ export function LoopForm({ loop }: LoopFormProps) {
203217
{step === 1 ? (
204218
<Step
205219
title="When should it run?"
206-
description="Add one or more triggers. Leave empty to run it only on demand."
220+
description="A loop can have several triggers, and any one of them starts a run. With no triggers, you run it yourself from the loop's page."
207221
>
208222
<LoopTriggerEditor
209223
triggers={values.triggers}
@@ -224,14 +238,15 @@ export function LoopForm({ loop }: LoopFormProps) {
224238
className="max-w-[340px]"
225239
hint={
226240
values.contextTarget
227-
? "Loops attached to a context post runs to its shared feed, so they're visible to everyone on the project."
241+
? "Loops attached to a channel post runs to its shared feed, so they're visible to everyone on the project."
228242
: undefined
229243
}
230244
>
231245
<SettingsOptionSelect
232246
value={values.visibility}
233247
options={VISIBILITY_OPTIONS}
234248
disabled={isSubmitting || !!values.contextTarget}
249+
size="lg"
235250
ariaLabel="Visibility"
236251
onValueChange={(value) =>
237252
patch({
@@ -243,35 +258,39 @@ export function LoopForm({ loop }: LoopFormProps) {
243258

244259
<Divider />
245260

246-
<Field
247-
label="Context"
248-
hint="Attach this loop to a context to file its runs in the feed and keep its context.md or a canvas up to date."
249-
>
250-
<LoopContextFields
251-
value={values.contextTarget}
252-
disabled={isSubmitting}
253-
onChange={(contextTarget) =>
254-
patch(
255-
contextTarget
256-
? { contextTarget, visibility: "team" }
257-
: { contextTarget },
258-
)
259-
}
260-
/>
261-
</Field>
261+
{showContextField ? (
262+
<>
263+
<Field
264+
label="Context"
265+
hint="A context is one of the channels in your sidebar. Attach this loop to a channel and its runs show up in that channel's feed; it can also keep the channel's context.md or a canvas up to date."
266+
>
267+
<LoopContextFields
268+
value={values.contextTarget}
269+
disabled={isSubmitting}
270+
onChange={(contextTarget) =>
271+
patch(
272+
contextTarget
273+
? { contextTarget, visibility: "team" }
274+
: { contextTarget },
275+
)
276+
}
277+
/>
278+
</Field>
262279

263-
<Divider />
280+
<Divider />
281+
</>
282+
) : null}
264283

265284
<Field
266-
label="Repository"
285+
label="Base repository"
267286
hint={
268287
values.repositories.length > 1
269288
? `${values.repositories.length - 1} more ${
270289
values.repositories.length === 2
271290
? "repository stays"
272291
: "repositories stay"
273292
} attached to this loop.`
274-
: "Optional. Leave empty for a report-only loop that works purely through connectors."
293+
: "The repository runs check out and work in. Optional. Leave empty for a report-only loop that works purely through connectors."
275294
}
276295
>
277296
<LoopRepositoryPicker
@@ -352,7 +371,7 @@ export function LoopForm({ loop }: LoopFormProps) {
352371
title="Review"
353372
description="Check everything before you create the loop."
354373
>
355-
<ReviewList values={values} />
374+
<ReviewList values={values} showContext={showContextField} />
356375
</Step>
357376
) : null}
358377
</Box>
@@ -497,7 +516,13 @@ function Divider() {
497516
return <Box className="h-px bg-(--gray-4)" />;
498517
}
499518

500-
function ReviewList({ values }: { values: LoopFormValues }) {
519+
function ReviewList({
520+
values,
521+
showContext,
522+
}: {
523+
values: LoopFormValues;
524+
showContext: boolean;
525+
}) {
501526
const reasoning = values.reasoningEffort ?? "auto";
502527
const channels = (["push", "email", "slack"] as const).filter(
503528
(channel) => values.notifications[channel]?.enabled,
@@ -524,12 +549,14 @@ function ReviewList({ values }: { values: LoopFormValues }) {
524549
values.model || "Default model"
525550
} · ${reasoning} reasoning`}
526551
/>
552+
{showContext ? (
553+
<ReviewRow
554+
label="Context"
555+
value={describeContext(values.contextTarget)}
556+
/>
557+
) : null}
527558
<ReviewRow
528-
label="Context"
529-
value={describeContext(values.contextTarget)}
530-
/>
531-
<ReviewRow
532-
label="Repository"
559+
label="Base repository"
533560
value={
534561
values.repositories.length > 0
535562
? values.repositories.map((repo) => repo.full_name).join(", ")
@@ -580,7 +607,7 @@ function ReviewRow({
580607
}
581608

582609
function describeContext(target: LoopContextTargetDraft | null): string {
583-
if (!target) return "Not attached";
610+
if (!target) return "Not attached to a channel";
584611
const outputs: string[] = [];
585612
if (target.outputs.post_to_feed) outputs.push("feed");
586613
if (target.outputs.update_context) outputs.push("context.md");

packages/ui/src/features/loops/components/LoopFormPrimitives.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ export function Field({
2020
children: ReactNode;
2121
}) {
2222
return (
23-
<Flex direction="column" gap="1" className={className}>
24-
<Text as="label" className="font-medium text-[12px] text-gray-11">
23+
<Flex direction="column" gap="2" className={className}>
24+
<Text as="label" className="font-medium text-[13px] text-gray-12">
2525
{label}
2626
{required ? <span className="ml-0.5 text-(--accent-9)">*</span> : null}
2727
</Text>
2828
{children}
2929
{hint ? (
30-
<Text className="text-[11px] text-gray-10 leading-snug">{hint}</Text>
30+
<Text className="text-[12px] text-gray-10 leading-snug">{hint}</Text>
3131
) : null}
3232
</Flex>
3333
);

packages/ui/src/features/loops/components/LoopModelFields.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export function LoopModelFields({
100100
onModelChange(value === DEFAULT_MODEL_VALUE ? "" : value)
101101
}
102102
disabled={disabled}
103+
size="lg"
103104
ariaLabel="Model"
104105
/>
105106
</Field>
@@ -113,6 +114,7 @@ export function LoopModelFields({
113114
onAdapterChange(value as LoopSchemas.LoopRuntimeAdapterEnum)
114115
}
115116
disabled={disabled}
117+
size="lg"
116118
ariaLabel="Adapter"
117119
/>
118120
</Field>
@@ -129,6 +131,7 @@ export function LoopModelFields({
129131
)
130132
}
131133
disabled={disabled}
134+
size="lg"
132135
ariaLabel="Reasoning effort"
133136
/>
134137
</Field>

0 commit comments

Comments
 (0)