feat(resources): empty-state graphics for knowledge, tables, logs, and files - #6828
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryLow Risk Overview
Shared Tool-registry baseline counts are bumped for the added UI modules. Reviewed by Cursor Bugbot for commit 123fb85. Configure here. |
Greptile SummaryThe PR adds shared empty-state presentation and resource-specific graphics for Files, Knowledge, Tables, and Logs while preserving table headers.
Confidence Score: 4/5The PR needs the folder-query loading gap fixed before merging because existing folder-only workspaces can still be presented transiently as empty. The centralized gate addresses primary-query loading, placeholder, and error states, but Files, Knowledge, and Tables each build rows from an independent folder query whose unresolved state is omitted. 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
|
| Filename | Overview |
|---|---|
| apps/sim/app/workspace/[workspaceId]/components/resource/is-resource-list-empty.ts | Centralizes zero-data gating, but callers cannot represent unresolved secondary folder queries. |
| apps/sim/app/workspace/[workspaceId]/files/files.tsx | Integrates the Files empty state, but forwards loading state only for files even though folders also produce rows. |
| apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.tsx | Integrates the Knowledge empty state with the same omitted folder-query status. |
| apps/sim/app/workspace/[workspaceId]/tables/tables.tsx | Integrates the Tables empty state with the same omitted folder-query status. |
| apps/sim/app/workspace/[workspaceId]/logs/logs.tsx | Adds a Logs empty state gated by the list query and active search or filters. |
| apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx | Adds an empty-state slot beneath persistent table headers when no rows render. |
Reviews (2): Last reviewed commit: "chore(audits): re-record the page module..." | Re-trigger Greptile
…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>
182ac10 to
a2e27ca
Compare
Drops the review-only preview route and gallery, which the branch always meant to delete before merging. Three ways the zero-data graphic painted over a workspace that has content: - The gate read the instant URL search term while `rows` is filtered by the debounced one, so clearing a search that matched nothing showed the full "you have nothing yet" state for one debounce window. - Nothing gated on the list still loading. Knowledge and tables hydrate from a server prefetch that is allowed to seed nothing, and the files list deliberately seeds nothing above 300 rows — so the emptiest-looking screen was shown to the fullest workspaces. - The filters are part of the query key and every list keeps the previous key's data, so `isLoading` is false across a filter change. Only the placeholder gate suppresses the graphic during that refetch. Also folds the re-declared isometric fills and stroke back onto the shared `iso-illustration-style` source they were copied from, so a change to the iso ramp reaches this mark too; only the stroke width still diverges. The static face paths move to module scope, the bore interior becomes a named component so its note is TSDoc rather than a JSX comment, and the four identical docs chips become one.
…ayout Hides the empty-state graphic behind `error` as well. A failed load also leaves `rows` empty, and inviting someone to create their first item is the wrong answer to a request that did not complete — all four pages only logged the error, so the zero-data copy was what a failed load actually rendered. `iso-illustration-style` moves out of the landing route group to `components/iso/`. Importing it from a workspace route was the only workspace-to-landing edge in the app, one directory away from an `iso-marks` barrel that pulls ~10KB gzipped of illustration components — a hazard for whoever needs the second constant. The contour recipe is now shared too: `createIsoLineProps` takes an optional stroke width, so the knowledge mark stops re-declaring it and only its weight diverges. `EmptyState` owns the action row's layout, so the three pages with two chips drop their wrapper div and every empty state's chips sit identically. Its unused `className` prop goes with them. Also drops the `height` prop that had one caller passing its default, and the `CORNER` constant that promised single-sourcing the path's four bare literals did not honour.
"This list holds nothing" was derived in four pages, each with the same seven clauses under the same nine-line comment. Adding the `error` gate one commit ago took four identical edits, and the skills empty state that was reverted off this branch would have made it five copies. `isResourceListEmpty` now owns the rule and the reasoning behind each gate. Logs omits the folder argument because it has no folder navigation; the other three pass theirs. `Resource.Table` also wraps the slot in its own growth box, so the empty state centres because the table says so rather than because the node handed to it happened to carry `flex-1`.
The four empty-state graphics and the shared frame add **+10 modules** to each
of the five routes that render them — measured against `origin/staging`, not
against the recorded baseline:
files/[fileId] 1958 -> 1968
files 1958 -> 1968
knowledge 2167 -> 2177
logs 1727 -> 1737
tables 1817 -> 1827
The baseline itself was last recorded in #6697, and staging has drifted up to
+29 on tables since — inside the max(25, 2%) tolerance on its own, but close
enough that this +10 tipped it over. So the failure was the stale baseline
meeting a small real addition, not a heavy import. The other 29 entries move
only by that accumulated drift.
The graphics stay eagerly imported on purpose: an empty state is the first
thing a new workspace paints, and deferring ~4KB gzipped behind a chunk
request would trade a shared, already-fetched module for a visible pop on the
one screen where the product has to look like it works.
|
@waleedlatif1 you can merge if ready I will clean later |
|
@greptileai review All six threads are addressed and resolved. Since the last review:
|
|
bugbot run |
Folder rows share the list with resource rows, so a workspace whose only contents are folders has an empty `rows` until the folder tree lands — and got the "create your first item" graphic in the gap. The resource list's own loading gates never covered it because the folder tree is a separate query. `useFolderNavigation` already exposes `foldersResolved` (`isSuccess && !isPlaceholderData`) for exactly this hazard — it guards the ancestry index against evicting a folder id it has not loaded yet. Knowledge and tables pass it straight through; files reads the same two flags off `useWorkspaceFileFolders`, which it calls directly. Logs omits it, as it has no folders.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 123fb85. Configure here.
#6828 gave the four workspace resource lists a zero-data graphic and left the one list a level below them still painting column headers over a blank body — the list every user meets immediately, because creating a base does not require a file and does not navigate anywhere on success. The mark is a stack of sheets with the front one dog-eared and ruled. The dog-ear is the one signifier the set does not already use: the folder is a container and the knowledge mark is a shelf of volumes, and this has to read as the pages inside one of them rather than as either. It reuses the rest of the recipe — hairline contours, the surface ramp for depth, ink mixed off `--text-secondary` because the ramp is near-white in light mode, and a fade running the direction the stack recedes. `HAIRLINE` was byte-identical between the folder and this mark, so it moves to a shared module beside `mask.ts`. Visibility goes through the same `isResourceListEmpty` the four pages use, with one difference the call site documents: it counts the server's `total` rather than the visible rows, because this list is paginated and an empty page 2 is a paging position, not an empty base. The folder arguments are omitted — a base's documents are flat. The frame is derived from the artwork's bounds rather than a round-numbered viewBox. The sheets step up and to the right, which left the drawn mass far enough off-centre that the mark sat visibly right of the copy beneath it.
Four resource pages had no empty state at all —
Resource.Tablepainted 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
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:--brand-knowledgeand--brand-secondaryand they were the loudest things on their pages.--surface-4/--surface-5are near-white in light mode (#f5f5f5/#f3f3f3). Skeleton geometry built on them dissolves against a white page. Ink is mixed ascolor-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.iso-illustration-style, so a change to the iso ramp reaches it too; only the stroke width diverges.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.Tablegains a sanctionedemptyStateslot rendered below the column headers whenrowsis 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. Getting that right took more than
rows.length === 0:rowsis filtered by that one: reading the instant URL value flashed the full "you have nothing yet" state for one debounce window every time a search was cleared.isLoadingis false across a filter change; only the placeholder gate suppresses the graphic during that refetch.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
EmptyStateframe from the editor branch so this branch stands alone.Not included
Skills. It was built and then pulled — the skills list still renders nothing for zero data, as before.