From 0f259e08b6b7d50365a27f82f9ac11b04b5560ad Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sun, 10 May 2026 12:53:37 -0700 Subject: [PATCH 1/3] fix(settings): accurate View navigation after restore in recently deleted Files now deep-link to /files/{id} (was /files), and folders expand the restored folder + its parent chain in the sidebar before navigating to /w so the user actually lands on the item they restored. Co-Authored-By: Claude Opus 4.7 --- .../recently-deleted/recently-deleted.tsx | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx index f3922176f7..94f624c436 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx @@ -1,6 +1,7 @@ 'use client' import { useMemo, useState } from 'react' +import { toError } from '@sim/utils/errors' import { formatDate } from '@sim/utils/formatting' import { Folder, Search } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' @@ -21,6 +22,10 @@ import { useKnowledgeBasesQuery, useRestoreKnowledgeBase } from '@/hooks/queries import { useRestoreTable, useTablesList } from '@/hooks/queries/tables' import { useRestoreWorkflow, useWorkflows } from '@/hooks/queries/workflows' import { useRestoreWorkspaceFile, useWorkspaceFiles } from '@/hooks/queries/workspace-files' +import { useFolderStore } from '@/stores/folders/store' +import type { WorkflowFolder } from '@/stores/folders/types' + +type ResourceType = 'all' | 'workflow' | 'table' | 'knowledge' | 'file' | 'folder' function getResourceHref( workspaceId: string, @@ -36,14 +41,12 @@ function getResourceHref( case 'knowledge': return `${base}/knowledge/${id}` case 'file': - return `${base}/files` + return `${base}/files/${id}` case 'folder': return `${base}/w` } } -type ResourceType = 'all' | 'workflow' | 'table' | 'knowledge' | 'file' | 'folder' - type SortColumn = 'deleted' | 'name' | 'type' interface SortConfig { @@ -119,13 +122,9 @@ function ResourceIcon({ resource }: { resource: DeletedResource }) { const mothershipType = RESOURCE_TYPE_TO_MOTHERSHIP[resource.type] if (!mothershipType) return null const config = RESOURCE_REGISTRY[mothershipType] - return ( - <> - {config.renderTabIcon( - { type: mothershipType, id: resource.id, title: resource.name }, - ICON_CLASS - )} - + return config.renderTabIcon( + { type: mothershipType, id: resource.id, title: resource.name }, + ICON_CLASS ) } @@ -141,6 +140,7 @@ export function RecentlyDeleted() { const workflowsQuery = useWorkflows(workspaceId, { scope: 'archived' }) const foldersQuery = useFolders(workspaceId, { scope: 'archived' }) + const activeFoldersQuery = useFolders(workspaceId) const tablesQuery = useTablesList(workspaceId, 'archived') const knowledgeQuery = useKnowledgeBasesQuery(workspaceId, { scope: 'archived' }) const filesQuery = useWorkspaceFiles(workspaceId, 'archived') @@ -220,7 +220,6 @@ export function RecentlyDeleted() { }) } - // Merge back restored items that are no longer in the query data const itemIds = new Set(items.map((i) => i.id)) for (const [id, resource] of restoredItems) { if (!itemIds.has(id)) { @@ -267,6 +266,23 @@ export function RecentlyDeleted() { const showNoResults = searchTerm.trim() && filtered.length === 0 && resources.length > 0 const selectedSort = activeSort ?? DEFAULT_SORT + function handleView(resource: DeletedResource) { + if (resource.type === 'folder') { + const setExpanded = useFolderStore.getState().setExpanded + const byId = new Map( + (activeFoldersQuery.data ?? []).map((folder) => [folder.id, folder]) + ) + let current: WorkflowFolder | undefined = byId.get(resource.id) + const seen = new Set() + while (current && !seen.has(current.id)) { + seen.add(current.id) + setExpanded(current.id, true) + current = current.parentId ? byId.get(current.parentId) : undefined + } + } + router.push(getResourceHref(resource.workspaceId, resource.type, resource.id)) + } + function handleRestore(resource: DeletedResource) { setRestoringIds((prev) => new Set(prev).add(resource.id)) @@ -315,7 +331,7 @@ export function RecentlyDeleted() {

- {error instanceof Error ? error.message : 'Failed to load deleted items'} + {toError(error).message || 'Failed to load deleted items'}

) : isLoading ? ( @@ -387,7 +403,7 @@ export function RecentlyDeleted() { return (
@@ -405,15 +421,7 @@ export function RecentlyDeleted() { {isRestored ? (
Restored -
From 92cca4057d1ea08b2e3d5b076c488cde59192ef1 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sun, 10 May 2026 13:00:53 -0700 Subject: [PATCH 2/3] fix: fall back to archived folders for parent-chain lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The restored folder may not be in the active folders cache yet when View is clicked (the invalidation+refetch fires from onSettled, after onSuccess surfaces the View button). Merge archived folder data — where the restored item still lives — into the lookup map so the expansion loop can always resolve the folder and walk its parent chain. Co-Authored-By: Claude Opus 4.7 --- .../components/recently-deleted/recently-deleted.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx index 94f624c436..56642b89d3 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx @@ -269,9 +269,9 @@ export function RecentlyDeleted() { function handleView(resource: DeletedResource) { if (resource.type === 'folder') { const setExpanded = useFolderStore.getState().setExpanded - const byId = new Map( - (activeFoldersQuery.data ?? []).map((folder) => [folder.id, folder]) - ) + const byId = new Map() + for (const folder of foldersQuery.data ?? []) byId.set(folder.id, folder) + for (const folder of activeFoldersQuery.data ?? []) byId.set(folder.id, folder) let current: WorkflowFolder | undefined = byId.get(resource.id) const seen = new Set() while (current && !seen.has(current.id)) { From bb58ef3feed4ff1027b2b4ac547151a183eb9475 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sun, 10 May 2026 13:10:59 -0700 Subject: [PATCH 3/3] chore(styling): canonicalize size-* shorthand for equal height/width Document the size-* shorthand as the canonical pattern across CLAUDE.md, AGENTS.md, .claude rules, .cursor + .agents commands, and the emcn design-review skill. Default icon size is size-[14px]. Treat h-[Npx] w-[Npx] and h-N w-N pairs as refactor targets. Also migrate the remaining occurrences in recently-deleted.tsx. Co-Authored-By: Claude Opus 4.7 --- .agents/skills/emcn-design-review/SKILL.md | 25 +++++++++++-------- .claude/commands/emcn-design-review.md | 2 +- .claude/rules/emcn-components.md | 1 + .claude/rules/sim-styling.md | 3 ++- .cursor/commands/emcn-design-review.md | 2 +- AGENTS.md | 6 +++++ CLAUDE.md | 6 +++++ .../recently-deleted/recently-deleted.tsx | 4 +-- 8 files changed, 33 insertions(+), 16 deletions(-) diff --git a/.agents/skills/emcn-design-review/SKILL.md b/.agents/skills/emcn-design-review/SKILL.md index 415a85046d..04e39da1f8 100644 --- a/.agents/skills/emcn-design-review/SKILL.md +++ b/.agents/skills/emcn-design-review/SKILL.md @@ -234,7 +234,7 @@ Use for context menus and action menus: @@ -281,19 +281,21 @@ Rules: - Stack multiple skeletons for lists ### Icons -Standard sizing — `h-[14px] w-[14px]` is the dominant pattern (400+ uses): +Standard sizing — use the `size-*` shorthand. `size-[14px]` is the dominant pattern: ```tsx - + ``` -Size scale by frequency: -1. `h-[14px] w-[14px]` — default for inline icons (most common) -2. `h-[16px] w-[16px]` — slightly larger inline icons -3. `h-3 w-3` (12px) — compact/tight spaces -4. `h-4 w-4` (16px) — Tailwind equivalent, also common -5. `h-3.5 w-3.5` (14px) — Tailwind equivalent of 14px -6. `h-5 w-5` (20px) — larger icons, section headers +Always prefer `size-*` over the legacy `h-* w-*` pair. `size-[14px]` is canonical; treat any `h-[Npx] w-[Npx]` or `h-N w-N` pair as a refactor target. + +Size scale (most common first): +1. `size-[14px]` — default for inline icons +2. `size-[16px]` — slightly larger inline icons +3. `size-3` (12px) — compact/tight spaces +4. `size-4` (16px) — Tailwind equivalent +5. `size-3.5` (14px) — Tailwind equivalent of 14px +6. `size-5` (20px) — larger icons, section headers Use `text-[var(--text-icon)]` for icon color (113+ uses in codebase). @@ -332,4 +334,5 @@ Use `text-[var(--text-icon)]` for icon color (113+ uses in codebase). - Importing from emcn subpaths instead of barrel export - Using arbitrary z-index (`z-50`, `z-[9999]`) instead of z-index tokens - Custom shadows instead of shadow tokens -- Icon sizes that don't follow the established scale (default to `h-[14px] w-[14px]`) +- Icon sizes that don't follow the established scale (default to `size-[14px]`) +- Splitting equal height/width into `h-* w-*` pairs instead of the `size-*` shorthand diff --git a/.claude/commands/emcn-design-review.md b/.claude/commands/emcn-design-review.md index ecfad5bcc2..e1e3860ecc 100644 --- a/.claude/commands/emcn-design-review.md +++ b/.claude/commands/emcn-design-review.md @@ -65,7 +65,7 @@ Modal `size="sm"`, title "Delete/Remove {ItemType}", `variant="destructive"` act ## Icons -Default: `h-[14px] w-[14px]` (400+ uses). Color: `text-[var(--text-icon)]`. Scale: 14px > 16px > 12px > 20px. +Default: `size-[14px]`. Color: `text-[var(--text-icon)]`. Scale: 14px > 16px > 12px > 20px. Use the `size-*` shorthand — flag `h-[Npx] w-[Npx]` and `h-N w-N` pairs as refactor targets. ## Anti-patterns to flag diff --git a/.claude/rules/emcn-components.md b/.claude/rules/emcn-components.md index 011a3280f4..53373bc923 100644 --- a/.claude/rules/emcn-components.md +++ b/.claude/rules/emcn-components.md @@ -32,4 +32,5 @@ function Label({ className, ...props }) { - Export component and variants (if using CVA) - TSDoc with usage examples - Consistent tokens: `font-medium`, `text-[12px]`, `rounded-[4px]` +- Equal height/width → `size-*` (e.g. `size-[14px]`, `size-4`), never `h-[Npx] w-[Npx]` or `h-N w-N`. Default icon size is `size-[14px]` - `transition-colors` for hover states diff --git a/.claude/rules/sim-styling.md b/.claude/rules/sim-styling.md index 1b8c384a70..65911694a4 100644 --- a/.claude/rules/sim-styling.md +++ b/.claude/rules/sim-styling.md @@ -11,7 +11,8 @@ paths: 1. **No inline styles** - Use Tailwind classes 2. **No duplicate dark classes** - Skip `dark:` when value matches light mode 3. **Exact values** - `text-[14px]`, `h-[26px]` -4. **Transitions** - `transition-colors` for interactive states +4. **Equal h/w → `size-*`** - Use `size-[14px]` / `size-4`, never `h-[14px] w-[14px]` or `h-4 w-4`. Default icon size is `size-[14px]` +5. **Transitions** - `transition-colors` for interactive states ## Conditional Classes diff --git a/.cursor/commands/emcn-design-review.md b/.cursor/commands/emcn-design-review.md index a68e96eb9b..e51e5bc444 100644 --- a/.cursor/commands/emcn-design-review.md +++ b/.cursor/commands/emcn-design-review.md @@ -60,7 +60,7 @@ Modal `size="sm"`, title "Delete/Remove {ItemType}", `variant="destructive"` act ## Icons -Default: `h-[14px] w-[14px]` (400+ uses). Color: `text-[var(--text-icon)]`. Scale: 14px > 16px > 12px > 20px. +Default: `size-[14px]`. Color: `text-[var(--text-icon)]`. Scale: 14px > 16px > 12px > 20px. Use the `size-*` shorthand — flag `h-[Npx] w-[Npx]` and `h-N w-N` pairs as refactor targets. ## Anti-patterns to flag diff --git a/AGENTS.md b/AGENTS.md index 0ab9bf6eb1..cfa04710cf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -357,6 +357,12 @@ Use Tailwind only, no inline styles. Use `cn()` from `@/lib/utils` for condition
``` +For equal height and width, use the `size-*` shorthand — never `h-[Npx] w-[Npx]` or `h-N w-N`. Default icon size is `size-[14px]`. + +```typescript + +``` + ## EMCN Components Import from `@/components/emcn`, never from subpaths (except CSS files). Use CVA when 2+ variants exist. diff --git a/CLAUDE.md b/CLAUDE.md index 4b27c31857..9807d221ca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -347,6 +347,12 @@ Use Tailwind only, no inline styles. Use `cn()` from `@/lib/utils` for condition
``` +For equal height and width, use the `size-*` shorthand — never `h-[Npx] w-[Npx]` or `h-N w-N`. Default icon size is `size-[14px]`. + +```typescript + +``` + ## EMCN Components Import from `@/components/emcn`, never from subpaths (except CSS files). Use CVA when 2+ variants exist. diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx index 56642b89d3..31215b1cac 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx @@ -62,7 +62,7 @@ const SORT_OPTIONS: { column: SortColumn; direction: 'asc' | 'desc'; label: stri { column: 'type', direction: 'asc', label: 'Type (A–Z)' }, ] -const ICON_CLASS = 'h-[14px] w-[14px]' +const ICON_CLASS = 'size-[14px]' const RESOURCE_TYPE_TO_MOTHERSHIP: Partial< Record, MothershipResourceType> @@ -331,7 +331,7 @@ export function RecentlyDeleted() {