State Panel
One calm place for empty, loading, success, unavailable, and error states. It explains what happened and offers the next step.
No files yet
Upload the documents your work depends on, like price lists and contracts.
Usage
import { StatePanel } from "@tulipfarm/sprout/state-panel";
<StatePanel
kind="error"
heading="h3"
title="Could not open this file"
description={error.message}
>
<Button variant="outline" onClick={retry}>Try again</Button>
</StatePanel>kind is "empty", "loading", "success", "unavailable", or "error". Loading shows a spinner, success a check, and error a warning. Empty and unavailable showicon, which defaults to an inbox. Any component that takes asize number works, including your own brand glyphs. Passicon= for a text-only empty or unavailable panel.
heading sets the title element, h1 to h4, and defaults to h2. Use h1 when the panel is the whole page, like a not-found route. An error panel must have children, so people always have a way to recover.
No files yet
Upload the documents your work depends on, like price lists and contracts.
Opening file
Preparing the document viewer.
Slack is connected
Agents can now post to the channels you choose.
This format cannot be displayed
Download the original file to open it in a compatible application.
Could not open this file
The connection dropped while loading. Try again.
Sample preview
<StatePanel
kind="empty"
icon={Files}
title="No files yet"
description="Upload the documents your work depends on."
sample={<FilesPreview />}
>
<Button><Add aria-hidden="true" />Upload files</Button>
</StatePanel>Empty panels can take a sample: a picture of the page once it has content. The panel becomes a card that sizes to its container, not the window. The preview hides below 28rem and moves beside the text from 48rem. The preview is hidden from assistive technology and cannot be focused. Passundefined to fall back to the centered layout.
No files yet
Upload the documents your work depends on, like price lists, contracts, and policies. Files stay private until you share them.
No models configured yet
Once your operator configures model providers, each model family appears here.
No workflows yet
Describe the steps once, then run them on a schedule.
Sample presets
Sixteen ready-made samples ship with State Panel, one for each TulipFarm page that can be empty. Each one is a State Sample window with fixed, realistic content.
import { StatePanel, WorkflowsSample } from "@tulipfarm/sprout/state-panel";
<StatePanel
kind="empty"
icon={BranchDown}
title={archived ? "No archived workflows" : "No workflows yet"}
description="Describe the work in chat, review its diagram, then save and activate it."
sample={archived ? undefined : <WorkflowsSample />}
>
<Button variant="outline">New workflow</Button>
</StatePanel>No files yet
Upload the documents your work depends on, like price lists, contracts, and policies. Files stay private until you share them.
<FilesSample /> · List · folder tiles and file-type icons · two-part trail · pages/files.tsx
Only RecordsSample takes data, because it copies the table you are looking at. Every preset also accepts the outer window props: label changes the “Example” chip, and className, data-*, and other div attributes go to the window root.
Every preset
Each card names the export and the TulipFarm page that shows it. Avatars, brand icons, file-type icons, and badges come from Sprõut's Avatar, Brand Icon, Attachment, and Badge.
ChatsSampleChats · pages/workspace-chats.tsxFilesSampleFiles · pages/files.tsxAgentsSampleAgents · pages/agents.tsxProjectsSampleProjects · pages/projects.tsxActive projects
Archived projects
AgentTasksSampleAgent tasks · pages/agent-tasks.tsxSkillsSampleSkills · pages/skills.tsxWorkflowsSampleWorkflows · pages/workflows.tsxTablesSampleTables · features/tables/tables.tsxRecordsSampleRows · features/tables/tables.tsxCreateInChatSampleCreate in chat · pages/tables.tsxTrack our suppliers with a name, contact email, payment terms, and region.
I’ll create a Suppliers table with these fields. Review the schema before approving.
IntegrationsSampleIntegrations · pages/integrations.tsxModelsSampleModels · pages/models.tsxMembersSampleMembers · pages/member-administration.tsxTeamsSampleTeams · pages/team-administration.tsxInvitationsSampleInvitations · pages/invitation-list.tsxGrantsSampleTeam grants · pages/governance.tsxRows preset
<RecordsSample
table={table.definition.label}
fields={table.definition.fields.map((field) => field.label)}
/>table becomes the current crumb after “Tables”. The first threefields become columns. The first is wide and starts with a colored mark, and the rest share the space. With no fields, one wide “Row” column is shown. Cell values are gray bars, so the sample never shows made-up data.
Window props and overflow
Presets fill their container. Long titles, crumbs, and column names truncate with an ellipsis instead of widening the page.
Changing state
Render the panel that matches your request. Loading and success are announced politely; errors are announced right away. When a retry removes the focused button, move focus to the surrounding region.
Loading Google Drive
Checking your connection and files.
Actions
Children sit under the description. Use buttons, links rendered as buttons, guidance text, or an inline alert. Wrap several links in your own row to keep them side by side. A child that renders nothing leaves no gap.
Could not open this file
The download stopped before it finished.
Workspace access changed
Check your workspace access before continuing.
Inbox isn’t available yet
Tasks waiting for you are in Tasks, and your conversations are in Chats.
Calendar did not load
This item could not be refreshed.
Ask again in the chat.
Slack isn’t connected
Connect Slack so agents can read and post in channels.
Slack status could not refresh.
No default knowledge
Choose a shared drive that every new chat can read.
You can’t open this folder
Ask the owner to share it with you.
Copied to your Drive
The copy is ready to edit in Google Sheets.
Waiting for Slack
Finish signing in on the Slack tab.
Channels are still syncing
Slack channels appear once the first sync finishes.
Requests, permissions, and routing in these examples are sample code. The package only draws the panel.
Accessibility
The icon tile is decorative. Error text uses role="alert"; loading and success text use role="status". Matchheading to the surrounding outline. Sample presets are pictures, not controls, and nothing in them can be focused. The panel fades in over 180ms and the spinner turns; reduced motion turns both off.