@@ -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
137165const BARE_DOMAIN_RE = / ^ ( [ a - z A - Z 0 - 9 ] ( [ a - z A - Z 0 - 9 - ] { 0 , 61 } [ a - z A - Z 0 - 9 ] ) ? \. ) + [ a - z A - Z ] { 2 , } $ /
0 commit comments