Skip to content

Commit 2fcbe72

Browse files
committed
refactor(resources): migrate log and knowledge onto the axes; collapse tab chrome
Continues the resource-view layer. Three kinds now have a canonical view (file, log, knowledge) and only `table` remains — deliberately, because the mothership mounts its editing shell, which is a product decision rather than unfinished work. **log** — `log-details/` splits into `components/resources/log-view/` (the view) and a 198-line shell that keeps resize, keyboard nav and close chrome. Four leaked contexts resolved: `useParams` → `source.workspaceId`, `useRouter` → an `onNavigate` prop, `useQueryState(tab)` → view-owned state gated by `hostOwnsUrl(host)`, `usePermissionConfig` → a required `showExecutionInternals` prop (deliberately not folded into `grants`: it gates *seeing* trace internals, not write or run, and an optional field would default permissive — the wrong failure mode for a permission-group restriction). A fifth leak the plan missed — `file-download.tsx` holding its own `useRouter` — was caught by the gate. **knowledge** — `base.tsx` (1733 lines) splits into a 909-line canonical view and a 974-line shell. The read surface (document list, filters, sort, pagination, unavailable state) is the view; upload, connectors, tag editing, rename, delete and bulk operations stay in the shell, exactly as the tables editing grid kept its write path. All six nuqs keys now route through `useKnowledgeListState(host)`, whose every write sits behind `hostOwnsUrl`. **tab chrome** — the five `Embedded*Actions` collapse into one kind-keyed `ResourceTabActions`. They differed only in icon, copy and destination, which is a config table. `knowledge` and `table` destinations now resolve through `workspaceSource().hrefFor()` instead of hand-built strings. **schedule** is removed as a resource kind: its page was deleted upstream, its `resourceHref` case pointed at a route that no longer exists, and `MothershipResourceType` never carried it, so it was unreachable. Behavior change, intended and limited to embedded surfaces: the mothership panel no longer writes the log `?tab` key or the six knowledge keys into the host page's address bar. That is the bug the `host` axis exists to fix. Both route pages keep their deep-linkable params unchanged. `Resource` and `InlineRenameInput` moved to `components/` — mandatory, since a canonical unit may not import the workspace route tree. The workspace barrel re-exports both, so its consumers are byte-unchanged. Ratchets: shadow-named components 8 → 2 (the 2 left are deliberate — `EmbeddedWorkflow`, because a workflow is a live collaborative session rather than a document with an address, and `EmbeddedFolder`, because a folder is structure inside a resource); cross-tree imports 38 → 37. None raised. Verification: apps/sim + 22 packages typecheck, biome clean, 18626 tests pass, and all 15 repo gates pass including both strict variants. Not visually verified.
1 parent 001cbb4 commit 2fcbe72

76 files changed

Lines changed: 3499 additions & 2978 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/rules/sim-resource-views.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ paths:
1212

1313
# Resource Views
1414

15-
A **resource** is a thing a workspace holds that can also be shared: a file, a table, an interface, a knowledge base, a log, a scheduled task. A resource with a canonical view has **exactly one**, and every consumer mounts that one — the workspace route page, the mothership panel, an interface module, and the public share page.
15+
A **resource** is a thing a workspace holds that can also be shared: a file, a table, an interface, a knowledge base, a log. A resource with a canonical view has **exactly one**, and every consumer mounts that one — the workspace route page, the mothership panel, an interface module, and the public share page.
1616

1717
**One view per resource. Consumers construct the axes and mount it. They never wrap it.**
1818

