Skip to content

Commit f2bfa14

Browse files
fix(tables): workflow-output cells format values like normal cells
Workflow-output columns short-circuited in resolveCellRender and rendered their value as plain text, so a sim-resource URL / external URL / JSON / date produced by a workflow never got the chip, favicon link, or typed formatting a normal cell gets. Factor value formatting into a shared `resolveValueKind` helper used by both the workflow-value branch and the plain-cell branch; the workflow branch keeps the typewriter reveal for plain streaming text via a `typewriter` flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 172c183 commit f2bfa14

1 file changed

Lines changed: 51 additions & 23 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells/cell-render.tsx

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ export function resolveCellRender({
7777
// Value wins over pending-upstream: a finished column stays finished even
7878
// while other blocks in the group are still running. An empty string is not
7979
// a value — it falls through so a completed enrichment can show "Not found".
80-
if (!isEmpty) return { kind: 'value', text: stringifyValue(value) }
80+
// Format the value exactly like a plain cell (resource chip / URL / JSON /
81+
// date / boolean), keeping the typewriter reveal for plain streaming text.
82+
if (!isEmpty) return resolveValueKind(value, column, currentWorkspaceId, { typewriter: true })
8183

8284
if (inFlight && !(groupHasBlockErrors && !blockRunning)) {
8385
// A `pending` cell whose jobId starts with `paused-` is mid-pause
@@ -103,35 +105,61 @@ export function resolveCellRender({
103105
return { kind: 'empty' }
104106
}
105107

108+
return resolveValueKind(value, column, currentWorkspaceId, { typewriter: false })
109+
}
110+
111+
function stringifyValue(value: unknown): string {
112+
if (typeof value === 'string') return value
113+
if (value === null || value === undefined) return ''
114+
return JSON.stringify(value)
115+
}
116+
117+
/** Returns a `sim-resource` cell kind when `text` is a URL pointing to a
118+
* resource in the current workspace, else null. Shared by plain string cells
119+
* and workflow-output value cells so both surface in-workspace resource links
120+
* as tagged chips. */
121+
function resolveSimResourceKind(
122+
text: string,
123+
currentWorkspaceId: string | undefined
124+
): Extract<CellRenderKind, { kind: 'sim-resource' }> | null {
125+
if (!currentWorkspaceId) return null
126+
const resource = extractSimResourceInfo(text)
127+
if (!resource || resource.workspaceId !== currentWorkspaceId) return null
128+
return {
129+
kind: 'sim-resource',
130+
workspaceId: resource.workspaceId,
131+
resourceType: resource.resourceType,
132+
resourceId: resource.resourceId,
133+
href: resource.href,
134+
}
135+
}
136+
137+
/**
138+
* Maps a present (non-empty) cell value to its render kind based on the column
139+
* type — the shared formatter for plain cells and workflow-output value cells,
140+
* so a workflow output renders booleans, JSON, dates, resource chips and URL
141+
* links exactly like a normal cell. `typewriter` selects the plain-text
142+
* fallback: `value` (animated reveal, for streaming workflow outputs) vs `text`
143+
* (static, for plain cells).
144+
*/
145+
function resolveValueKind(
146+
value: unknown,
147+
column: DisplayColumn,
148+
currentWorkspaceId: string | undefined,
149+
opts: { typewriter: boolean }
150+
): CellRenderKind {
106151
if (column.type === 'boolean') return { kind: 'boolean', checked: Boolean(value) }
107-
if (isNull) return { kind: 'empty' }
152+
if (value === null || value === undefined) return { kind: 'empty' }
108153
if (column.type === 'json') return { kind: 'json', text: JSON.stringify(value) }
109154
if (column.type === 'date') return { kind: 'date', text: String(value) }
155+
const text = stringifyValue(value)
110156
if (column.type === 'string') {
111-
const text = stringifyValue(value)
112-
if (currentWorkspaceId) {
113-
const resource = extractSimResourceInfo(text)
114-
if (resource && resource.workspaceId === currentWorkspaceId) {
115-
return {
116-
kind: 'sim-resource',
117-
workspaceId: resource.workspaceId,
118-
resourceType: resource.resourceType,
119-
resourceId: resource.resourceId,
120-
href: resource.href,
121-
}
122-
}
123-
}
157+
const simKind = resolveSimResourceKind(text, currentWorkspaceId)
158+
if (simKind) return simKind
124159
const urlInfo = extractUrlInfo(text)
125160
if (urlInfo) return { kind: 'url', text, href: urlInfo.href, domain: urlInfo.domain }
126-
return { kind: 'text', text }
127161
}
128-
return { kind: 'text', text: stringifyValue(value) }
129-
}
130-
131-
function stringifyValue(value: unknown): string {
132-
if (typeof value === 'string') return value
133-
if (value === null || value === undefined) return ''
134-
return JSON.stringify(value)
162+
return opts.typewriter ? { kind: 'value', text } : { kind: 'text', text }
135163
}
136164

137165
const BARE_DOMAIN_RE = /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/

0 commit comments

Comments
 (0)