Reads rows in the tables you allow.
Adds rows. Changes data.
Posts to attached channels.
A form that locks its fields while saving, moves focus to the first error, and ends with a save area that explains why it cannot save yet.
import { Form, FormFooter, FormSection } from "@tulipfarm/sprout/form";
import { Field } from "@tulipfarm/sprout/field";
import { Input } from "@tulipfarm/sprout/input";
<Form label="Change interval" disabled={saving} onSubmit={() => {
if (error) { setChecked(true); return "invalid"; }
save(minutes);
}}>
<FormSection title="Scheduled trigger" description="Start this agent on an interval.">
<Field label="Run every" required error={checked ? error : undefined}>
<Input type="number" value={minutes} onChange={…} />
</Field>
</FormSection>
<FormFooter
dirty={minutes !== saved}
note="Saving makes you the sponsor."
submit={{
label: "Save interval",
pendingLabel: "Saving interval…",
state: saving ? { kind: "busy" } : { kind: "ready" },
}}
cancel={{ kind: "reset", label: "Discard changes", onReset: reset }}
/>
</Form>Form renders a noValidate form and prevents the browser submit. Return "invalid" from onSubmitafter you show errors; the form then scrolls to and focuses the first control with aria-invalid="true". disabledlocks every Field, ChoiceGroup, andCheckboxField inside. label names the form.
FormSection is a bordered region named by itstitle, or by label when set. It takes an optionaldescription, a meta badge such as3 / 20, an id to jump to, andlevel2 or 3 for the heading.
Clear a field and save. Focus moves to the first error, the footer shows a summary, and Discard changes restores the saved values. While saving, the fields lock and Discard changes hides.
bar sits under a divider for page and section forms.stack uses full-width large buttons for sign-in, onboarding, and dialogs. compact fits chat cards: it hides the status line from sight, and its children sit beside the buttons instead of below them.
How long should my replies be?
submit={{
label: "Add Slack destination",
pendingLabel: "Adding Slack destination…",
state: team ? { kind: "ready" } : {
kind: "blocked",
reason: "Choose a destination team below first.",
fix: { kind: "jump", label: "Choose a team", target: "slack-destination-team" },
},
}}busy shows pendingLabel and setsaria-busy. blocked shows itsreason under the buttons and links it witharia-describedby. Busy and blocked buttons stay focusable so keyboard users hear why. A fix adds a link button:jump focuses an element by id, action runs a callback, and link renders your router link.dirty shows Unsaved changes; a reset cancel shows only while dirty and not busy.
Connect a personal account, or ask a team integration manager for integration use and repository read permissions.
Meta badges count choices or items. The header wraps the badge under the title when the section is narrow. Long titles and descriptions wrap.
Choose actions by app. Skills guide the work; these choices grant the capability.
Give each recurring run its own task. Schedules stay off until activation.
Runs use the version you activate. Activate again after you change the instructions.
Read rows, Post to Slack
Where this agent is listed and who can use or manage it.
Teams that can use this agent. Managers can also edit it.
Operations · can use
Removes the agent and stops its active versions.
secondarySubmit adds an outline submit such as Save draft; itsonSelect runs before the form submits. Aleave cancel renders a link. Pass form to put the footer outside its form, and summary to replace the status line with your own content. Footer children carry save feedback, alerts, and extra actions.
Where this agent is listed and who can use or manage it.
Teams whose permissions this agent uses when it runs.
Which supplier should I contact first?
Name every form with label or a surrounding section. Errors render in a destructive Alert. Status lines fade in over 180ms and appear at once with reduced motion. Saving, routing, and data in these examples are sample code; the textarea is a docs-only stand-in.