@@ -30,14 +30,14 @@ Enforced by `bun run check:resources` (strict CI gate: `bun run check:resources:
3030

3131
There is no fourth axis. Agent streaming is **one optional prop on `FileView`** (`streaming?: FileViewStreaming`), because only files stream.
3232

33-
`ShareSource` declares `workspaceId?: never` and `resourceId?: never`, and `WorkspaceSource` declares `token?: never` and `seed?: never`. A share source **cannot** carry a workspace id — that is a compile error, not a convention. A kind whose seed is typed `never` (`knowledge`, `log`, `schedule`) structurally cannot construct a share source at all: "no public surface" is a compile-time fact.
33+
`ShareSource` declares `workspaceId?: never` and `resourceId?: never`, and `WorkspaceSource` declares `token?: never` and `seed?: never`. A share source **cannot** carry a workspace id — that is a compile error, not a convention. A kind whose seed is typed `never` (`table`, `knowledge`, `log`) structurally cannot construct a share source at all: "no public surface" is a compile-time fact.
3434

3535
```
3636
apps/sim/resources/ # kinds.ts · source.ts · grants.ts · host.ts — pure TS
3737
apps/sim/components/resources/<unit>/ # 'use client' — THE view, one per resource
3838
```
3939

40-
A resource kind with no canonical view yet (`table`, `knowledge`, `log`, `schedule`) is simply **absent** from `CANONICAL_UNITS` in the check. That is the correct state for an unmigrated kind. Do not add a flag, a shim, or a placeholder entry for it.
40+
A resource kind with no canonical view yet (`table`, `knowledge`, `log`) is simply **absent** from `CANONICAL_UNITS` in the check. That is the correct state for an unmigrated kind. Do not add a flag, a shim, or a placeholder entry for it.
4141

4242
## Consume: construct the axes, then mount
4343

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,15 +416,15 @@ Principles when building or migrating shared UI:
416416

417417
## Resource Views
418418

419-
A **resource** is a thing a workspace holds that can also be shared — a file, a table, a knowledge base, a log, a scheduled task. A resource with a canonical view has exactly ONE, in `apps/sim/components/resources/<unit>/`, mounted by every consumer: the workspace route page, the mothership panel, the public share page.
419+
A **resource** is a thing a workspace holds that can also be shared — a file, a table, a knowledge base, a log. A resource with a canonical view has exactly ONE, in `apps/sim/components/resources/<unit>/`, mounted by every consumer: the workspace route page, the mothership panel, the public share page.
420420

421421
Views are mounted against exactly **three axes**, defined in `apps/sim/resources/**` (pure TypeScript — no React, no `'use client'`, because a Server Component builds a share source during SSR):
422422

423423
- `source` — where the data comes from and by what address: `WorkspaceSource<K> | ShareSource<K>`, discriminated on `via`. Replaces `workspaceId`, `token`, `contentSource`, `isPublic`. `ShareSource` declares `workspaceId?: never`, so a share token can no longer be laundered through a workspace-shaped slot.
424424
- `grants` — what this viewer may do: `{ write, run }`. Replaces `canEdit`, `canRun`, `canAdmin`, `disableEdit/Insert/Delete`.
425425
- `host` — who owns the URL, the router, the document frame: `'page' | 'panel' | 'public'`. Replaces `embedded`. `hostOwnsUrl(host)` is the one place the "embedded views do not write nuqs keys" rule lives.
426426

427-
There is no fourth axis; agent streaming is one optional prop on `FileView`. Consumers CONSTRUCT the axes and MOUNT the view — never wrap it in a passthrough, never reach past its barrel, never reimplement its UI because it lacks a seam (add the seam), never import `@/app/workspace/[workspaceId]/**` from an anonymous surface (`app/f/**`, `app/(interfaces)/**`), and never read `useRouter`/`useParams`/`useQueryState`/`useUserPermissionsContext` inside a unit. A kind with no canonical view yet (`knowledge`, `log`, `schedule`) is simply absent from the check's `CANONICAL_UNITS` — no flag, shim, or placeholder.
427+
There is no fourth axis; agent streaming is one optional prop on `FileView`. Consumers CONSTRUCT the axes and MOUNT the view — never wrap it in a passthrough, never reach past its barrel, never reimplement its UI because it lacks a seam (add the seam), never import `@/app/workspace/[workspaceId]/**` from an anonymous surface (`app/f/**`, `app/(interfaces)/**`), and never read `useRouter`/`useParams`/`useQueryState`/`useUserPermissionsContext` inside a unit. A kind with no canonical view yet (`knowledge`, `log`) is simply absent from the check's `CANONICAL_UNITS` — no flag, shim, or placeholder.
428428

429429
Enforced by `bun run check:resources` (strict: `check:resources:strict`), which ratchets counters for wrappers, imports past a barrel, cross-tree imports, unsanctioned props, token-as-`workspaceId`, and context leaks. Escape hatches — reason mandatory, on the line directly above: `// boundary-resource-wrapper:`, `// boundary-resource-internal:`, `// boundary-resource-tree:`, `// boundary-resource-prop:`. Full rules in `.claude/rules/sim-resource-views.md`.
430430

apps/sim/app/_shell/desktop-title-bar-surfaces.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const logoShell = read('../(landing)/components/logo-shell/logo-shell.tsx')
4040
const pageHeaderBar = read('../../components/page-header-bar.ts')
4141
const settingsHeader = read('../../components/settings/settings-header.tsx')
4242
const resourceHeader = read(
43-
'../workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx'
43+
'../../components/resource/components/resource-header/resource-header.tsx'
4444
)
4545
const mothershipView = read(
4646
'../workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx'

apps/sim/app/workspace/[workspaceId]/components/folders/folder-breadcrumbs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type {
33
BreadcrumbEditing,
44
BreadcrumbItem,
55
DropdownOption,
6-
} from '@/app/workspace/[workspaceId]/components/resource/components/resource-header'
6+
} from '@/components/resource/components/resource-header'
77
import type { WorkflowFolder } from '@/stores/folders/types'
88

99
export interface FolderBreadcrumbItemsOptions {

apps/sim/app/workspace/[workspaceId]/components/folders/folder-row.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { Folder } from '@sim/emcn/icons'
2+
import type { ResourceCell, ResourceRow } from '@/components/resource/resource'
23
import { folderRowId } from '@/app/workspace/[workspaceId]/components/folders/folder-row-id'
3-
import type {
4-
ResourceCell,
5-
ResourceRow,
6-
} from '@/app/workspace/[workspaceId]/components/resource/resource'
74
import type { WorkflowFolder } from '@/stores/folders/types'
85

96
const FOLDER_ICON = <Folder className='size-[14px]' />

apps/sim/app/workspace/[workspaceId]/components/folders/use-folder-row-drag-drop.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use client'
22

33
import { type DragEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
4+
import type { RowDragDropConfig } from '@/components/resource/resource'
45
import { parseFolderedRowId } from '@/app/workspace/[workspaceId]/components/folders/folder-row-id'
5-
import type { RowDragDropConfig } from '@/app/workspace/[workspaceId]/components/resource/resource'
66

77
/**
88
* Private drag payload, namespaced so a drag started on another Sim surface (or an external
Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,47 @@
1-
export { ConversationListItem } from './conversation-list-item'
2-
export type { ErrorBoundaryProps, ErrorStateProps } from './error'
3-
export { ErrorShell, ErrorState } from './error'
4-
export { InlineRenameInput } from './inline-rename-input'
5-
export { IntegrationTabsHeader } from './integration-tabs-header'
6-
export { MessageActions } from './message-actions'
7-
export { FloatingOverflowText } from './resource/components/floating-overflow-text'
1+
/**
2+
* `Resource` and `InlineRenameInput` are re-exported from `@/components/**`:
3+
* they moved out of this tree because the canonical resource views under
4+
* `components/resources/**` mount them too, and a shared unit may not import
5+
* `@/app/workspace/[workspaceId]/**`. The workspace tree keeps reaching them
6+
* through this barrel, which stays its single aggregated entry point.
7+
*/
8+
export { InlineRenameInput } from '@/components/inline-rename-input'
89
export {
10+
type BreadcrumbEditing,
11+
type BreadcrumbItem,
12+
type ChromeActionSpec,
13+
type ColumnOption,
14+
type DropdownOption,
15+
EMPTY_CELL_PLACEHOLDER,
16+
type FilterConfig,
17+
type FilterTag,
18+
FloatingOverflowText,
919
type MemberFilterOption,
1020
memberFilterOptions,
1121
ownerCell,
12-
} from './resource/components/owner-cell'
13-
export {
14-
type ChromeActionSpec,
22+
type PaginationConfig,
23+
Resource,
24+
type ResourceAction,
25+
type ResourceCell,
26+
type ResourceCellEditing,
1527
ResourceChromeFallback,
16-
} from './resource/components/resource-chrome-fallback'
17-
export type {
18-
BreadcrumbEditing,
19-
BreadcrumbItem,
20-
DropdownOption,
21-
ResourceAction,
22-
} from './resource/components/resource-header'
23-
export type {
24-
ColumnOption,
25-
FilterConfig,
26-
FilterTag,
27-
SearchConfig,
28-
SearchTag,
29-
SortConfig,
30-
} from './resource/components/resource-options'
31-
export { SortDropdown } from './resource/components/resource-options'
32-
export { timeCell } from './resource/components/time-cell'
33-
export type {
34-
PaginationConfig,
35-
ResourceCell,
36-
ResourceCellEditing,
37-
ResourceColumn,
38-
ResourceRow,
39-
ResourceTableHandle,
40-
RowDragDropConfig,
41-
SelectableConfig,
42-
} from './resource/resource'
43-
export { EMPTY_CELL_PLACEHOLDER, Resource } from './resource/resource'
44-
export { useBackgroundContextMenu } from './resource/use-background-context-menu'
28+
type ResourceColumn,
29+
type ResourceRow,
30+
type ResourceTableHandle,
31+
type RowDragDropConfig,
32+
type SearchConfig,
33+
type SearchTag,
34+
type SelectableConfig,
35+
type SortConfig,
36+
SortDropdown,
37+
timeCell,
38+
useBackgroundContextMenu,
39+
} from '@/components/resource'
40+
export { ConversationListItem } from './conversation-list-item'
41+
export type { ErrorBoundaryProps, ErrorStateProps } from './error'
42+
export { ErrorShell, ErrorState } from './error'
43+
export { IntegrationTabsHeader } from './integration-tabs-header'
44+
export { MessageActions } from './message-actions'
4545
export { ResourceTile } from './resource-tile'
4646
export { ShareModal, type ShareModalProps } from './share-modal'
4747
export { SkillTile } from './skill-tile'

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
Tooltip,
1717
} from '@sim/emcn'
1818
import { Folder, Plus } from '@sim/emcn/icons'
19+
import { formatDate } from '@/components/resources/log-view'
1920
import { isBrowserAgentAvailable } from '@/lib/browser-agent/transport'
2021
import {
2122
BROWSER_SESSION_RESOURCE_ID,
@@ -36,7 +37,6 @@ import type {
3637
MothershipResource,
3738
MothershipResourceType,
3839
} from '@/app/workspace/[workspaceId]/home/types'
39-
import { formatDate } from '@/app/workspace/[workspaceId]/logs/utils'
4040
import { listIntegrations } from '@/blocks/integration-matcher'
4141
import { useFolders } from '@/hooks/queries/folders'
4242
import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge'

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export { AddResourceDropdown, useAvailableResources } from './add-resource-dropdown'
2-
export { ResourceActions, ResourceContent } from './resource-content'
2+
export { ResourceContent, ResourceTabActions } from './resource-content'
33
export type { ResourceTypeConfig } from './resource-registry'
44
export {
55
getResourceConfig,
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { ResourceActions, ResourceContent } from './resource-content'
1+
export { ResourceContent, ResourceTabActions } from './resource-content'

0 commit comments

Comments
 (0)