Skip to content

feat(resources): empty-state graphics for knowledge, tables, logs, and files - #6828

Open
andresdjasso wants to merge 12 commits into
mainfrom
resources-empty-states
Open

feat(resources): empty-state graphics for knowledge, tables, logs, and files#6828
andresdjasso wants to merge 12 commits into
mainfrom
resources-empty-states

Conversation

@andresdjasso

Copy link
Copy Markdown
Contributor

Four resource pages had no empty state at all — Resource.Table painted its column headers over a blank scroll area and stopped there. This adds a graphic per resource, plus the actions you'd want from a page with nothing on it.

Design review is the point of this branch@emirKarabeg to take it from here.

The graphics

Knowledge bases An isometric set of volumes, bored through the front cover so you see the wall and the volume behind
Tables A ruled grid running off two edges with one cell held in an edit ring
Logs An activity feed, newest run lifted onto its own card
Files A folder with sheets standing proud of its front panel

What they're built from

The recipe comes from two places already in the repo — the workflow editor's empty state (improvement/editor-deploy-experience) and the landing page's feature graphics. Both are worth knowing before changing anything here:

  • No brand colour anywhere. The editor vignette and all three generations of landing graphics are built entirely from neutral tokens. An earlier pass used --brand-knowledge and --brand-secondary and they were the loudest things on their pages.
  • --surface-4/--surface-5 are near-white in light mode (#f5f5f5/#f3f3f3). Skeleton geometry built on them dissolves against a white page. Ink is mixed as color-mix(in srgb, var(--text-secondary) N%, transparent) instead — a real mid-grey that inverts with the theme, which is the idiom the editor vignette already uses.
  • Hairlines throughout. The landing iso marks draw their 3.2 stroke into a ~526-unit viewBox, so it lands as a hairline only after scaling. Used naively at this size it is a fat outline. The knowledge mark authors its geometry in the same large unit space and thins the stroke further, because it sits beside a grid whose rules are 1px.
  • Fades run whichever way the subject recedes — tables to the bottom-right, knowledge up and right, logs down, the folder up. A fade cutting against its subject eats the thing you are meant to look at.
  • Objects stay crisp, repeating structures dissolve. The editor vignette keeps its block fully opaque and fades only the connectors leaving frame. A grid means the same thing cropped; a folder does not.

The isometric mark computes its projection and faces rather than carrying hand-authored path data, so the bore, its depth, and the cover plane all follow if the volumes are retuned.

Plumbing

Resource.Table gains a sanctioned emptyState slot rendered below the column headers when rows is empty. Its documented chrome guarantee — headers render unconditionally, no state ever drops them — still holds.

Each page shows its graphic only for true zero-data: never for a search or filter that matched nothing, never inside an empty subfolder, and for logs never before the first page of runs lands. Every empty state's create chip runs the same handler as the header's primary chip and inherits its disabled state.

Also ports the shared EmptyState frame from the editor branch so this branch stands alone.

Before merge

apps/sim/app/empty-states-preview/ and gallery-preview.tsx are review-only — a route rendering all four side by side. Delete both before merging; they're kept here because they're the fastest way to judge the set as a family.

Not included

Skills. It was built and then pulled — the skills list still renders nothing for zero data, as before.

🤖 Generated with Claude Code

andresdjasso and others added 12 commits August 18, 2026 16:09
…les, skills

Four of the resource pages (knowledge, tables, logs, files) had no empty
state at all — `Resource.Table` painted column headers over a blank scroll
area and stopped there. Skills had a `: null` branch for zero data.

Adds a graphic per resource, drawn in the editor vignette's recipe: take the
product's own primitives, shrink them, strip the content to skeletons, and
let the composition bleed off the frame edges.

- Knowledge — a document fanning into the chunks it is embedded as, using the
  editor's 6px smooth-step connector language in --workflow-edge
- Tables — a sheet of cells running off two edges with one cell in an edit ring
- Logs — runs stacked newest-first, their trace spans staggered into a waterfall
- Files — a folder held open with one file still above its dashed landing slot
- Skills — a skill card opened far enough to show the tools bundled inside

`Resource.Table` gains a sanctioned `emptyState` slot rendered below the
column headers when `rows` is empty, so the chrome guarantee still holds.
Each page shows the graphic only for true zero-data — never for a search or
filter that matched nothing, never inside an empty subfolder, and (logs) never
before the first page of runs lands.

Also ports the shared `EmptyState` frame from the editor branch so this branch
stands alone, and adds a review-only /empty-states-preview gallery route.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…scale

Matches the workflow editor's vignette and the landing feature graphics, which
between them use no brand colour at all — every one of them is built from
neutral tokens.

Two corrections:

- The blue edit ring is gone. Nothing in the reference graphics carries a hue,
  and it was the loudest element on the page.
- `--surface-4`/`--surface-5` are near-white in light mode (#f5f5f5/#f3f3f3), so
  skeleton geometry built on them dissolved on a white card. Bars now mix
  `--text-secondary` into transparent at graded strengths — a real mid-grey that
  inverts with the theme, which is the idiom the editor vignette already uses for
  the one bar it needs you to see.

Also drops the full-composition mask. The editor vignette keeps its block fully
opaque and fades only the connector strokes leaving the frame; masking
everything is what made the miniature read washed rather than deliberate. The
card is crisp now and the continuation is drawn the way a real table draws it —
an overflow fade at the edge the columns run off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… a corner fade

Minimal pass. The card is gone — no border, no fill, no header shading, no type
squares. What is left is the grid itself: hairline rules in `--border-1`, ink
bars at two strengths, and the one cell held in an edit ring.

With no card fill the grid sits directly on the page, so it can dissolve into
the background instead of ending at a border. The fade is the landing page's own
idiom — two gradients intersected (`mask-composite: intersect`), crisp at the
top-left and gone through the bottom-right, the same construction
`workflow-graph-preview` uses.

Two placement notes:

- The grid is offset right of frame centre. A diagonal dissolve puts the visual
  mass toward its opaque corner, so centring the geometry would leave the
  graphic reading left of the copy beneath it.
- The selected cell sits in the quadrant the fade leaves fully opaque. A
  selection ring dissolving mid-stroke reads as a rendering fault, not a detail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The ring mixed `--text-secondary` into `transparent`, so the grid rules running
underneath showed through its own stroke. Mixing into `--bg` instead holds the
same apparent value while staying opaque, and still inverts with the theme.

Raised 32% -> 46% so it reads as chrome rather than more content, and added a
stacking context: neighbouring cells are later siblings, so their rules were
painting over the ring's right and bottom edges.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6px on the top-left only — the one corner the fade leaves intact, and the same
radius the workflow editor's vignette uses. The other three dissolve, so there
is nothing there to round.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…reate chips

Knowledge gets the same treatment tables just went through: no brand colour (the
`--brand-knowledge` accent is gone), no card chrome, ink mixed from
`--text-secondary`, and the landing page's intersected corner fade.

The graphic is a document and the chunks it is embedded as. Its fade is held
back further than the tables grid on both axes — the document has to stay whole
for the graphic to mean anything, so only the chunk grid may trail off. The
three chunks the edges actually land on are the only filled ones; filling the
whole first column left a chunk with no edge feeding it.

Both empty states now carry two chips in the frame's action slot — a docs link
and the create action, each running the same handler as the header's primary
chip and inheriting its disabled state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…te chip

Chip order swapped on both empty states — the primary action reads first, the
docs link second.

Adds a review-only `knowledge-alternates.tsx` rendered in the preview gallery,
because the document-to-chunks graphic is not landing. Three directions:

- A. the embedding mesh — the landing hero's own knowledge-base panel already
  draws a base this way (`stage-kb.tsx`), so this is the house depiction rather
  than a new invention
- B. a stack of documents — the most literal reading, at the cost of colliding
  with what the files empty state wants to draw
- C. a query and the passages that answered it — depicts what a base is for,
  which is what the description copy actually promises

Delete this file and the gallery entries once one is chosen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the document-to-chunks diagram, which read as a workflow graph rather
than as a knowledge base.

Built on the landing page's iso-illustration recipe rather than a new one:
`ISO_STROKE` contours (`--text-subtle` mixed toward `--text-muted`) at the shared
3.2 stroke width, faces filled from the three-tier surface ramp brightest-on-top,
round caps and joins. Geometry is authored in a large unit space so that 3.2
lands as a hairline once scaled to empty-state size — the same reason the landing
marks draw 3.2 into a ~526-unit viewBox.

The projection and faces are computed rather than hand-authored as path data, so
the volumes stay coherent when the geometry is retuned.

No corner fade here. The fade belongs to repeating structures that mean the same
thing cropped — the tables grid keeps its meaning with two columns or four. A
discrete object does not, which is also why the workflow editor's vignette keeps
its block fully opaque.

Drops the three candidate depictions now that the direction is settled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ge beside it

Drops the loose page on the ground and puts the knowledge-base mark on the front
volume's cover — the same `Database` glyph the sidebar and the page header use,
so the empty state names its own resource.

The mark is laid into the cover's plane rather than drawn over it. The cover is
the face at max x, spanned by the volume's depth across and its height up;
walking those two edges gives the face's basis vectors in projected space, and an
affine matrix built from them maps flat artwork into the face. So the glyph
skews with the isometric, and because both vectors derive from the box, retuning
the volumes carries the mark with them instead of stranding hand-fitted path data.

Its stroke is pre-divided by the same factor the matrix scales by, so the glyph's
contours land at the volumes' weight rather than four times it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…et back

Replaces the mark on the cover with a hole through it.

The bore is authored as a plain circle in the cover's own plane and skewed into
an ellipse by the face matrix. Its far mouth is the same circle stepped back
through the volume: boring straight back is a world step of `-w` along x, and
solving the cover-plane matrix for the local offset that produces it gives
`(+w, -w)`. The sliver of near mouth the far mouth fails to cover is exactly the
wall you see down the hole, so the depth falls out of the geometry rather than
being drawn by hand.

Down the hole the near mouth is floored in a tone darker than any outer face —
the wall turns away from the light — and the far mouth is painted in the cover
tone of the volume standing behind it, because looking through a hole in the
front volume lands on that volume's face, not on the page.

Corners stay square. Rounding was tried and reverted: rounding each face
separately notches every corner where three faces meet, and rounding the
silhouette instead cost a clip per volume for a softness the set did not want.

The tables grid's corner fade is applied along the other diagonal. There it
dissolves toward the bottom-right because a grid keeps its meaning cropped; here
the set recedes up and to the left and the front volume carries the bore, so
anchoring at the bottom-right eats into the back of the stack and reads as more
volumes behind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ollection

Logs is an activity feed — newest run lifted onto its own card, older ones
settling behind it. The relative stamps are the only literal text in any of these
graphics; everything else stays skeleton, so nothing here has to be translated or
kept true.

Files is a folder with sheets standing proud of its front panel. Depth comes from
the surface ramp rather than shadow, which would need separate light and dark
recipes where the ramp inverts on its own. The tab's diagonal is filleted at both
ends and every outer corner shares one radius — mixing radii, or running the
diagonal into square junctions, made the corners fight at this size.

Consistency pass across the set:

- Titles are the resource name alone. "No tables yet" earned nothing the
  description does not already say.
- The knowledge mark is mirrored so its bore faces left. Rebuilt on the geometry
  rather than flipped, since a flip would have put the shading on the wrong side.
  Its contours are thinned and mixed toward `--border-1`: the landing marks are
  the focal art of their section, but this one sits beside a ruled grid whose
  lines are 1px, and full-weight contours read as ink next to it.
- The logs feed is sized to the same ~148px footprint as the rest. The frame
  centres graphic and copy together, so a taller graphic pushes its title out of
  line with the others' and the set stops reading as one thing.
- Every empty state carries its create action and a docs link, each running the
  same handler as the header's primary chip.
- Fades run whichever way the subject recedes: the tables grid to the
  bottom-right, the knowledge set up and right, the logs feed down, the folder
  up.

Fixes a duplicate React key in the knowledge mark — the volumes stack along y
now, so keying on `box.x` gave every one of them `0`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Removed at request. The skills list goes back to rendering nothing for zero data,
which is what it did before this branch.

Takes `vignette.tsx` with it — the shared stage and skeleton bar were left over
from the first pass, and skills was the last thing still importing them once the
other four graphics were redrawn.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 18, 2026 11:00pm

Request Review

@cursor

cursor Bot commented Aug 18, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Presentation-layer changes to resource list empty UI with conservative gating; no auth, data, or API changes.

Overview
Adds empty states for Knowledge, Tables, Logs, and Files when a workspace truly has no data — each with a neutral graphic, short copy, primary action (where applicable), and a Docs link.

Resource.Table now accepts an emptyState slot rendered under sticky column headers when rows is empty; headers and table chrome stay in place, and the scroll container uses flex layout so the empty content can fill the area.

Each list page gates the graphic on true zero-data only: root folder, no active search, and no filter tags (Logs also waits until the first fetch finishes so it does not flash before runs load). Create/upload chips mirror the header actions and permission/disabled state.

Also introduces a shared EmptyState layout component and a review-only gallery route (empty-states-preview + gallery-preview) to compare all four side by side — intended to be removed before merge.

Reviewed by Cursor Bugbot for commit 182ac10. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 182ac10. Configure here.

rows.length === 0 &&
currentFolderId === null &&
!urlSearchQuery.trim() &&
filterTags.length === 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty state flashes during load

High Severity

showEmptyState treats an empty rows array as zero-data without waiting for the list query. On a cold load or workspace switch, knowledgeBases / tables / files default to [] while fetching, so the create/upload empty state and its primary chip appear before real data lands. Logs already gates on !logsQuery.isLoading; the same readiness check is missing here, and a failed fetch with empty data hits the same path.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 182ac10. Configure here.

rows.length === 0 &&
currentFolderId === null &&
!urlSearchQuery.trim() &&
filterTags.length === 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleared search shows false empty

Medium Severity

showEmptyState checks the instant URL search value (urlSearchQuery / urlSearchTerm), while row filtering still uses the debounced search. Clearing a search that matched nothing strips the URL immediately, so for the debounce window rows stay empty and the gate thinks the workspace has no data. Logs avoids this by gating on debouncedSearchQuery.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 182ac10. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds shared resource empty-state framing and dedicated graphics and actions for Knowledge, Tables, Logs, and Files. It also extends Resource.Table with a header-preserving empty-state slot and includes a temporary visual-review gallery.

  • Adds four neutral-token empty-state illustrations and resource-specific actions.
  • Shows the states only at the root when rows, searches, and filters are empty.
  • Adds a reusable EmptyState component and Resource.Table empty-state slot.
  • Adds a review-only route displaying all four designs together.

Confidence Score: 4/5

The initial-loading guards should be fixed before merging because three resource pages can briefly present pending data as a confirmed empty workspace.

Files, Knowledge, and Tables derive zero-data state from empty fallback arrays without checking their active initial requests, while the remaining accepted feedback concerns non-blocking adherence to the Tailwind-only styling convention.

Files Needing Attention: apps/sim/app/workspace/[workspaceId]/files/files.tsx, apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.tsx, apps/sim/app/workspace/[workspaceId]/tables/tables.tsx, and the Logs/Tables empty-state graphics

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx Adds an emptyState slot beneath persistent column headers; the conditional flex layout remains structurally sound.
apps/sim/app/workspace/[workspaceId]/files/files.tsx Wires the Files empty state and upload action, but can display it during a cold initial fetch.
apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.tsx Wires the Knowledge empty state and create action with the same missing initial-loading guard.
apps/sim/app/workspace/[workspaceId]/tables/tables.tsx Wires the Tables empty state and create action with the same missing initial-loading guard.
apps/sim/app/workspace/[workspaceId]/logs/logs.tsx Correctly suppresses the Logs empty state while the first page is loading and while search or filters are active.
apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/logs-empty-state.tsx Adds the Logs illustration and documentation action, but uses inline style props contrary to the styling rule.
apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/tables-empty-state.tsx Adds the Tables illustration and actions, with several inline style declarations contrary to the styling rule.
apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/knowledge-iso.tsx Adds a computed isometric SVG whose fixed definition identifiers do not cause a current rendering failure.
apps/sim/components/empty-state/empty-state.tsx Adds a reusable flex-based frame for empty-state graphics, guidance, and actions.

Reviews (1): Last reviewed commit: "revert(skills): drop the skills empty st..." | Re-trigger Greptile

Comment on lines +1813 to +1817
const showEmptyState =
rows.length === 0 &&
currentFolderId === null &&
!urlSearchTerm.trim() &&
filterTags.length === 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Loading mistaken for zero data

When a user cold-loads the Files, Knowledge, or Tables root page without cached data, each query initially supplies an empty rows array and this predicate renders the zero-data state before loading finishes, causing existing resources to be presented temporarily as an empty workspace. Include the corresponding loading state in each page's empty-state condition.

Knowledge Base Used:

Comment on lines +64 to +68
className='block h-[6px] rounded-full'
style={{ width: row.title, background: INK.title }}
/>
<span
className='block h-[4px] rounded-full'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Inline styles bypass Tailwind convention

The new Logs graphic assigns widths and backgrounds through JSX style props, splitting its visual configuration between inline declarations and the repository's required Tailwind styling system. The same pattern also occurs for grid templates, cell styles, and selection borders in tables-empty-state.tsx.

Context Used: Tailwind CSS and styling conventions (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant