@@ -3,6 +3,8 @@ import { CanvasRenderer } from '@geometra/renderer-canvas'
33import replay from '../../examples/replays/claims-review.json'
44
55const selectedStep = signal ( 0 )
6+ const viewportWidth = signal ( window . innerWidth )
7+ const viewportHeight = signal ( window . innerHeight )
68const action = replay . actions [ 0 ]
79const before = action ?. frameBefore
810const after = action ?. frameAfter
@@ -28,8 +30,14 @@ function label(value: unknown): string {
2830 return JSON . stringify ( value )
2931}
3032
33+ function outputField ( name : string ) : string {
34+ if ( ! action ?. output || typeof action . output !== 'object' ) return 'n/a'
35+ return label ( ( action . output as Record < string , unknown > ) [ name ] )
36+ }
37+
3138function card ( title : string , lines : string [ ] , accent = colors . border ) {
3239 return box ( {
40+ flexDirection : 'column' ,
3341 backgroundColor : colors . card ,
3442 borderColor : accent ,
3543 borderWidth : 1 ,
@@ -38,14 +46,17 @@ function card(title: string, lines: string[], accent = colors.border) {
3846 gap : 8 ,
3947 } , [
4048 text ( { text : title , font : '700 18px Inter, system-ui' , lineHeight : 24 , color : colors . text } ) ,
41- ...lines . map ( line => text ( { text : line , font : '14px Inter, system-ui' , lineHeight : 21 , color : colors . muted } ) ) ,
49+ ...lines . map ( line => text ( { text : line , font : '14px Inter, system-ui' , lineHeight : 21 , color : colors . muted , whiteSpace : 'normal' } ) ) ,
4250 ] )
4351}
4452
4553function framePreview ( title : string , node : typeof beforeNode | typeof afterNode , status : string ) {
4654 const bounds = node ?. bounds
4755 return box ( {
48- flex : 1 ,
56+ flexDirection : 'column' ,
57+ flexGrow : 1 ,
58+ flexShrink : 1 ,
59+ flexBasis : 0 ,
4960 backgroundColor : '#020617' ,
5061 borderColor : colors . border ,
5162 borderWidth : 1 ,
@@ -55,7 +66,8 @@ function framePreview(title: string, node: typeof beforeNode | typeof afterNode,
5566 } , [
5667 text ( { text : title , font : '700 17px Inter, system-ui' , lineHeight : 24 , color : colors . text } ) ,
5768 box ( {
58- height : 178 ,
69+ flexDirection : 'column' ,
70+ height : 132 ,
5971 backgroundColor : colors . panel ,
6072 borderColor : colors . border ,
6173 borderWidth : 1 ,
@@ -66,6 +78,7 @@ function framePreview(title: string, node: typeof beforeNode | typeof afterNode,
6678 text ( { text : 'Claims review surface' , font : '700 15px Inter, system-ui' , lineHeight : 20 , color : colors . text } ) ,
6779 text ( { text : 'CLM-1042 / Northstar Fabrication' , font : '14px Inter, system-ui' , lineHeight : 20 , color : colors . muted } ) ,
6880 box ( {
81+ flexDirection : 'column' ,
6982 width : bounds ? Math . max ( 132 , Math . min ( 220 , bounds . width ) ) : 160 ,
7083 height : bounds ? Math . max ( 38 , Math . min ( 54 , bounds . height ) ) : 44 ,
7184 backgroundColor : status === 'completed' ? '#064e3b' : '#1e3a8a' ,
@@ -84,6 +97,7 @@ function framePreview(title: string, node: typeof beforeNode | typeof afterNode,
8497 font : '13px Inter, system-ui' ,
8598 lineHeight : 19 ,
8699 color : colors . muted ,
100+ whiteSpace : 'normal' ,
87101 } ) ,
88102 ] )
89103}
@@ -92,8 +106,12 @@ function timelineItem(index: number, title: string, detail: string) {
92106 const active = selectedStep . value === index
93107 return box ( {
94108 onClick : ( ) => {
95- selectedStep . value = index
109+ selectedStep . set ( index )
96110 } ,
111+ flexDirection : 'column' ,
112+ flexGrow : 1 ,
113+ flexShrink : 1 ,
114+ flexBasis : 0 ,
97115 backgroundColor : active ? '#0c4a6e' : colors . card ,
98116 borderColor : active ? colors . accent : colors . border ,
99117 borderWidth : 1 ,
@@ -103,7 +121,7 @@ function timelineItem(index: number, title: string, detail: string) {
103121 semantic : { id : `replay-step-${ index } ` , role : 'button' , ariaLabel : title } ,
104122 } , [
105123 text ( { text : title , font : '700 14px Inter, system-ui' , lineHeight : 19 , color : colors . text } ) ,
106- text ( { text : detail , font : '12px Inter, system-ui' , lineHeight : 17 , color : colors . muted } ) ,
124+ text ( { text : detail , font : '12px Inter, system-ui' , lineHeight : 17 , color : colors . muted , whiteSpace : 'normal' } ) ,
107125 ] )
108126}
109127
@@ -116,16 +134,17 @@ function root() {
116134 : 'Replay now contains frame-before, output, approval, and frame-after proof.'
117135
118136 return box ( {
119- width : '100%' ,
120- height : '100%' ,
137+ flexDirection : 'column' ,
138+ width : viewportWidth . value ,
139+ height : viewportHeight . value ,
121140 backgroundColor : colors . bg ,
122141 padding : 28 ,
123142 gap : 22 ,
124143 } , [
125144 box ( { flexDirection : 'row' , justifyContent : 'space-between' , alignItems : 'center' } , [
126- box ( { gap : 6 } , [
145+ box ( { flexDirection : 'column' , flexGrow : 1 , flexShrink : 1 , flexBasis : 0 , gap : 6 } , [
127146 text ( { text : 'Agent-Native Audit Replay Viewer' , font : '800 30px Inter, system-ui' , lineHeight : 38 , color : colors . text } ) ,
128- text ( { text : 'A visual packet of exactly what the agent saw, requested, approved, and completed.' , font : '15px Inter, system-ui' , lineHeight : 22 , color : colors . muted } ) ,
147+ text ( { text : 'A visual packet of exactly what the agent saw, requested, approved, and completed.' , font : '15px Inter, system-ui' , lineHeight : 22 , color : colors . muted , whiteSpace : 'normal' } ) ,
129148 ] ) ,
130149 card ( 'Replay artifact' , [
131150 `Session ${ replay . sessionId } ` ,
@@ -135,29 +154,39 @@ function root() {
135154 box ( { flexDirection : 'row' , gap : 16 } , [
136155 timelineItem ( 0 , '1. Inspect' , `${ before ?. geometry . nodes . length ?? 0 } geometry nodes` ) ,
137156 timelineItem ( 1 , '2. Approval' , `${ action ?. approval ?. actor ?? 'manager' } approved` ) ,
138- timelineItem ( 2 , '3. Completion' , `${ label ( action ?. output ) } ` ) ,
139- ] ) ,
140- card ( 'Selected replay step' , [ selected ] , selectedStep . value === 2 ? colors . success : colors . accent ) ,
141- box ( { flexDirection : 'row' , gap : 18 , flex : 1 } , [
142- framePreview ( 'Frame before' , beforeNode , 'pending' ) ,
143- framePreview ( 'Frame after' , afterNode , action ?. status ?? 'completed' ) ,
157+ timelineItem ( 2 , '3. Completion' , 'completed with audit proof' ) ,
144158 ] ) ,
145- box ( { flexDirection : 'row' , gap : 18 } , [
146- card ( 'Action contract' , [
147- `id: ${ action ?. actionId ?? 'unknown' } ` ,
148- `risk: ${ target ?. risk ?? 'unknown' } ` ,
149- `requires confirmation: ${ target ?. requiresConfirmation ? 'yes' : 'no' } ` ,
150- ] , colors . warning ) ,
151- card ( 'Audit result' , [
152- `status: ${ action ?. status ?? 'unknown' } ` ,
153- `approval: ${ action ?. approval ?. approved ? 'approved' : 'not approved' } ` ,
154- `output: ${ label ( action ?. output ) } ` ,
155- ] , colors . success ) ,
159+ box ( { flexDirection : 'row' , gap : 18 , flexGrow : 1 , flexShrink : 1 , flexBasis : 0 } , [
160+ box ( { flexDirection : 'column' , gap : 16 , flexGrow : 1 , flexShrink : 1 , flexBasis : 0 } , [
161+ card ( 'Selected replay step' , [ selected ] , selectedStep . value === 2 ? colors . success : colors . accent ) ,
162+ box ( { flexDirection : 'row' , gap : 18 , flexGrow : 1 , flexShrink : 1 , flexBasis : 0 } , [
163+ framePreview ( 'Frame before' , beforeNode , 'pending' ) ,
164+ framePreview ( 'Frame after' , afterNode , action ?. status ?? 'completed' ) ,
165+ ] ) ,
166+ ] ) ,
167+ box ( { flexDirection : 'column' , gap : 16 , width : 300 } , [
168+ card ( 'Action contract' , [
169+ `id: ${ action ?. actionId ?? 'unknown' } ` ,
170+ `risk: ${ target ?. risk ?? 'unknown' } ` ,
171+ `requires confirmation: ${ target ?. requiresConfirmation ? 'yes' : 'no' } ` ,
172+ ] , colors . warning ) ,
173+ card ( 'Audit result' , [
174+ `status: ${ action ?. status ?? 'unknown' } ` ,
175+ `approval: ${ action ?. approval ?. approved ? 'approved' : 'not approved' } ` ,
176+ `claim: ${ outputField ( 'claimId' ) } ` ,
177+ `audit: ${ outputField ( 'auditId' ) } ` ,
178+ ] , colors . success ) ,
179+ ] ) ,
156180 ] ) ,
157181 ] )
158182}
159183
160184const canvas = document . getElementById ( 'app' ) as HTMLCanvasElement
161185const renderer = new CanvasRenderer ( { canvas, background : colors . bg } )
162- const app = await createApp ( root , renderer , { width : window . innerWidth , height : window . innerHeight , waitForFonts : true } )
186+ const app = await createApp ( root , renderer , { width : viewportWidth . value , height : viewportHeight . value , waitForFonts : true } )
163187app . update ( )
188+
189+ window . addEventListener ( 'resize' , ( ) => {
190+ viewportWidth . set ( window . innerWidth )
191+ viewportHeight . set ( window . innerHeight )
192+ } )
0 commit comments