@@ -57,14 +57,29 @@ const EMPTY_FIRING_SET: ReadonlySet<string> = new Set()
5757const PLACE_NODE = "cf-place"
5858const TRANSITION_NODE = "cf-transition"
5959
60+ const PLACE_CIRCLE = 72
61+ const PLACE_LABEL_GAP = 32
6062const PLACE_W = 96
61- const PLACE_H = 96
62- const TRANSITION_W = 128
63- const TRANSITION_H = 56
63+ const PLACE_H = PLACE_CIRCLE + PLACE_LABEL_GAP
64+ const TRANSITION_H = 40
65+ const TRANSITION_MIN_W = 64
66+ const TRANSITION_MAX_W = 200
67+ const TRANSITION_CHAR_PX = 7
68+ const TRANSITION_PAD_PX = 24
69+
70+ function computeTransitionWidth ( transitions : ReadonlyArray < DiagramTransition > ) : number {
71+ let maxLen = 0
72+ for ( const t of transitions ) {
73+ if ( t . name . length > maxLen ) maxLen = t . name . length
74+ }
75+ const raw = maxLen * TRANSITION_CHAR_PX + TRANSITION_PAD_PX
76+ return Math . max ( TRANSITION_MIN_W , Math . min ( TRANSITION_MAX_W , raw ) )
77+ }
6478
6579type PlaceNodeData = DiagramPlace & Record < string , unknown >
6680type TransitionNodeData = DiagramTransition & {
6781 enactmentState : EnactmentState
82+ width : number
6883} & Record < string , unknown >
6984
7085type PlaceNode = Node < PlaceNodeData , typeof PLACE_NODE >
@@ -159,6 +174,8 @@ export function buildGraph(
159174 return { nodes : [ ] , edges : [ ] , isEmpty : true }
160175 }
161176
177+ const transitionW = computeTransitionWidth ( transitions )
178+
162179 const g = new dagre . graphlib . Graph ( )
163180 g . setDefaultEdgeLabel ( ( ) => ( { } ) )
164181 const tight = places . length + transitions . length <= 6
@@ -177,7 +194,7 @@ export function buildGraph(
177194 }
178195 for ( const transition of transitions ) {
179196 g . setNode ( transitionId ( transition . name ) , {
180- width : TRANSITION_W ,
197+ width : transitionW ,
181198 height : TRANSITION_H
182199 } )
183200 }
@@ -207,8 +224,8 @@ export function buildGraph(
207224 return {
208225 id : transitionId ( transition . name ) ,
209226 type : TRANSITION_NODE ,
210- position : { x : pos . x - TRANSITION_W / 2 , y : pos . y - TRANSITION_H / 2 } ,
211- data : { ...transition , enactmentState } as TransitionNodeData ,
227+ position : { x : pos . x - transitionW / 2 , y : pos . y - TRANSITION_H / 2 } ,
228+ data : { ...transition , enactmentState, width : transitionW } as TransitionNodeData ,
212229 sourcePosition : Position . Bottom ,
213230 targetPosition : Position . Top ,
214231 draggable : true ,
@@ -265,49 +282,62 @@ function PlaceNodeViewImpl({ data }: NodeProps<PlaceNode>) {
265282 const hasTokens = data . tokens_count > 0
266283 const tooltip = data . tokens_summary || `${ data . tokens_count } token(s)`
267284 return (
268- < Surface
269- as = "div"
270- title = { tooltip }
271- className = "relative flex h-[96px] w-[96px] items-center justify-center rounded-full border bg-cf-surface text-center shadow-sm"
272- style = { {
273- borderColor : hasTokens ? "var(--color-cf-accent)" : "var(--color-cf-border)" ,
274- borderWidth : hasTokens ? 2 : 1.5
275- } }
276- data-testid = { `place-node-${ data . name } ` }
285+ < div
286+ className = "flex flex-col items-center"
287+ style = { { width : PLACE_W , height : PLACE_H } }
277288 >
278- < Handle
279- type = "target"
280- position = { Position . Left }
281- style = { PLACE_HANDLE_STYLE }
282- isConnectable = { false }
283- />
284- < div className = "flex flex-col items-center gap-0.5 px-2" >
289+ < div className = "relative" style = { { width : PLACE_CIRCLE , height : PLACE_CIRCLE } } >
290+ < Surface
291+ as = "div"
292+ title = { tooltip }
293+ className = "flex h-full w-full items-center justify-center rounded-full border bg-cf-surface shadow-sm"
294+ style = { {
295+ borderColor : hasTokens ? "var(--color-cf-accent)" : "var(--color-cf-border)" ,
296+ borderWidth : hasTokens ? 2 : 1.5
297+ } }
298+ data-testid = { `place-node-${ data . name } ` }
299+ >
300+ < Handle
301+ type = "target"
302+ position = { Position . Left }
303+ style = { PLACE_HANDLE_STYLE }
304+ isConnectable = { false }
305+ />
306+ < Handle
307+ type = "source"
308+ position = { Position . Right }
309+ style = { PLACE_HANDLE_STYLE }
310+ isConnectable = { false }
311+ />
312+ </ Surface >
285313 { hasTokens ? (
286- < span data-testid = { `place-tokens-${ data . name } ` } className = "contents" >
314+ < span
315+ data-testid = { `place-token-badge-${ data . name } ` }
316+ className = "absolute -right-1 -top-1"
317+ >
287318 < Badge
288319 variant = "primary"
289- className = "bg-cf-accent-tint px-2 py- 0 text-base font-semibold leading-none tabular-nums text-cf-accent-ink"
320+ className = "inline-flex size-5 items-center justify-center rounded-full bg-cf-accent-tint p- 0 text-[11px] font-semibold leading-none tabular-nums text-cf-accent-ink shadow-sm "
290321 >
291322 { data . tokens_count }
292323 </ Badge >
293324 </ span >
294325 ) : null }
295- < span className = "max-w-[80px] truncate text-xs font-medium text-cf-ink" >
296- { truncate ( data . name , 12 ) }
326+ </ div >
327+ < div
328+ data-testid = { `place-label-${ data . name } ` }
329+ className = "mt-1 flex w-[120px] flex-col items-center leading-tight"
330+ >
331+ < span className = "max-w-full truncate font-mono text-xs font-medium text-cf-ink" >
332+ { data . name }
297333 </ span >
298334 { data . colour_set ? (
299- < span className = "max-w-[80px] truncate text-[10px] uppercase tracking-wide text-cf-ink-faint " >
300- { truncate ( data . colour_set , 12 ) }
335+ < span className = "max-w-full truncate font-mono text-[10px] text-cf-ink-muted " >
336+ { data . colour_set }
301337 </ span >
302338 ) : null }
303339 </ div >
304- < Handle
305- type = "source"
306- position = { Position . Right }
307- style = { PLACE_HANDLE_STYLE }
308- isConnectable = { false }
309- />
310- </ Surface >
340+ </ div >
311341 )
312342}
313343
@@ -341,8 +371,10 @@ function TransitionNodeViewImpl({ data }: NodeProps<TransitionNode>) {
341371 return (
342372 < Surface
343373 as = "div"
344- className = "flex h-[56px] w-[128px] items-center justify-center rounded-md border bg-cf-surface text-center shadow-sm"
374+ className = "flex items-center justify-center rounded-md border bg-cf-surface text-center shadow-sm"
345375 style = { {
376+ width : data . width ,
377+ height : TRANSITION_H ,
346378 borderColor : "var(--color-cf-border)" ,
347379 borderWidth : 1.5 ,
348380 boxShadow : pulseShadow ?? baseShadow ,
@@ -361,8 +393,8 @@ function TransitionNodeViewImpl({ data }: NodeProps<TransitionNode>) {
361393 style = { PLACE_HANDLE_STYLE }
362394 isConnectable = { false }
363395 />
364- < span className = "max-w-[112px] truncate px-2 text-xs font-medium text-cf-ink" >
365- { truncate ( data . name , 16 ) }
396+ < span className = "truncate px-4 font-mono text-xs font-medium text-cf-ink" >
397+ { data . name }
366398 </ span >
367399 < Handle
368400 type = "source"
@@ -401,7 +433,3 @@ function usePulseOnChange(value: string | null): boolean {
401433 return pulsing
402434}
403435
404- function truncate ( value : string , max : number ) : string {
405- if ( value . length <= max ) return value
406- return `${ value . slice ( 0 , max - 1 ) } …`
407- }
0 commit comments