@@ -4,6 +4,7 @@ import { Link } from "@tanstack/react-router";
44import { AGENT_ONBOARDING_WITH_INSTALL } from "@ax/onboarding-prompt" ;
55import { HeroLogoField , PROVIDERS } from "./supports-strip" ;
66import { RetroTerminal } from "./retro-terminal" ;
7+ import { CardViz , type VizSpec } from "@ax/recap-deck" ;
78
89// ============================================================================
910// Mission Control preview - a static recreation of the studio home (instrument
@@ -148,268 +149,6 @@ function McClock() {
148149}
149150
150151
151- // ---- Wrapped recap charts (static recreations of the studio card-viz registry:
152- // apps/studio/src/instrument/card-viz.tsx). Each keys --card-accent. ----
153- function McBars ( { data } : { data : number [ ] } ) {
154- const max = Math . max ( ...data , 1 ) ;
155- const last = data . length - 1 ;
156- return (
157- < div className = "mc-bars" aria-hidden = "true" >
158- { data . map ( ( b , i ) => (
159- < i
160- key = { i }
161- className = { i === last ? "is-now" : undefined }
162- style = { { height : `${ Math . max ( 6 , ( b / max ) * 100 ) } %` } }
163- />
164- ) ) }
165- </ div >
166- ) ;
167- }
168-
169- function McLine ( { data } : { data : number [ ] } ) {
170- const W = 100 ;
171- const H = 46 ;
172- const padX = 2 ;
173- const padT = 5 ;
174- const padB = 5 ;
175- const uh = H - padT - padB ;
176- const n = data . length ;
177- const max = Math . max ( ...data , 1 ) ;
178- const xy = data . map ( ( v , i ) => [ padX + ( i / ( n - 1 ) ) * ( W - 2 * padX ) , padT + ( 1 - v / max ) * uh ] as const ) ;
179- const stroke = `M ${ xy . map ( ( p ) => `${ p [ 0 ] . toFixed ( 1 ) } ${ p [ 1 ] . toFixed ( 1 ) } ` ) . join ( " L " ) } ` ;
180- const area = `${ stroke } L ${ xy [ n - 1 ] [ 0 ] . toFixed ( 1 ) } ${ H - padB } L ${ xy [ 0 ] [ 0 ] . toFixed ( 1 ) } ${ H - padB } Z` ;
181- return (
182- < svg className = "mc-line" viewBox = { `0 0 ${ W } ${ H } ` } preserveAspectRatio = "none" aria-hidden = "true" >
183- < defs >
184- < linearGradient id = "mcLineFill" x1 = "0" y1 = "0" x2 = "0" y2 = "1" >
185- < stop offset = "0%" stopColor = "var(--card-accent)" stopOpacity = "0.32" />
186- < stop offset = "100%" stopColor = "var(--card-accent)" stopOpacity = "0" />
187- </ linearGradient >
188- </ defs >
189- < path d = { area } fill = "url(#mcLineFill)" />
190- < path d = { stroke } className = "mc-line-stroke" fill = "none" />
191- </ svg >
192- ) ;
193- }
194-
195- function McRadar ( { data } : { data : number [ ] } ) {
196- const axes = data . length ;
197- const cx = 23 ;
198- const cy = 23 ;
199- const R = 21 ;
200- const max = Math . max ( ...data , 1 ) ;
201- const ang = ( i : number ) => - Math . PI / 2 + ( i / axes ) * Math . PI * 2 ;
202- const at = ( i : number , r : number ) => [ cx + Math . cos ( ang ( i ) ) * r , cy + Math . sin ( ang ( i ) ) * r ] as const ;
203- const rad = ( v : number ) => ( ( v / max ) * 0.84 + 0.08 ) * R ;
204- const fmt = ( p : readonly [ number , number ] ) => `${ p [ 0 ] . toFixed ( 1 ) } ,${ p [ 1 ] . toFixed ( 1 ) } ` ;
205- const poly = data . map ( ( v , i ) => fmt ( at ( i , rad ( v ) ) ) ) . join ( " " ) ;
206- return (
207- < svg className = "mc-radar" viewBox = "0 0 46 46" aria-hidden = "true" >
208- { [ 0.4 , 0.7 , 1 ] . map ( ( rr , k ) => (
209- < polygon key = { k } className = "web" points = { Array . from ( { length : axes } , ( _ , i ) => fmt ( at ( i , rr * R ) ) ) . join ( " " ) } />
210- ) ) }
211- < polygon className = "mc-radar-fill" points = { poly } />
212- { data . map ( ( v , i ) => {
213- const [ x , y ] = at ( i , rad ( v ) ) ;
214- return < circle key = { i } className = "mc-radar-node" cx = { x . toFixed ( 1 ) } cy = { y . toFixed ( 1 ) } r = { 1.5 } /> ;
215- } ) }
216- </ svg >
217- ) ;
218- }
219-
220- function McRing ( { pct } : { pct : number } ) {
221- const R = 15.5 ;
222- const C = 2 * Math . PI * R ;
223- const dash = C * 0.75 ;
224- const frac = Math . max ( 0.04 , pct / 100 ) ;
225- return (
226- < div className = "mc-ring" aria-hidden = "true" >
227- < svg viewBox = "0 0 40 40" >
228- < circle cx = "20" cy = "20" r = { R } className = "bg" strokeDasharray = { `${ dash } ${ C } ` } />
229- < circle cx = "20" cy = "20" r = { R } className = "fg" strokeDasharray = { `${ dash } ${ C } ` } strokeDashoffset = { dash * ( 1 - frac ) } />
230- </ svg >
231- < span className = "mc-ring-val" > { pct } </ span >
232- </ div >
233- ) ;
234- }
235-
236- function McWaffle ( { pct } : { pct : number } ) {
237- const ROWS = 5 ;
238- const COLS = 14 ;
239- const total = ROWS * COLS ;
240- const on = Math . round ( ( pct / 100 ) * total ) ;
241- return (
242- < div className = "mc-waffle" aria-hidden = "true" >
243- { Array . from ( { length : total } , ( _ , i ) => {
244- const col = i % COLS ;
245- const row = Math . floor ( i / COLS ) ;
246- const order = col * ROWS + ( ROWS - 1 - row ) ;
247- return < span key = { i } className = { `mc-waffle-cell${ order < on ? " on" : "" } ` } /> ;
248- } ) }
249- </ div >
250- ) ;
251- }
252-
253- function McCandles ( { data } : { data : number [ ] } ) {
254- const max = Math . max ( ...data , 1 ) ;
255- const min = Math . min ( ...data , 0 ) ;
256- const span = Math . max ( 1 , max - min ) ;
257- const Y = ( v : number ) => 4 + ( 1 - ( v - min ) / span ) * 36 ;
258- return (
259- < div className = "mc-candles" aria-hidden = "true" >
260- { data . map ( ( v , i ) => {
261- const prev = i === 0 ? data [ 0 ] : data [ i - 1 ] ;
262- const up = v >= prev ;
263- const oY = Y ( prev ) ;
264- const cY = Y ( v ) ;
265- const top = Math . min ( oY , cY ) ;
266- const bodyH = Math . max ( 2 , Math . abs ( cY - oY ) ) ;
267- const wickTop = Math . min ( top , Y ( Math . max ( v , prev ) ) - 3 ) ;
268- const wickBot = Math . max ( top + bodyH , Y ( Math . min ( v , prev ) ) + 3 ) ;
269- return (
270- < span key = { i } className = { `mc-candle ${ up ? "up" : "dn" } ` } >
271- < span className = "mc-candle-wick" style = { { top : `${ wickTop } px` , height : `${ Math . max ( 3 , wickBot - wickTop ) } px` } } />
272- < span className = "mc-candle-body" style = { { top : `${ top } px` , height : `${ bodyH } px` } } />
273- </ span >
274- ) ;
275- } ) }
276- </ div >
277- ) ;
278- }
279-
280- function McComet ( { pct } : { pct : number } ) {
281- const cx = 23 ;
282- const cy = 23 ;
283- const rx = 19 ;
284- const ry = 15 ;
285- const frac = Math . max ( 0.02 , pct / 100 ) ;
286- const C = Math . PI * ( 3 * ( rx + ry ) - Math . sqrt ( ( 3 * rx + ry ) * ( rx + 3 * ry ) ) ) ;
287- const dash = C * frac ;
288- const ang = - Math . PI / 2 + frac * Math . PI * 2 ;
289- const hx = cx + Math . cos ( ang ) * rx ;
290- const hy = cy + Math . sin ( ang ) * ry ;
291- return (
292- < div className = "mc-comet" aria-hidden = "true" >
293- < svg viewBox = "0 0 46 46" >
294- < ellipse className = "mc-comet-orbit" cx = { cx } cy = { cy } rx = { rx } ry = { ry } />
295- < ellipse
296- className = "mc-comet-trail"
297- cx = { cx }
298- cy = { cy }
299- rx = { rx }
300- ry = { ry }
301- strokeDasharray = { `${ dash . toFixed ( 1 ) } ${ C . toFixed ( 1 ) } ` }
302- transform = { `rotate(-90 ${ cx } ${ cy } )` }
303- />
304- < circle className = "mc-comet-head" cx = { hx . toFixed ( 1 ) } cy = { hy . toFixed ( 1 ) } r = { 2.6 } />
305- </ svg >
306- </ div >
307- ) ;
308- }
309-
310- function McWave ( { data } : { data : number [ ] } ) {
311- const W = 100 ;
312- const H = 46 ;
313- const mid = H / 2 ;
314- const n = data . length ;
315- const max = Math . max ( ...data , 1 ) ;
316- const min = Math . min ( ...data , 0 ) ;
317- const span = Math . max ( 1 , max - min ) ;
318- const d = data
319- . map ( ( v , i ) => {
320- const x = ( i / ( n - 1 ) ) * ( W - 2 ) + 1 ;
321- const y = mid + ( 0.5 - ( v - min ) / span ) * ( H - 10 ) ;
322- return `${ i === 0 ? "M" : "L" } ${ x . toFixed ( 1 ) } ${ y . toFixed ( 1 ) } ` ;
323- } )
324- . join ( " " ) ;
325- return (
326- < svg className = "mc-wave" viewBox = { `0 0 ${ W } ${ H } ` } preserveAspectRatio = "none" aria-hidden = "true" >
327- < line className = "mc-wave-grat" x1 = "0" y1 = { mid } x2 = { W } y2 = { mid } />
328- < line className = "mc-wave-grat dash" x1 = { W * 0.5 } y1 = "2" x2 = { W * 0.5 } y2 = { H - 2 } />
329- < path className = "mc-wave-trace" d = { d } fill = "none" vectorEffect = "non-scaling-stroke" />
330- </ svg >
331- ) ;
332- }
333-
334- function McBullet ( { actual, target } : { actual : number ; target : number } ) {
335- const onTrack = actual >= target ;
336- return (
337- < div className = "mc-bullet" aria-hidden = "true" >
338- < div className = "mc-bullet-track" >
339- < span className = "mc-bullet-fill" style = { { width : `${ actual } %` } } />
340- < span className = { `mc-bullet-tick${ onTrack ? " ok" : "" } ` } style = { { left : `${ target } %` } } />
341- </ div >
342- < div className = "mc-bullet-foot" >
343- < span className = "mc-bullet-val" > { actual } </ span >
344- < span className = "mc-bullet-goal" > / { target } goal</ span >
345- </ div >
346- </ div >
347- ) ;
348- }
349-
350- function McSignal ( { on, bars = 6 } : { on : number ; bars ?: number } ) {
351- return (
352- < div className = "mc-signal" aria-hidden = "true" >
353- { Array . from ( { length : bars } , ( _ , i ) => (
354- < span
355- key = { i }
356- className = { `mc-signal-bar${ i < on ? " on" : "" } ` }
357- style = { { height : `${ 28 + ( i / ( bars - 1 ) ) * 70 } %` } }
358- />
359- ) ) }
360- </ div >
361- ) ;
362- }
363-
364- function McScatter ( { data } : { data : number [ ] } ) {
365- const n = data . length ;
366- const max = Math . max ( ...data , 1 ) ;
367- const min = Math . min ( ...data , 0 ) ;
368- const span = Math . max ( 1 , max - min ) ;
369- const mean = data . reduce ( ( a , b ) => a + b , 0 ) / n ;
370- const meanY = 4 + ( 1 - ( mean - min ) / span ) * 38 ;
371- return (
372- < div className = "mc-scatter" aria-hidden = "true" >
373- < svg className = "mc-scatter-svg" viewBox = "0 0 100 46" preserveAspectRatio = "none" >
374- < line className = "mc-scatter-mean" x1 = "0" y1 = { meanY . toFixed ( 1 ) } x2 = "100" y2 = { meanY . toFixed ( 1 ) } vectorEffect = "non-scaling-stroke" />
375- </ svg >
376- < div className = "mc-scatter-dots" >
377- { data . map ( ( v , i ) => {
378- const x = ( i / ( n - 1 ) ) * 100 ;
379- const y = 4 + ( 1 - ( v - min ) / span ) * 38 ;
380- return < span key = { i } className = { `mc-scatter-dot${ v >= mean ? " hot" : "" } ` } style = { { left : `${ x } %` , top : `${ y } px` } } /> ;
381- } ) }
382- </ div >
383- </ div >
384- ) ;
385- }
386-
387- function McDelta ( { data } : { data : number [ ] } ) {
388- const last = data [ data . length - 1 ] ;
389- const first = data [ 0 ] ;
390- const diff = last - first ;
391- const up = diff >= 0 ;
392- const n = data . length ;
393- const max = Math . max ( ...data , 1 ) ;
394- const min = Math . min ( ...data , 0 ) ;
395- const span = Math . max ( 1 , max - min ) ;
396- const spark = data . map ( ( v , i ) => `${ ( i / ( n - 1 ) ) * 100 } ,${ ( 14 - ( ( v - min ) / span ) * 12 ) . toFixed ( 1 ) } ` ) . join ( " " ) ;
397- return (
398- < div className = { `mc-delta ${ up ? "up" : "dn" } ` } aria-hidden = "true" >
399- < div className = "mc-delta-num" >
400- < span className = "mc-delta-val" > { Math . round ( last ) } </ span >
401- < span className = "mc-delta-trend" >
402- < i > { up ? "▲" : "▼" } </ i >
403- { Math . abs ( Math . round ( diff ) ) }
404- </ span >
405- </ div >
406- < svg className = "mc-delta-spark" viewBox = "0 0 100 14" preserveAspectRatio = "none" >
407- < polyline points = { spark } fill = "none" stroke = "var(--card-accent)" strokeWidth = "1.25" vectorEffect = "non-scaling-stroke" />
408- </ svg >
409- </ div >
410- ) ;
411- }
412-
413152// The 12 wrapped readouts the floating popups cycle through - each keys a channel
414153// accent and carries one chart + a one-line caption. (Recreated from the studio
415154// card-viz registry: apps/studio/src/instrument/card-viz.tsx.)
@@ -418,20 +157,22 @@ const POP_CHARTS: ReadonlyArray<{
418157 accent : string ;
419158 q : string ;
420159 h : string ;
421- viz : React . ReactNode ;
160+ viz : VizSpec ;
422161} > = [
423- { key : "bars" , accent : "acc-green" , q : "when you ship" , h : "Tuesday nights" , viz : < McBars data = { [ 22 , 31 , 28 , 44 , 39 , 58 , 47 , 63 , 71 , 55 , 82 , 90 , 74 , 61 , 88 , 96 ] } /> } ,
424- { key : "ring" , accent : "acc-gold" , q : "fixes stick" , h : "82% hold" , viz : < McRing pct = { 82 } /> } ,
425- { key : "line" , accent : "acc-blue" , q : "tokens / year" , h : "1.4B" , viz : < McLine data = { [ 18 , 24 , 21 , 33 , 40 , 38 , 52 , 60 , 57 , 71 , 80 , 78 , 92 , 100 ] } /> } ,
426- { key : "radar" , accent : "acc-violet" , q : "skill profile" , h : "systems mind" , viz : < McRadar data = { [ 82 , 64 , 91 , 48 , 73 ] } /> } ,
427- { key : "waffle" , accent : "acc-green" , q : "paths covered" , h : "73%" , viz : < McWaffle pct = { 73 } /> } ,
428- { key : "candles" , accent : "acc-rose" , q : "session swings" , h : "big nights" , viz : < McCandles data = { [ 40 , 60 , 55 , 75 , 70 , 50 , 62 , 80 , 72 , 88 ] } /> } ,
429- { key : "comet" , accent : "acc-blue" , q : "quota burn" , h : "61%" , viz : < McComet pct = { 61 } /> } ,
430- { key : "wave" , accent : "acc-violet" , q : "latency" , h : "steady" , viz : < McWave data = { [ 50 , 80 , 20 , 60 , 35 , 70 , 45 , 66 , 30 , 72 ] } /> } ,
431- { key : "scatter" , accent : "acc-rose" , q : "cost vs reward" , h : "lands cheap" , viz : < McScatter data = { [ 30 , 50 , 45 , 70 , 60 , 85 , 55 , 40 , 66 , 52 ] } /> } ,
432- { key : "bullet" , accent : "acc-gold" , q : "ship goal" , h : "82 / 70" , viz : < McBullet actual = { 82 } target = { 70 } /> } ,
433- { key : "signal" , accent : "acc-green" , q : "harness health" , h : "5 / 6" , viz : < McSignal on = { 5 } /> } ,
434- { key : "delta" , accent : "acc-blue" , q : "weekly tokens" , h : "+18" , viz : < McDelta data = { [ 40 , 55 , 48 , 62 , 70 , 66 , 78 , 84 ] } /> } ,
162+ { key : "bars" , accent : "acc-green" , q : "when you ship" , h : "Tuesday nights" , viz : { kind : "bars" , data : [ 22 , 31 , 28 , 44 , 39 , 58 , 47 , 63 , 71 , 55 , 82 , 90 , 74 , 61 , 88 , 96 ] } } ,
163+ { key : "ring" , accent : "acc-gold" , q : "fixes stick" , h : "82% hold" , viz : { kind : "ring" , data : [ 82 ] } } ,
164+ { key : "line" , accent : "acc-blue" , q : "tokens / year" , h : "1.4B" , viz : { kind : "line" , data : [ 18 , 24 , 21 , 33 , 40 , 38 , 52 , 60 , 57 , 71 , 80 , 78 , 92 , 100 ] } } ,
165+ { key : "radar" , accent : "acc-violet" , q : "skill profile" , h : "systems mind" , viz : { kind : "radar" , data : [ 82 , 64 , 91 , 48 , 73 ] } } ,
166+ { key : "waffle" , accent : "acc-green" , q : "paths covered" , h : "73%" , viz : { kind : "waffle" , data : [ 73 ] } } ,
167+ { key : "candles" , accent : "acc-rose" , q : "session swings" , h : "big nights" , viz : { kind : "candles" , data : [ 40 , 60 , 55 , 75 , 70 , 50 , 62 , 80 , 72 , 88 ] } } ,
168+ { key : "comet" , accent : "acc-blue" , q : "quota burn" , h : "61%" , viz : { kind : "comet" , data : [ 61 ] } } ,
169+ { key : "wave" , accent : "acc-violet" , q : "latency" , h : "steady" , viz : { kind : "wave" , data : [ 50 , 80 , 20 , 60 , 35 , 70 , 45 , 66 , 30 , 72 ] } } ,
170+ { key : "scatter" , accent : "acc-rose" , q : "cost vs reward" , h : "lands cheap" , viz : { kind : "scatter" , data : [ 30 , 50 , 45 , 70 , 60 , 85 , 55 , 40 , 66 , 52 ] } } ,
171+ // package bullet reads [target, actual]: target=first, actual=last -> 70 goal, 82 actual
172+ { key : "bullet" , accent : "acc-gold" , q : "ship goal" , h : "82 / 70" , viz : { kind : "bullet" , data : [ 70 , 82 ] } } ,
173+ // package signal reads avg(data) as a 0..100 pct -> on = round(83/100*6) = 5 of 6
174+ { key : "signal" , accent : "acc-green" , q : "harness health" , h : "5 / 6" , viz : { kind : "signal" , data : [ 83 ] } } ,
175+ { key : "delta" , accent : "acc-blue" , q : "weekly tokens" , h : "+18" , viz : { kind : "delta" , data : [ 40 , 55 , 48 , 62 , 70 , 66 , 78 , 84 ] } } ,
435176] ;
436177
437178// chart kinds that render small/fixed-size (center them in the popup viz row);
@@ -720,13 +461,17 @@ export function DashboardPreview() {
720461 { /* floating wrapped highlights - cycle through all 12 charts */ }
721462 < div className = "mc-pop-strip" >
722463 { POP_SLOTS . map ( ( { off, place } ) => {
723- const c = POP_CHARTS [ ( popTick + off ) % POP_CHARTS . length ] ;
464+ const c = POP_CHARTS [ ( popTick + off ) % POP_CHARTS . length ] ! ;
724465 return (
725- < article key = { place } className = { `mc-pop ${ place } ${ c . accent } browser--instrument` } >
466+ < article
467+ key = { place }
468+ className = { `mc-pop ${ place } ${ c . accent } browser--instrument rdx` }
469+ data-theme = "dark"
470+ >
726471 < span className = "mc-pop-badge" > wrapped</ span >
727472 < div key = { c . key } className = "mc-pop-swap" >
728473 < div className = { `mc-pop-viz${ POP_CENTERED . has ( c . key ) ? " mc-pop-viz--center" : "" } ` } >
729- { c . viz }
474+ < CardViz spec = { c . viz } />
730475 </ div >
731476 < span className = "mc-pop-q" > $ { c . q } </ span >
732477 < h4 className = "mc-pop-h" > { c . h } </ h4 >
0 commit comments