From 9420a65f88c11d5eaee9a963db35be80704be5bf Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 19 Aug 2026 11:54:08 -0700 Subject: [PATCH] polish(resources): stop the tables grid reappearing past its fade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The grid is authored larger than the box it fades inside — 358x160 drawn into 320x148, deliberately, so it runs off two edges. But a mask tile is sized to the element box and `mask-repeat` starts at `repeat`, so the overflow landed in the *next* tile at the opaque head of the gradient: a solid strip of cells reappeared just past where the fade had finished dissolving. The frame now clips, and every fade in the set pins `no-repeat` rather than relying on its subject happening to fit. Also: - `--border-1` is a legacy alias; the new files use the canonical `--border`, which `resource.tsx` was already using two lines above them. - `createIsoLineProps` returns `SVGAttributes`. It never returns a `ref`, and `ref` was the only member forcing an element type — which had made the knowledge mark reach for an `SVGPathElement & SVGCircleElement` intersection to spread onto both. `className` moves last so no caller passes `undefined` positionally to skip it. - `isResourceListEmpty` is exported from the components barrel its four callers already import `Resource` from, instead of being reached past it. - `emptyState` sits after `rows` on all four tables; three had it leading. - Two TSDoc blocks claimed things the code stopped doing: the folder graphic does not have three fill tiers, and the empty-state wrapper grows the slot but does not centre it. Adds the predicate's unit test — it is a pure eight-clause function that decides whether a page tells someone they have nothing, and it had none. Verified it fails when the placeholder and folder guards are removed. --- .../iso-marks/iso-build-illustration.tsx | 2 +- .../iso-marks/iso-ingest-illustration.tsx | 2 +- .../iso-marks/iso-integrate-illustration.tsx | 2 +- .../iso-marks/iso-monitor-illustration.tsx | 2 +- .../[workspaceId]/components/index.ts | 1 + .../files-empty-state.tsx | 16 +++--- .../resource-empty-state/knowledge-iso.tsx | 15 +++--- .../resource-empty-state/logs-empty-state.tsx | 5 +- .../components/resource-empty-state/mask.ts | 7 +++ .../tables-empty-state.tsx | 12 +++-- .../resource/is-resource-list-empty.test.ts | 53 +++++++++++++++++++ .../components/resource/resource.tsx | 5 +- .../workspace/[workspaceId]/files/files.tsx | 6 +-- .../[workspaceId]/knowledge/knowledge.tsx | 6 +-- .../app/workspace/[workspaceId]/logs/logs.tsx | 9 ++-- .../workspace/[workspaceId]/tables/tables.tsx | 6 +-- .../components/iso/iso-illustration-style.ts | 16 +++--- 17 files changed, 119 insertions(+), 46 deletions(-) create mode 100644 apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/mask.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/components/resource/is-resource-list-empty.test.ts diff --git a/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-build-illustration.tsx b/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-build-illustration.tsx index 00f921aaca9..1a605b3ddb6 100644 --- a/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-build-illustration.tsx +++ b/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-build-illustration.tsx @@ -14,7 +14,7 @@ export interface IsoBuildIllustrationProps { const STROKE_PAINT = ISO_STROKE -const LINE_PROPS = createIsoLineProps('iso-build-line', STROKE_PAINT) +const LINE_PROPS = createIsoLineProps(STROKE_PAINT, undefined, 'iso-build-line') /** Isometric tile module the floor grid and the columns are built on. */ const TILE_WIDTH = 38 diff --git a/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-ingest-illustration.tsx b/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-ingest-illustration.tsx index 980bb792339..92289a3972d 100644 --- a/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-ingest-illustration.tsx +++ b/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-ingest-illustration.tsx @@ -18,7 +18,7 @@ export interface IsoIngestIllustrationProps { const STROKE_PAINT = ISO_STROKE -const LINE_PROPS = createIsoLineProps('iso-ingest-line', STROKE_PAINT) +const LINE_PROPS = createIsoLineProps(STROKE_PAINT, undefined, 'iso-ingest-line') /** * Inline supplied illustration for the Context area - a central store diff --git a/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-integrate-illustration.tsx b/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-integrate-illustration.tsx index 4981feb35b0..51f78761cd2 100644 --- a/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-integrate-illustration.tsx +++ b/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-integrate-illustration.tsx @@ -14,7 +14,7 @@ export interface IsoIntegrateIllustrationProps { const STROKE_PAINT = ISO_STROKE -const LINE_PROPS = createIsoLineProps('iso-integrate-line', STROKE_PAINT) +const LINE_PROPS = createIsoLineProps(STROKE_PAINT, undefined, 'iso-integrate-line') /** * Inline supplied illustration for the Integrate area - a three-tier isometric diff --git a/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-monitor-illustration.tsx b/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-monitor-illustration.tsx index b70bab7a7b1..fb0b921308c 100644 --- a/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-monitor-illustration.tsx +++ b/apps/sim/app/(landing)/components/mothership/components/iso-marks/iso-monitor-illustration.tsx @@ -14,7 +14,7 @@ export interface IsoMonitorIllustrationProps { const STROKE_PAINT = ISO_STROKE -const LINE_PROPS = createIsoLineProps('iso-monitor-line', STROKE_PAINT) +const LINE_PROPS = createIsoLineProps(STROKE_PAINT, undefined, 'iso-monitor-line') /** * Inline supplied illustration for the Monitor area - an isometric housing whose diff --git a/apps/sim/app/workspace/[workspaceId]/components/index.ts b/apps/sim/app/workspace/[workspaceId]/components/index.ts index 3aeaaeebbeb..4aa4ad52ac7 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/components/index.ts @@ -1,3 +1,4 @@ +export { isResourceListEmpty } from '@/app/workspace/[workspaceId]/components/resource/is-resource-list-empty' export { ConversationListItem } from './conversation-list-item' export type { ErrorBoundaryProps, ErrorStateProps } from './error' export { ErrorShell, ErrorState } from './error' diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/files-empty-state.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/files-empty-state.tsx index 09a5f802c6c..ae1735a7015 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/files-empty-state.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/files-empty-state.tsx @@ -2,11 +2,12 @@ import { Chip, cn } from '@sim/emcn' import { Upload } from '@sim/emcn/icons' import { EmptyState } from '@/components/empty-state/empty-state' import { EmptyStateDocsLink } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/docs-link' +import { MASK_NO_REPEAT } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/mask' const FILES_DOCS_URL = 'https://docs.sim.ai/files' /** - * Hairline contours, matching the tables grid's 1px `--border-1` rules and the + * Hairline contours, matching the tables grid's 1px `--border` rules and the * knowledge mark's thinned strokes — the graphics sit one nav item apart, so a * heavier outline here would read as a different illustration system. * @@ -15,7 +16,7 @@ const FILES_DOCS_URL = 'https://docs.sim.ai/files' * solid mid-grey body made this the heaviest thing on the page. */ const HAIRLINE = { - stroke: 'var(--border-1)', + stroke: 'var(--border)', strokeWidth: 1.1, strokeLinejoin: 'round' as const, } as const @@ -48,12 +49,13 @@ const FOLDER_FADE = /** * A folder held open with sheets standing proud of its front panel. * - * Depth is carried by the surface ramp rather than by shadow: the back panel is the - * darkest tier, the sheets the lightest, the front panel between them. Shadows + * Depth is carried by the surface ramp rather than by shadow: the back panel sits a + * tier down on `--surface-4`, everything in front of it on `--surface-2`. Shadows * would need separate light and dark recipes; the ramp inverts on its own. * - * Every outer corner shares the same 10-unit radius so the silhouette reads as a - * single drawn shape — mixing radii makes the corners fight each other at this size. + * Unlike the tables grid — where a skeleton bar has no outline and so needs mid-grey + * ink to survive a light page — the hairline draws this shape, so the fills only have + * to separate one layer from the next and a near-white tier carries it. */ function FilesGraphic() { return ( @@ -64,7 +66,7 @@ function FilesGraphic() { fill='none' aria-hidden='true' focusable='false' - className={cn('block max-w-none shrink-0', FOLDER_FADE)} + className={cn('block max-w-none shrink-0', FOLDER_FADE, MASK_NO_REPEAT)} > diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/knowledge-iso.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/knowledge-iso.tsx index 37cd2b50a9f..6f03e3659ae 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/knowledge-iso.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/knowledge-iso.tsx @@ -7,6 +7,7 @@ import { ISO_FILL_PULSE_LOW, ISO_STROKE as ISO_STROKE_BASE, } from '@/components/iso/iso-illustration-style' +import { MASK_NO_REPEAT } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/mask' const COS_30 = Math.cos(Math.PI / 6) @@ -61,14 +62,14 @@ const SLABS: Box[] = [0, 90, 180].map((offset) => ({ * Lighter and thinner than the landing marks draw them. * * Those marks are the focal art of their section; here the mark sits beside a - * ruled grid and a skeleton feed whose lines are 1px of `--border-1`. Carrying + * ruled grid and a skeleton feed whose lines are 1px of `--border`. Carrying * the landing's full-weight contour made the volumes read as ink next to those, - * so the shared stroke is mixed toward `--border-1` and thinned to land near a + * so the shared stroke is mixed toward `--border` and thinned to land near a * hairline once the mark is scaled to empty-state size. Only the width diverges * from the shared recipe; the fills are imported so a change to the iso ramp * reaches this mark too. */ -const ISO_STROKE = `color-mix(in srgb, ${ISO_STROKE_BASE} 55%, var(--border-1))` +const ISO_STROKE = `color-mix(in srgb, ${ISO_STROKE_BASE} 55%, var(--border))` /** Darker than any outer face — the bore's wall turns away from the light. */ const ISO_FILL_BORE = ISO_FILL_PULSE_LOW const KNOWLEDGE_STROKE_WIDTH = 1.9 @@ -145,11 +146,7 @@ const STACK_FADE = '[-webkit-mask-image:linear-gradient(to_right,#000_56%,transparent_100%),linear-gradient(to_bottom,transparent_0%,#000_40%)] [mask-image:linear-gradient(to_right,#000_56%,transparent_100%),linear-gradient(to_bottom,transparent_0%,#000_40%)] [-webkit-mask-composite:source-in] [mask-composite:intersect]' /** Shared iso contour recipe at this mark's own weight; spread onto both paths and circles. */ -const LINE_PROPS = createIsoLineProps( - undefined, - ISO_STROKE, - KNOWLEDGE_STROKE_WIDTH -) +const LINE_PROPS = createIsoLineProps(ISO_STROKE, KNOWLEDGE_STROKE_WIDTH) /** * Down the hole: the near mouth is floored with the wall tone, then the far mouth @@ -198,7 +195,7 @@ export function KnowledgeIsoMark() { fill='none' aria-hidden='true' focusable='false' - className={cn('block max-w-none shrink-0', STACK_FADE)} + className={cn('block max-w-none shrink-0', STACK_FADE, MASK_NO_REPEAT)} > diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/logs-empty-state.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/logs-empty-state.tsx index 6ea0d5ba336..443e1155888 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/logs-empty-state.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/logs-empty-state.tsx @@ -1,6 +1,7 @@ import { cn } from '@sim/emcn' import { EmptyState } from '@/components/empty-state/empty-state' import { EmptyStateDocsLink } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/docs-link' +import { MASK_NO_REPEAT } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state/mask' const LOGS_DOCS_URL = 'https://docs.sim.ai/logs-debugging' @@ -33,14 +34,14 @@ const FEED_FADE = /** Four rows, sized to the ~148px the other resource graphics occupy so the frame centres the set alike. */ function LogsGraphic() { return ( -