@datarecce/ui 1.52.0 → 1.53.0-nightly.20260611

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/advanced.d.ts +3 -3
  2. package/dist/advanced.js +1 -1
  3. package/dist/advanced.js.map +1 -1
  4. package/dist/api-DTKI1Y_n.js +3 -0
  5. package/dist/{api-S5ho3Qs3.js.map → api-DTKI1Y_n.js.map} +1 -1
  6. package/dist/api.js +1 -1
  7. package/dist/components-C1oqsBU3.js +3 -0
  8. package/dist/components-C1oqsBU3.js.map +1 -0
  9. package/dist/components-run.js +1 -1
  10. package/dist/components.d.ts +3 -3
  11. package/dist/components.js +1 -1
  12. package/dist/contexts.js +1 -1
  13. package/dist/{fetchClient-D0p358nB.js → fetchClient-Bf9Q3QKq.js} +2 -2
  14. package/dist/{fetchClient-D0p358nB.js.map → fetchClient-Bf9Q3QKq.js.map} +1 -1
  15. package/dist/hooks.js +1 -1
  16. package/dist/{index-DjuwVxKT.d.ts → index-Cl16UDPD.d.ts} +2 -2
  17. package/dist/{index-DjuwVxKT.d.ts.map → index-Cl16UDPD.d.ts.map} +1 -1
  18. package/dist/index.d.ts +3 -3
  19. package/dist/index.js +1 -1
  20. package/dist/{keepAlive-bjIHulj-.js → keepAlive-Bowms1oa.js} +2 -2
  21. package/dist/{keepAlive-bjIHulj-.js.map → keepAlive-Bowms1oa.js.map} +1 -1
  22. package/dist/lib/api/user.js +1 -1
  23. package/dist/lineage-C6YQCnhM.js +7 -0
  24. package/dist/lineage-C6YQCnhM.js.map +1 -0
  25. package/dist/{lineage-2nc0YlpN.d.ts → lineage-DZl7z5RZ.d.ts} +4 -4
  26. package/dist/{lineage-2nc0YlpN.d.ts.map → lineage-DZl7z5RZ.d.ts.map} +1 -1
  27. package/dist/{primitives-Dvljxc6u.d.ts → primitives-DEcmeDKq.d.ts} +3 -3
  28. package/dist/{primitives-Dvljxc6u.d.ts.map → primitives-DEcmeDKq.d.ts.map} +1 -1
  29. package/dist/primitives.d.ts +1 -1
  30. package/dist/primitives.js +1 -1
  31. package/dist/result.js +1 -1
  32. package/dist/src-D9arJm8F.js +3 -0
  33. package/dist/src-D9arJm8F.js.map +1 -0
  34. package/dist/style.css +23 -23
  35. package/dist/theme.js +1 -1
  36. package/dist/types.d.ts +2 -2
  37. package/dist/utils-BzZEjJAS.js +3 -0
  38. package/dist/{utils-FOrjYCpW.js.map → utils-BzZEjJAS.js.map} +1 -1
  39. package/dist/utils-D1bvitEj.js +6 -0
  40. package/dist/utils-D1bvitEj.js.map +1 -0
  41. package/dist/utils.js +1 -1
  42. package/package.json +1 -1
  43. package/dist/api-S5ho3Qs3.js +0 -3
  44. package/dist/hooks-CrJq15DU.js +0 -7
  45. package/dist/hooks-CrJq15DU.js.map +0 -1
  46. package/dist/src-DI-sLY25.js +0 -12
  47. package/dist/src-DI-sLY25.js.map +0 -1
  48. package/dist/utils-Dl2dMzK8.js +0 -6
  49. package/dist/utils-Dl2dMzK8.js.map +0 -1
  50. package/dist/utils-FOrjYCpW.js +0 -3
@@ -2,9 +2,9 @@
2
2
  import { rn as ColumnLineageData } from "./instanceInfo-CMOHULsR.js";
3
3
  import { a as LineageGraphColumnNode, c as LineageGraphNode, i as LineageGraph, l as LineageGraphNodes, m as isLineageGraphNode, p as isLineageGraphColumnNode, r as EnvInfo, s as LineageGraphEdge } from "./types-9MeiHUfo.js";
4
4
  import { _ as toReactFlowBasic, c as useRunsAggregated, d as buildLineageGraph, f as getNeighborSet, g as selectUpstream, h as selectDownstream, l as COLUMN_HEIGHT, m as layoutWithDagre, p as intersect, s as useLineageGraphContext, u as NodeColumnSetMap, v as union } from "./index-BOFGzyVm.js";
5
- import { Ct as RunTypeIconMap, Dt as RowCountTag, Et as RowCountSummary, Gt as LineageTabContentProps, Jt as FIT_VIEW_PADDING, Kt as DIM_FILTER, St as NodeViewProps, Tt as RowCountDiffTag, Wt as LineageTabContent, Xt as ColumnAnnotation, Yt as LEGIBLE_MIN_ZOOM, bt as NodeViewActionCallbacks, n as toReactFlow, qt as EXPLORE_MIN_ZOOM, t as layout, wt as SchemaViewProps, xt as NodeViewNodeData, yt as NodeView } from "./lineage-2nc0YlpN.js";
6
- import { Dn as NodeChangeStatus } from "./primitives-Dvljxc6u.js";
7
- import { b as LineageCanvasProps, y as LineageCanvas } from "./index-DjuwVxKT.js";
5
+ import { Ct as RunTypeIconMap, Dt as RowCountTag, Et as RowCountSummary, Gt as LineageTabContentProps, Jt as FIT_VIEW_PADDING, Kt as DIM_FILTER, St as NodeViewProps, Tt as RowCountDiffTag, Wt as LineageTabContent, Xt as ColumnAnnotation, Yt as LEGIBLE_MIN_ZOOM, bt as NodeViewActionCallbacks, n as toReactFlow, qt as EXPLORE_MIN_ZOOM, t as layout, wt as SchemaViewProps, xt as NodeViewNodeData, yt as NodeView } from "./lineage-DZl7z5RZ.js";
6
+ import { Dn as NodeChangeStatus } from "./primitives-DEcmeDKq.js";
7
+ import { b as LineageCanvasProps, y as LineageCanvas } from "./index-Cl16UDPD.js";
8
8
  import { c as useRecceActionContext, g as InstanceInfoType, h as useRecceInstanceContext, i as IdleTimeoutContextType, p as useRecceInstanceInfo, r as useIdleTimeout, u as RecceActionContextType } from "./index-CL1zCliW.js";
9
9
  import { n as useThemeColors } from "./useThemeColors-CUdNH8LP.js";
10
10
  import { a as SemanticColorVariant, i as ColorShade, s as colors } from "./index-L2LAN6Z9.js";
package/dist/advanced.js CHANGED
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- "use client";import{D as e,E as t,S as n,a as r,b as i,c as a,i as o,j as s,l as c,m as l,n as u,o as d,p as f,r as p,s as m,t as h,y as g}from"./utils-FOrjYCpW.js";import{n as _}from"./colors-4aGxuAVw.js";import{Mn as v,Nn as y,Pn as b,jn as x,t as S}from"./hooks-CrJq15DU.js";import{A as C,C as w,E as T,F as E,M as D,N as O,S as k,T as A,j,w as M}from"./src-DI-sLY25.js";const N=`0.2.0`;export{N as ADVANCED_API_VERSION,h as COLUMN_HEIGHT,x as DIM_FILTER,v as EXPLORE_MIN_ZOOM,y as FIT_VIEW_PADDING,b as LEGIBLE_MIN_ZOOM,E as LineageCanvas,T as LineageTabContent,k as NodeView,w as RowCountDiffTag,M as RowCountSummary,A as RowCountTag,u as buildLineageGraph,_ as colors,O as computeImpactedColumns,D as computeIsImpacted,p as getNeighborSet,o as intersect,f as isLineageGraphColumnNode,l as isLineageGraphNode,C as layout,r as layoutWithDagre,d as selectDownstream,m as selectUpstream,j as toReactFlow,a as toReactFlowBasic,c as union,n as useIdleTimeout,g as useLineageGraphContext,s as useRecceActionContext,t as useRecceInstanceContext,e as useRecceInstanceInfo,i as useRunsAggregated,S as useThemeColors};
2
+ "use client";import{Mt as e,Nt as t,a as n,an as r,c as i,cn as a,dn as o,fn as s,i as c,jt as l,ln as u,m as d,o as f,on as p,pn as m,sn as h,un as g}from"./lineage-C6YQCnhM.js";import{n as _}from"./colors-4aGxuAVw.js";import{A as v,E as y,T as b,a as x,c as S,f as C,i as w,l as T,n as E,o as D,p as O,r as k,s as A,t as j,u as M,v as N,x as ee,y as te}from"./utils-BzZEjJAS.js";import{t as ne}from"./constants-Bgp90yan.js";import{useEffect as P,useMemo as F,useState as I}from"react";import{Fragment as L,jsx as R,jsxs as z}from"react/jsx-runtime";import B from"@mui/material/Box";import{Position as V}from"@xyflow/react";import H from"@mui/material/Tooltip";import U from"@mui/material/Button";import W from"@mui/material/IconButton";import G from"@mui/material/Typography";import{IoClose as re}from"react-icons/io5";import K from"@mui/material/Stack";import{MdArrowBack as ie,MdArrowDownward as q,MdArrowUpward as ae,MdGpsFixed as oe,MdSearch as se}from"react-icons/md";import J from"@mui/material/Tab";import ce from"@mui/material/Tabs";import le from"@mui/material/InputBase";import ue from"@dagrejs/dagre";function de(e){let{columns:t,parent_map:n}=e.current,r=new Map;function i(e){let a=r.get(e);if(a===`pending`)return!1;if(a!==void 0)return a;if(r.set(e,`pending`),t[e]?.change_status)return r.set(e,!0),!0;let o=n[e]??[];for(let t of o)if(i(t))return r.set(e,!0),!0;return r.set(e,!1),!1}let a=new Set;for(let e of Object.keys(t))i(e)&&a.add(e);return a}function fe(e,t,n,r){if(n)return!0;if(!t)return!1;if(t.current.nodes[e]?.impacted)return!0;let i=r,a=`${e}_`;for(let e of i)if(e.startsWith(a))return!0;return!1}const pe={light:`rgb(252 211 77 / 0.18)`,dark:`rgb(252 211 77 / 0.10)`};function Y(e){return e?u.dark:u.light}function me(e){return e?.data.changeStatus??`unchanged`}function he(e,t){return e===`unchanged`&&t?`impacted`:e}function ge(e){return e===`impacted`?p.impacted:r[e]}function X(e,t){return t?.[e]?.data.name??e}function _e(e,t,n){let r=t.trim().toLowerCase();return r?e.filter(e=>X(e,n).toLowerCase().includes(r)):e}function ve(e,t){return e?`Show all models`:t?`Show only impacting models`:`Show only impacted models`}function ye({status:e,cllImpacted:t}){let n=he(e,t);return R(B,{component:`span`,"data-testid":`lineage-status-dot`,"data-status":n,sx:{width:`8px`,height:`8px`,borderRadius:`2px`,backgroundColor:ge(n),flex:`0 0 auto`}})}function be({name:e,status:t,direction:n,decorateAsImpacted:r,dotImpacted:i,onClick:a}){let{isDark:o}=d(),s=n===`up`?`Impacts this model`:`Impacted by this model`;return z(B,{onClick:a,sx:{position:`relative`,display:`flex`,alignItems:`center`,gap:.75,pl:1.5,pr:1.25,py:.5,cursor:a?`pointer`:`default`,fontSize:`12px`,fontFamily:`ui-monospace, 'IBM Plex Mono', monospace`,lineHeight:1.3,color:`text.primary`,minWidth:0,backgroundColor:r?o?pe.dark:pe.light:void 0,"&:hover":a?{backgroundColor:`action.hover`}:void 0},children:[r&&R(B,{"aria-hidden":`true`,sx:{position:`absolute`,left:0,top:0,bottom:0,width:`3px`,backgroundColor:p.impacted}}),R(ye,{status:t,cllImpacted:i}),R(B,{component:`span`,sx:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`,minWidth:0,flex:1},title:e,children:e}),r&&R(H,{title:s,placement:`top`,arrow:!0,children:R(B,{component:`span`,"aria-label":s,"data-testid":`lineage-impact-mark`,sx:{display:`inline-flex`,alignItems:`center`,justifyContent:`center`,width:`16px`,height:`16px`,color:Y(o),flex:`0 0 auto`},children:R(q,{size:12})})})]})}function xe({isDark:e,active:t}){return t?h.dark:e?a.dark:a.light}function Se({isDark:e,active:t}){return t?`#fff`:e?u.dark:u.light}const Ce={display:`inline-flex`,alignItems:`center`,gap:`4px`,ml:.75,px:`7px`,py:`1px`,borderRadius:`999px`,fontSize:`10px`,fontWeight:600,letterSpacing:`0.02em`};function we({color:e}){return R(B,{component:`span`,"aria-hidden":`true`,sx:{width:`5px`,height:`5px`,borderRadius:`50%`,backgroundColor:e}})}function Te({label:e,title:t,active:n,isDark:r,onClick:i}){let a={isDark:r,active:n};return R(H,{title:t,placement:`top`,arrow:!0,children:z(B,{component:`button`,type:`button`,"data-testid":`lineage-impact-chip`,"aria-pressed":n,"aria-label":t,onClick:i,sx:{...Ce,backgroundColor:xe(a),color:Se(a),border:`none`,font:`inherit`,cursor:`pointer`,"&:hover":{filter:n?`brightness(0.95)`:`brightness(0.97)`}},children:[R(we,{color:Se(a)}),e]})})}function Ee({direction:e,directCount:t,impactCount:n,onlyImpact:r,onToggleOnlyImpact:i}){let a=e===`up`,o=a?ae:q,{isDark:s,background:c}=d(),l=n??0,u=l>0,f=a?`${l} impacting`:`${l} impacted`;return z(K,{direction:`row`,spacing:.75,sx:{alignItems:`center`,px:1.5,py:.875,backgroundColor:c.subtle,borderTop:`1px solid`,borderBottom:`1px solid`,borderColor:`divider`,fontSize:`11px`,color:`text.secondary`,fontWeight:600},children:[R(o,{size:11}),R(B,{component:`span`,sx:{textTransform:`uppercase`,letterSpacing:`0.06em`},children:a?`Upstream`:`Downstream`}),z(B,{component:`span`,sx:{color:`text.disabled`,fontWeight:500},children:[`· `,t,` direct`]}),u&&R(Te,{label:f,title:ve(r,a),active:r,isDark:s,onClick:i})]})}function De({label:e}){return R(B,{sx:{px:1.5,py:1,fontSize:`11px`,color:`text.disabled`,fontStyle:`italic`},children:e})}function Oe({direction:e,query:t,onChange:n}){return R(B,{sx:{px:1.25,py:.75,borderBottom:`1px solid`,borderColor:`divider`,backgroundColor:`background.paper`},children:z(K,{direction:`row`,spacing:.5,sx:{alignItems:`center`,border:`1px solid`,borderColor:`divider`,borderRadius:`4px`,px:.875,py:.25},children:[R(se,{size:12,color:`currentColor`,opacity:.55}),R(le,{value:t,onChange:e=>{n(e.target.value)},placeholder:`filter…`,inputProps:{"aria-label":`Filter ${e===`up`?`Upstream`:`Downstream`}`,style:{padding:0,fontSize:`11px`,fontFamily:`ui-monospace, 'IBM Plex Mono', monospace`}},sx:{flex:1,fontSize:`11px`}})]})})}function ke({hidden:e,onClick:t}){return z(B,{onClick:t,role:`button`,tabIndex:0,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),t())},sx:{px:1.5,py:.75,fontSize:`11px`,color:`info.main`,cursor:`pointer`,userSelect:`none`,"&:hover":{backgroundColor:`action.hover`}},children:[`+ show `,Math.min(20,e),` more`,` `,z(B,{component:`span`,sx:{color:`text.disabled`,ml:.5},children:[`(`,e,` hidden)`]})]})}function Ae({node:e,onCenterFocus:t,cllImpacted:n}){let r=he(me(e),n),{isDark:i,background:a}=d(),o=ge(r),s=r===`impacted`?Y(i):o,c=r===`impacted`?Y(i):`divider`;return z(K,{direction:`row`,spacing:1,sx:{alignItems:`center`,px:1.5,py:1.25,backgroundColor:i?a.emphasized:`rgb(255 245 241)`,borderTop:`1px solid`,borderBottom:`1px solid`,borderColor:`divider`},children:[R(B,{component:`span`,sx:{width:`4px`,height:`26px`,borderRadius:`2px`,backgroundColor:o,flex:`0 0 auto`}}),R(B,{component:`span`,sx:{flex:1,minWidth:0,fontFamily:`ui-monospace, 'IBM Plex Mono', monospace`,fontSize:`13px`,fontWeight:700,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},title:e.data.name,children:e.data.name}),t&&R(H,{title:`Center on canvas`,placement:`top`,children:R(W,{size:`small`,"aria-label":`Center on canvas`,onClick:t,sx:{p:.5,color:`text.secondary`,flex:`0 0 auto`},children:R(oe,{size:14})})}),R(B,{component:`span`,sx:{fontSize:`10px`,fontWeight:600,px:.875,py:`2px`,borderRadius:`3px`,color:s,backgroundColor:`background.paper`,border:`1px solid`,borderColor:c,textTransform:`capitalize`,flex:`0 0 auto`},children:r})]})}function je({previousName:e,currentName:t,onJumpBack:n}){let{isDark:r}=d();return z(K,{direction:`row`,spacing:.75,sx:{alignItems:`center`,minWidth:0,flex:1,overflow:`hidden`,fontFamily:`ui-monospace, 'IBM Plex Mono', monospace`,fontSize:`12px`},children:[R(B,{component:`span`,sx:{color:`text.secondary`,fontFamily:`inherit`,flex:`0 0 auto`},children:`Path`}),R(B,{component:`span`,role:n?`button`:void 0,tabIndex:n?0:void 0,onClick:n,onKeyDown:n?e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),n())}:void 0,sx:{color:`text.primary`,cursor:n?`pointer`:`default`,textDecoration:`underline`,textDecorationColor:r?`rgb(255 255 255 / 0.35)`:`rgb(0 0 0 / 0.25)`,textUnderlineOffset:`2px`,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`,minWidth:0,"&:hover":n?{textDecorationColor:`currentColor`}:void 0},title:e,children:e}),R(B,{component:`span`,"aria-hidden":`true`,sx:{color:`text.disabled`,flex:`0 0 auto`},children:`›`}),R(B,{component:`span`,sx:{color:`primary.main`,fontWeight:700,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`,minWidth:0},title:t,children:t})]})}function Me({node:e,nodesById:t,onNavigate:n,onBack:r,onCenterFocus:i,historyTrail:a,onJumpToHistory:o,impactingNodeIds:s,impactedNodeIds:c}){let{background:l}=d(),[u,f]=I(``),[p,m]=I(``),[h,g]=I(8),[_,v]=I(8),[y,b]=I(!1),[x,S]=I(!1);P(()=>{f(``),m(``),g(8),v(8),b(!1),S(!1)},[e.id]);let C=F(()=>Object.keys(e.data.parents??{}),[e.data.parents]),w=F(()=>Object.keys(e.data.children??{}),[e.data.children]),T=F(()=>_e(C,u,t),[C,u,t]),E=F(()=>_e(w,p,t),[w,p,t]),D=((s?.size??0)>0||(c?.size??0)>0)&&((s?.has(e.id)??!1)||(c?.has(e.id)??!1)),O=(e,t)=>D?(t===`up`?s:c)?.has(e)??!1:!1,k=D?C.filter(e=>s?.has(e)).length:0,A=D?w.filter(e=>c?.has(e)).length:0,j=e=>{let r=e===`up`,i=r?C:w,a=r?T:E,o=(r?y:x)?a.filter(t=>O(t,e)):a,s=r?u:p,l=r?f:m,d=r?h:_,b=r?g:v,S=i.length>8,D=o.slice(0,d),k=o.length-D.length,A=r?`(source — no upstream)`:`(leaf — no downstream)`;return z(L,{children:[S&&R(Oe,{direction:e,query:s,onChange:e=>{l(e),b(8)}}),i.length===0?R(De,{label:A}):o.length===0?R(De,{label:`no matches`}):D.map(r=>R(be,{name:X(r,t),status:me(t?.[r]),direction:e,decorateAsImpacted:O(r,e),dotImpacted:c?.has(r)??!1,onClick:n?()=>n(r):void 0},`${e}-${r}`)),k>0&&R(ke,{hidden:k,onClick:()=>b(e=>Math.min(o.length,e+20))})]})},M=(a?.length??0)>0;return z(B,{sx:{height:`100%`,display:`flex`,flexDirection:`column`,backgroundColor:`background.paper`},"data-testid":`lineage-tab-content`,children:[(!!r||M)&&z(K,{direction:`row`,spacing:1,sx:{alignItems:`center`,px:1.25,py:.75,borderBottom:`1px solid`,borderColor:`divider`,backgroundColor:l.subtle,minWidth:0},children:[r&&R(H,{title:`Back to previous node`,placement:`top`,children:R(W,{size:`small`,"aria-label":`Back to previous node`,onClick:r,sx:{flex:`0 0 auto`,border:`1px solid`,borderColor:`divider`,borderRadius:`6px`,px:1,py:.375,color:`text.secondary`,backgroundColor:`background.paper`,"&:hover":{backgroundColor:`action.hover`}},children:R(ie,{size:14})})}),M&&a?R(je,{previousName:X(a[a.length-1],t),currentName:e.data.name,onJumpBack:o?()=>o(a.length-1):r}):R(B,{sx:{flex:1}})]}),z(B,{sx:{flex:1,overflowY:`auto`,minHeight:0},children:[R(Ee,{direction:`up`,directCount:C.length,impactCount:k,onlyImpact:y,onToggleOnlyImpact:()=>{b(e=>!e),g(8)}}),j(`up`),R(Ae,{node:e,onCenterFocus:i,cllImpacted:c?.has(e.id)??!1}),R(Ee,{direction:`down`,directCount:w.length,impactCount:A,onlyImpact:x,onToggleOnlyImpact:()=>{S(e=>!e),v(8)}}),j(`down`)]})]})}function Ne(e,t){let n=[],r=[],{selectedNodes:i,cll:a,existingPositions:o,newCllExperience:s,columnAncestry:c}=t??{},l={};function u(e){return e===`removed`?0:e===`added`?2:1}function d(e,t){let n=u(e.data?.changeStatus),r=u(t.data?.changeStatus);return n<r?-1:+(n>r)}let f=i===void 0?void 0:new Set(i),p=Object.values(e.nodes).sort(d);for(let e of p){if(f&&!f.has(e.id))continue;let t=new Set,i=0;if(a&&!s){let o=a.current,s=o?.parent_map[e.id]??new Set;for(let t of s){let n=t,i=e.id;r.push({id:`m2c_${n}_${i}`,source:n,target:i,style:{zIndex:9999}})}let c=Object.entries(o?.columns??{}).filter(([t])=>t.startsWith(`${e.id}_`)).map(([t])=>t.slice(e.id.length+1));for(let o of c){let s=`${e.id}_${o}`,c=a.current,l=c?.columns[s],u=c?.parent_map[s]??new Set;if(l!=null){n.push({id:s,position:{x:10,y:70+i*24},parentId:e.id,extent:`parent`,draggable:!1,className:`no-track-pii-safe`,data:{node:e.data,column:l.name,type:l.type,transformationType:l.transformation_type,changeStatus:M(l.change_status)},style:{zIndex:9999},type:`lineageGraphColumnNode`,targetPosition:V.Left,sourcePosition:V.Right});for(let e of u){let t=e,n=s;r.push({id:`${t}_${n}`,source:t,target:n,style:{zIndex:9999}})}i++,t.add(l.name)}}}l[e.id]=t;let c=60;i>0&&(c+=20+i*24);let u=o?.get(e.id);n.unshift({id:e.id,position:u??{x:0,y:0},width:300,height:c,className:`no-track-pii-safe`,data:{...e.data},type:`lineageGraphNode`,targetPosition:V.Left,sourcePosition:V.Right,style:{width:300,height:c}})}let m=Object.values(e.edges).sort(d);for(let e of m)f&&(!f.has(e.source)||!f.has(e.target))||r.push({id:e.id,type:`lineageGraphEdge`,source:e.source,target:e.target,data:{...e.data}});return s&&c&&a&&Pe(n,r,c,a,f),n.some(e=>e.type===`lineageGraphNode`&&!o?.has(e.id))&&Fe(n,r),[n,r,l]}function Pe(e,t,n,r,i){let a=new Set,o=new Map;for(let[t,s]of n)if(!(i&&!i.has(t))){for(let n=0;n<s.length;n++){let i=s[n],c=`${t}_${i.column}`;a.add(c),o.set(c,{modelId:t,isImpacted:i.isImpacted});let l=r.current.columns[c];e.push({id:c,position:{x:10,y:64+n*24},parentId:t,draggable:!1,className:`no-track-pii-safe`,data:{node:{id:t},column:i.column,type:l?.type,transformationType:i.transformationType,changeStatus:i.changeStatus,isHighlighted:!0,isFocused:!1,isImpacted:i.isImpacted},style:{zIndex:9999},type:`lineageGraphColumnNode`,targetPosition:V.Left,sourcePosition:V.Right})}if(s.length>0){let n=e.find(e=>e.id===t&&e.type===`lineageGraphNode`);n&&(n.height=80+s.length*24)}}for(let e of a){let n=r.current.parent_map[e]??[];for(let r of n)if(a.has(r)){let n=o.get(r)?.isImpacted??!1;t.push({id:`ancestry_${r}_${e}`,source:r,target:e,style:{zIndex:9999,strokeWidth:2,stroke:n?p.impacted:p.unchanged}})}}}const Fe=(e,t,n=`LR`)=>{x(ue,e,t,n)};function Z({children:e,value:t,index:n}){return t===n?R(L,{children:e}):null}const Q=()=>R(`span`,{}),Ie=({children:e})=>R(L,{children:e}),Le=()=>!0;function $(e,t,n){return e?ne.add_or_remove:n(t)?``:`This action is not supported yet.`}function Re({node:e,actionCallbacks:t,runTypeIcons:n,isActionAvailable:r,rowCountDisplay:i}){let a=e.data.changeStatus===`added`||e.data.changeStatus===`removed`,o=n?.query??Q,s=n?.row_count??Q,c=n?.profile??Q;return z(K,{direction:`row`,sx:{alignItems:`center`,flexWrap:`wrap`,gap:1},children:[R(U,{size:`xsmall`,variant:`outlined`,color:`neutral`,startIcon:R(o,{fontSize:`small`}),onClick:t?.onQueryClick,sx:{textTransform:`none`},children:`Query`}),z(U,{size:`xsmall`,variant:`outlined`,color:`neutral`,startIcon:R(s,{fontSize:`small`}),onClick:t?.onRowCountClick,sx:{textTransform:`none`},children:[`Row Count`,i!=null&&z(L,{children:[`:\xA0`,i]})]}),R(H,{title:$(a,`profile`,r),placement:`top`,children:R(`span`,{children:R(U,{size:`xsmall`,variant:`outlined`,color:`neutral`,startIcon:R(c,{fontSize:`small`}),onClick:t?.onProfileClick,disabled:a,sx:{textTransform:`none`},children:`Profile`})})})]})}function ze({onClick:e,Icon:t}){return R(U,{size:`xsmall`,variant:`outlined`,color:`neutral`,startIcon:R(t,{fontSize:`small`}),onClick:e,sx:{textTransform:`none`},children:`Add schema diff to checklist`})}function Be({node:e,actionCallbacks:t,runTypeIcons:n,featureToggles:r,isActionAvailable:i,ConnectionPopoverWrapper:a,rowCountDisplay:o}){let s=r?.mode===`metadata only`,c=e.data.changeStatus===`added`||e.data.changeStatus===`removed`,l=n?.query_diff??Q,u=n?.row_count_diff??Q,d=n?.profile_diff??Q,f=n?.value_diff??Q,p=n?.top_k_diff??Q,m=n?.histogram_diff??Q,h=(e,t)=>s?R(a,{display:!0,children:e}):R(H,{title:$(c,t,i),placement:`top`,children:R(`span`,{children:e})});return z(K,{direction:`row`,sx:{alignItems:`center`,flexWrap:`wrap`,gap:2},children:[R(G,{variant:`caption`,sx:{fontWeight:`bold`},children:`Diff`}),z(K,{direction:`row`,sx:{alignItems:`center`,flexWrap:`wrap`,gap:1,width:`93%`},children:[R(a,{display:s,children:z(U,{size:`xsmall`,variant:`outlined`,color:`neutral`,startIcon:R(u,{fontSize:`small`}),onClick:t?.onRowCountDiffClick,disabled:r?.disableDatabaseQuery,sx:{textTransform:`none`},children:[`Row Count`,o!=null&&z(L,{children:[`:\xA0`,o]})]})}),h(R(U,{size:`xsmall`,variant:`outlined`,color:`neutral`,startIcon:R(d,{fontSize:`small`}),onClick:t?.onProfileDiffClick,disabled:c||r?.disableDatabaseQuery,sx:{textTransform:`none`},children:`Profile`}),`profile_diff`),h(R(U,{size:`xsmall`,variant:`outlined`,color:`neutral`,startIcon:R(f,{fontSize:`small`}),onClick:t?.onValueDiffClick,disabled:c||r?.disableDatabaseQuery,sx:{textTransform:`none`},children:`Value`}),`value_diff`),h(R(U,{size:`xsmall`,variant:`outlined`,color:`neutral`,startIcon:R(p,{fontSize:`small`}),onClick:t?.onTopKDiffClick,disabled:c||r?.disableDatabaseQuery,sx:{textTransform:`none`},children:`Top-K`}),`top_k_diff`),h(R(U,{size:`xsmall`,variant:`outlined`,color:`neutral`,startIcon:R(m,{fontSize:`small`}),onClick:t?.onHistogramDiffClick,disabled:c||r?.disableDatabaseQuery,sx:{textTransform:`none`},children:`Histogram`}),`histogram_diff`),R(a,{display:s,children:R(U,{size:`xsmall`,variant:`outlined`,color:`neutral`,startIcon:R(l,{fontSize:`small`}),onClick:t?.onQueryDiffClick,disabled:r?.disableDatabaseQuery,sx:{textTransform:`none`},children:`Query`})})]})]})}function Ve({node:n,onCloseNode:r,isSingleEnv:i,featureToggles:a,modelDetail:o,lineageTabContent:s,SchemaView:c,SingleEnvSchemaView:u,NodeSqlView:f,ResourceTypeTag:p,NotificationComponent:m,ConnectionPopoverWrapper:h=Ie,runTypeIcons:g,actionCallbacks:_,isActionAvailable:v=Le,isWholeModelChanged:y=!1,isWholeModelImpacted:b=!1,wholeModelImpact:x=!1,isImpacted:S=!1,rowCountDisplay:C}){let w=n.data.resourceType===`model`||n.data.resourceType===`seed`||n.data.resourceType===`source`||n.data.resourceType===`snapshot`,[T,E]=I(!0),[D,O]=I(0),{base:k,current:A}=o??{},j=!i&&n.data.change?.columns!=null&&Object.keys(n.data.change.columns).length>0,M=!i&&n.data.changeStatus===`modified`,N=n.data.resourceType===`model`||n.data.resourceType===`seed`||n.data.resourceType===`snapshot`,ee=!i&&N&&_?.onAddSchemaDiffClick!=null,te=g?.schema_diff??Q,{isDark:ne}=d(),P={wholeModelImpact:x,isWholeModelChanged:y,isWholeModelImpacted:b,isImpacted:S,changeCategory:n.data.change?.category},F=e(P,ne),L=l({name:n.data.name,resourceType:n.data.resourceType,materialized:n.data.materialized},P);return z(B,{className:F?`cll-experience`:void 0,sx:{height:`100%`,display:`flex`,flexDirection:`column`,borderLeft:`3px solid ${F?F.tokens.stripeAccent:`transparent`}`},children:[z(K,{direction:`row`,sx:{alignItems:`center`,px:2,py:1.5,gap:1},children:[R(H,{title:L,placement:`top`,children:F?R(t,{tokens:F.tokens,variant:`titleChip`,testId:`whole-model-${F.kind}-title-chip`,sx:{flex:`0 1 auto`,mr:`auto`,minWidth:0},children:R(G,{variant:`subtitle1`,component:`span`,className:`no-track-pii-safe`,sx:{fontWeight:600,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`,color:`inherit`},children:n.data.name})}):R(G,{component:`span`,variant:`subtitle1`,className:`no-track-pii-safe`,sx:{fontWeight:600,flex:`0 1 auto`,mr:`auto`,minWidth:0,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:n.data.name})}),p&&R(B,{sx:{color:`text.secondary`,flexShrink:0},children:R(p,{node:n})}),R(W,{size:`small`,onClick:r,sx:{flexShrink:0},children:R(re,{})})]}),N&&R(B,{sx:{pl:2,py:1},children:i?R(Re,{node:n,actionCallbacks:_,runTypeIcons:g,isActionAvailable:v,rowCountDisplay:C}):R(Be,{node:n,actionCallbacks:_,runTypeIcons:g,featureToggles:a,isActionAvailable:v,ConnectionPopoverWrapper:h,rowCountDisplay:C})}),w&&z(B,{sx:{overflow:`auto`,display:`flex`,flexDirection:`column`,flex:1,minHeight:0},children:[i&&T&&m&&R(B,{sx:{p:1.5},children:R(m,{onClose:()=>E(!1),children:R(G,{variant:`body2`,children:`Enable the Recce Checklist and start adding checks for better data validation and review.`})})}),z(ce,{value:D,onChange:(e,t)=>O(t),sx:{borderBottom:1,borderColor:`divider`},children:[R(J,{label:z(B,{component:`span`,sx:{display:`flex`,alignItems:`center`,gap:.75},children:[`Columns`,j&&R(B,{component:`span`,sx:{color:`amber.main`,fontSize:`0.5rem`,lineHeight:1},children:`●`})]})}),R(J,{label:z(B,{component:`span`,sx:{display:`flex`,alignItems:`center`,gap:.75},children:[`Code`,M&&R(B,{component:`span`,sx:{color:`amber.main`,fontSize:`0.5rem`,lineHeight:1},children:`●`})]})}),s&&R(J,{label:`Lineage`})]}),z(B,{sx:{overflow:`auto`,height:`calc(100% - 48px)`},children:[R(Z,{value:D,index:0,children:R(B,{sx:{overflowY:`auto`,height:`100%`},children:i?u&&R(u,{current:A}):c&&R(c,{base:k,current:A,columnChanges:n.data.change?.columns,onViewCode:()=>O(1),headerAction:ee?R(ze,{onClick:_?.onAddSchemaDiffClick,Icon:te}):void 0})})}),R(Z,{value:D,index:1,children:R(B,{sx:{height:`100%`},children:f&&R(f,{node:n})})}),s&&R(Z,{value:D,index:2,children:R(B,{sx:{height:`100%`},children:s})})]})]})]})}const He=`0.2.0`;export{He as ADVANCED_API_VERSION,j as COLUMN_HEIGHT,g as DIM_FILTER,o as EXPLORE_MIN_ZOOM,s as FIT_VIEW_PADDING,m as LEGIBLE_MIN_ZOOM,i as LineageCanvas,Me as LineageTabContent,Ve as NodeView,c as RowCountDiffTag,n as RowCountSummary,f as RowCountTag,E as buildLineageGraph,_ as colors,de as computeImpactedColumns,fe as computeIsImpacted,k as getNeighborSet,w as intersect,C as isLineageGraphColumnNode,O as isLineageGraphNode,Fe as layout,x as layoutWithDagre,D as selectDownstream,A as selectUpstream,Ne as toReactFlow,S as toReactFlowBasic,T as union,ee as useIdleTimeout,N as useLineageGraphContext,v as useRecceActionContext,b as useRecceInstanceContext,y as useRecceInstanceInfo,te as useRunsAggregated,d as useThemeColors};
3
3
  //# sourceMappingURL=advanced.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"advanced.js","names":[],"sources":["../src/advanced.ts"],"sourcesContent":["// @datarecce/ui/advanced - Lower-level components for custom composition\n// These exports may change between minor versions\n\n\"use client\";\n\n/**\n * Version marker for the advanced surface.\n */\nexport const ADVANCED_API_VERSION = \"0.2.0\";\n\n// =============================================================================\n// LINEAGE UTILITIES\n// =============================================================================\n// NOTE: Lineage types canonical in @datarecce/ui/types\n// NOTE: Lineage utilities canonical in @datarecce/ui/contexts\n\n/**\n * Lineage graph types for advanced consumers.\n * @deprecated Import from @datarecce/ui/types instead\n */\nexport type {\n EnvInfo,\n LineageGraph,\n LineageGraphColumnNode,\n LineageGraphEdge,\n LineageGraphNode,\n LineageGraphNodes,\n} from \"./contexts/lineage/types\";\n/**\n * Lineage graph type guards.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n isLineageGraphColumnNode,\n isLineageGraphNode,\n} from \"./contexts/lineage/types\";\n/**\n * Graph building and layout utilities.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n buildLineageGraph,\n COLUMN_HEIGHT,\n getNeighborSet,\n intersect,\n layoutWithDagre,\n type NodeColumnSetMap,\n selectDownstream,\n selectUpstream,\n toReactFlowBasic,\n union,\n} from \"./contexts/lineage/utils\";\n\n// =============================================================================\n// LINEAGE CANVAS\n// =============================================================================\n\n/**\n * Determine whether a model node is \"impacted\" for the new CLL experience.\n *\n * A node is impacted if ANY of:\n * 1. CLL analysis marks the node as impacted (node.impacted is truthy)\n * 2. Any column belonging to this node has a non-null change_status\n * 3. The model itself has a non-null changeStatus (added/removed/modified)\n */\nexport { type ColumnAnnotation } from \"./components/lineage/computeColumnLineage\";\nexport { computeImpactedColumns } from \"./components/lineage/computeImpactedColumns\";\nexport { computeIsImpacted } from \"./components/lineage/computeIsImpacted\";\n/**\n * Zoom and display constants for the lineage view.\n *\n * @remarks\n * Use with LineageCanvas fitViewOptions to match production behavior.\n */\nexport {\n DIM_FILTER,\n EXPLORE_MIN_ZOOM,\n FIT_VIEW_PADDING,\n LEGIBLE_MIN_ZOOM,\n} from \"./components/lineage/config/zoomConstants\";\n/**\n * Low-level lineage canvas component for custom graph rendering.\n *\n * @remarks\n * Exports: LineageCanvas, LineageCanvasProps.\n */\nexport {\n LineageCanvas,\n type LineageCanvasProps,\n} from \"./components/lineage/LineageCanvas\";\n/**\n * Lineage tab body — direct upstream/downstream + breadcrumb path —\n * intended to be injected into NodeView's `lineageTabContent` slot.\n */\nexport {\n LineageTabContent,\n type LineageTabContentProps,\n} from \"./components/lineage/LineageTabContent\";\n/**\n * OSS-specific toReactFlow with Column-Level Lineage (CLL) support.\n *\n * Converts a LineageGraph to React Flow nodes and edges, including\n * column nodes when CLL data is provided.\n *\n * @example\n * ```tsx\n * import { toReactFlow } from '@datarecce/ui/advanced';\n *\n * const [nodes, edges, columnSetMap] = toReactFlow(lineageGraph, {\n * cll: columnLineageData,\n * });\n * ```\n */\nexport { layout, toReactFlow } from \"./components/lineage/lineage\";\n/**\n * Row-count tags rendered inside `NodeView`. They read row-count data from\n * `LineageGraphProvider`'s `runsAggregated` prop, keyed by `node.id`.\n *\n * `RowCountSummary` is an inline-content variant (no pill background, no\n * refresh button) that accepts either a single-env `RowCount` or a\n * `RowCountDiff`. Intended for embedding in buttons or other tight layouts\n * via `NodeView`'s `rowCountDisplay` slot.\n */\nexport {\n RowCountDiffTag,\n RowCountSummary,\n RowCountTag,\n} from \"./components/lineage/NodeTag\";\n/**\n * Node detail panel with Columns and Code tabs.\n */\nexport {\n NodeView,\n type NodeViewActionCallbacks,\n type NodeViewNodeData,\n type NodeViewProps,\n type RunTypeIconMap,\n type SchemaViewProps,\n} from \"./components/lineage/NodeView\";\n\n// =============================================================================\n// CONTEXT HOOKS\n// =============================================================================\n// NOTE: Context hooks canonical in @datarecce/ui/contexts\n// NOTE: Context types canonical in @datarecce/ui/types\n\n/**\n * Recce action context hooks and types.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n type RecceActionContextType,\n useRecceActionContext,\n} from \"./contexts/action\";\n/**\n * Idle timeout context hooks and types.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n type IdleTimeoutContextType,\n useIdleTimeout,\n} from \"./contexts/idle\";\n/**\n * Recce instance context hooks and types.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n type InstanceInfoType,\n useRecceInstanceContext,\n useRecceInstanceInfo,\n} from \"./contexts/instance\";\n/**\n * Lineage context hooks for direct access.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n useLineageGraphContext,\n useRunsAggregated,\n} from \"./contexts/lineage\";\n\n// =============================================================================\n// THEME UTILITIES\n// =============================================================================\n\n/**\n * Theme color hook for advanced consumers.\n *\n * @remarks\n * Exports: useThemeColors.\n */\nexport { useThemeColors } from \"./hooks/useThemeColors\";\n\n/**\n * Theme color palette exports.\n * @deprecated Import from @datarecce/ui/theme instead\n * @deprecated Types (ColorShade, SemanticColorVariant) canonical in @datarecce/ui/types\n */\nexport {\n type ColorShade,\n colors,\n type SemanticColorVariant,\n} from \"./theme/colors\";\n"],"mappings":";sXAQA,MAAa,EAAuB"}
1
+ {"version":3,"file":"advanced.js","names":["MuiTooltip"],"sources":["../src/components/lineage/computeImpactedColumns.ts","../src/components/lineage/computeIsImpacted.ts","../src/components/lineage/LineageTabContent.tsx","../src/components/lineage/lineage.ts","../src/components/lineage/NodeView.tsx","../src/advanced.ts"],"sourcesContent":["import type { ColumnLineageData } from \"../../api\";\n\n/**\n * Walk the CLL parent_map to find all columns impacted by upstream changes.\n *\n * A column is impacted if it traces upstream (via parent_map) to any column\n * with a non-null change_status. Uses memoized DFS to avoid redundant traversal.\n *\n * @returns Set of column IDs that are impacted\n */\nexport function computeImpactedColumns(cll: ColumnLineageData): Set<string> {\n const { columns, parent_map } = cll.current;\n\n const memo = new Map<string, boolean | \"pending\">();\n\n function isImpacted(columnId: string): boolean {\n const cached = memo.get(columnId);\n if (cached === \"pending\") return false; // cycle\n if (cached !== undefined) return cached;\n\n memo.set(columnId, \"pending\");\n\n // Directly changed\n const col = columns[columnId];\n if (col?.change_status) {\n memo.set(columnId, true);\n return true;\n }\n\n // Check upstream\n const parents = parent_map[columnId] ?? [];\n for (const parent of parents) {\n if (isImpacted(parent)) {\n memo.set(columnId, true);\n return true;\n }\n }\n\n memo.set(columnId, false);\n return false;\n }\n\n const impacted = new Set<string>();\n for (const columnId of Object.keys(columns)) {\n if (isImpacted(columnId)) {\n impacted.add(columnId);\n }\n }\n\n return impacted;\n}\n","import type { ColumnLineageData } from \"../../api\";\nimport type { NodeChangeStatus } from \"./nodes/LineageNode\";\n\n/**\n * Determine whether a model node is \"impacted\" for the new CLL experience.\n *\n * A node is impacted if:\n * 1. The model itself has a non-null changeStatus (added/removed/modified)\n * 2. The CLL API explicitly marks it as impacted (e.g., breaking change propagation\n * where no individual column entries exist)\n * 3. Any column belonging to this node traces upstream to a changed column\n * (determined by walking the CLL parent_map)\n */\nexport function computeIsImpacted(\n nodeId: string,\n cll: ColumnLineageData | null,\n changeStatus: NodeChangeStatus | undefined,\n impactedColumns: Set<string>,\n): boolean {\n // Model-level change\n if (changeStatus) return true;\n if (!cll) return false;\n\n // Check if the CLL API explicitly marks this node as impacted\n const cllNode = cll.current.nodes[nodeId];\n if (cllNode?.impacted) return true;\n\n // Check if any column on this node is impacted via parent_map walk\n const cols = impactedColumns;\n const prefix = `${nodeId}_`;\n for (const colId of cols) {\n if (colId.startsWith(prefix)) return true;\n }\n\n return false;\n}\n","\"use client\";\n\n/**\n * @file LineageTabContent.tsx\n * @description \"Lineage\" tab inside the Model Detail panel — shows direct\n * upstream parents and direct downstream children for the focused node.\n *\n * Layout (top → bottom):\n * - Toolbar: back button + breadcrumb path of visited nodes + center-on-canvas\n * - UPSTREAM section: direct parents (filterable / paginated)\n * - Focused-node card (orange accent)\n * - DOWNSTREAM section: direct children (filterable / paginated)\n *\n * Impact marks: when the focused node and a neighbor are both in the impact\n * set, the row gets a yellow rail/tint/arrow (matching the canvas via\n * `cllChangeStatusColors.impacted`). Tooltip is \"Impacts this model\"\n * upstream / \"Impacted by this model\" downstream.\n */\n\nimport Box from \"@mui/material/Box\";\nimport IconButton from \"@mui/material/IconButton\";\nimport InputBase from \"@mui/material/InputBase\";\nimport Stack from \"@mui/material/Stack\";\nimport Tooltip from \"@mui/material/Tooltip\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport {\n MdArrowBack,\n MdArrowDownward,\n MdArrowUpward,\n MdGpsFixed,\n MdSearch,\n} from \"react-icons/md\";\nimport type { LineageGraphNode } from \"../../contexts/lineage/types\";\nimport { useThemeColors } from \"../../hooks\";\nimport {\n changeStatusColors,\n cllChangeStatusColors,\n cllImpactedAccent,\n cllImpactedBadgeBg,\n cllImpactedBadgeFg,\n} from \"./styles\";\n\n// Translucent overlay (vs. the solid `cllChangeStatusBackgrounds*.impacted`\n// canvas fill) so the row's `background.paper` shows through.\nconst impactTint = {\n light: \"rgb(252 211 77 / 0.18)\",\n dark: \"rgb(252 211 77 / 0.10)\",\n} as const;\n\n/** Text/thin-icon color for impacted accents — needs stronger contrast than\n * the canvas amber (`cllChangeStatusColors.impacted`), which works only as a\n * filled shape (dot, rail, chip bg). Mirrors the chip foreground palette. */\nfunction impactedTextColor(isDark: boolean): string {\n return isDark ? cllImpactedBadgeFg.dark : cllImpactedBadgeFg.light;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Rows after which the side shows a filter input. */\nconst FILTER_THRESHOLD = 8;\n/** Initial number of direct rows shown per side. */\nconst INITIAL_PAGE_SIZE = 8;\n/** Additional rows revealed per \"show N more\" click. */\nconst PAGE_STEP = 20;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Direction = \"up\" | \"down\";\ntype ChangeStatus = \"added\" | \"removed\" | \"modified\" | \"unchanged\";\ntype EffectiveStatus = ChangeStatus | \"impacted\";\n\nexport interface LineageTabContentProps {\n node: LineageGraphNode;\n /** Lookup of all nodes in the graph for resolving parent/child ids. */\n nodesById?: Record<string, LineageGraphNode>;\n /** Refocus the panel to a different node. */\n onNavigate?: (nodeId: string) => void;\n /** Return to the previously focused node. Hidden when undefined. */\n onBack?: () => void;\n /** Pan/zoom the canvas onto the currently focused node. */\n onCenterFocus?: () => void;\n /** Stack of previously focused node ids, oldest first. */\n historyTrail?: string[];\n /** Jump to an entry in the history (breadcrumb click). */\n onJumpToHistory?: (index: number) => void;\n /**\n * Nodes that propagate impact downward (own breaking/partial_breaking change,\n * added/removed, or receive upstream impact). Drives upstream rail marks.\n */\n impactingNodeIds?: Set<string>;\n /** Nodes with CLL `impacted = true`. Drives downstream rail marks. */\n impactedNodeIds?: Set<string>;\n}\n\n// ---------------------------------------------------------------------------\n// Status helpers\n// ---------------------------------------------------------------------------\n\nfunction getChangeStatus(node: LineageGraphNode | undefined): ChangeStatus {\n return node?.data.changeStatus ?? \"unchanged\";\n}\n\n/** Unchanged-but-impacted nodes display as \"impacted\", matching the canvas. */\nfunction effectiveStatus(\n status: ChangeStatus,\n cllImpacted: boolean | undefined,\n): EffectiveStatus {\n return status === \"unchanged\" && cllImpacted ? \"impacted\" : status;\n}\n\nfunction colorForEffective(status: EffectiveStatus): string {\n return status === \"impacted\"\n ? cllChangeStatusColors.impacted\n : changeStatusColors[status];\n}\n\nfunction getDisplayName(\n id: string,\n nodesById: Record<string, LineageGraphNode> | undefined,\n): string {\n return nodesById?.[id]?.data.name ?? id;\n}\n\n/** Case-insensitive filter on display name. Empty query returns input unchanged. */\nfunction filterIds(\n ids: string[],\n query: string,\n nodesById: Record<string, LineageGraphNode> | undefined,\n): string[] {\n const q = query.trim().toLowerCase();\n if (!q) return ids;\n return ids.filter((id) =>\n getDisplayName(id, nodesById).toLowerCase().includes(q),\n );\n}\n\nfunction getChipTitle(onlyImpact: boolean, isUp: boolean): string {\n if (onlyImpact) return \"Show all models\";\n return isUp ? \"Show only impacting models\" : \"Show only impacted models\";\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\nfunction StatusDot({\n status,\n cllImpacted,\n}: {\n status: ChangeStatus;\n /** When true, color the dot as impacted (matches canvas). */\n cllImpacted?: boolean;\n}) {\n const effective = effectiveStatus(status, cllImpacted);\n return (\n <Box\n component=\"span\"\n data-testid=\"lineage-status-dot\"\n data-status={effective}\n sx={{\n width: \"8px\",\n height: \"8px\",\n borderRadius: \"2px\",\n backgroundColor: colorForEffective(effective),\n flex: \"0 0 auto\",\n }}\n />\n );\n}\n\ninterface DirectRowProps {\n name: string;\n status: ChangeStatus;\n direction: Direction;\n /** When true, decorate the row with the impact mark (rail + tint + arrow). */\n decorateAsImpacted?: boolean;\n /** Carries CLL `impacted = true` — drives the dot color, independent of\n * `decorateAsImpacted` (which drives row decoration). */\n dotImpacted?: boolean;\n onClick?: () => void;\n}\n\nfunction DirectRow({\n name,\n status,\n direction,\n decorateAsImpacted,\n dotImpacted,\n onClick,\n}: DirectRowProps) {\n const { isDark } = useThemeColors();\n const tooltip =\n direction === \"up\" ? \"Impacts this model\" : \"Impacted by this model\";\n return (\n <Box\n onClick={onClick}\n sx={{\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 0.75,\n pl: 1.5,\n pr: 1.25,\n py: 0.5,\n cursor: onClick ? \"pointer\" : \"default\",\n fontSize: \"12px\",\n fontFamily: \"ui-monospace, 'IBM Plex Mono', monospace\",\n lineHeight: 1.3,\n color: \"text.primary\",\n minWidth: 0,\n backgroundColor: decorateAsImpacted\n ? isDark\n ? impactTint.dark\n : impactTint.light\n : undefined,\n \"&:hover\": onClick ? { backgroundColor: \"action.hover\" } : undefined,\n }}\n >\n {decorateAsImpacted && (\n <Box\n aria-hidden=\"true\"\n sx={{\n position: \"absolute\",\n left: 0,\n top: 0,\n bottom: 0,\n width: \"3px\",\n backgroundColor: cllChangeStatusColors.impacted,\n }}\n />\n )}\n <StatusDot status={status} cllImpacted={dotImpacted} />\n <Box\n component=\"span\"\n sx={{\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n minWidth: 0,\n flex: 1,\n }}\n title={name}\n >\n {name}\n </Box>\n {decorateAsImpacted && (\n <Tooltip title={tooltip} placement=\"top\" arrow>\n <Box\n component=\"span\"\n aria-label={tooltip}\n data-testid=\"lineage-impact-mark\"\n sx={{\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"16px\",\n height: \"16px\",\n color: impactedTextColor(isDark),\n flex: \"0 0 auto\",\n }}\n >\n {/* Arrow is direction-agnostic; the tooltip carries the direction. */}\n <MdArrowDownward size={12} />\n </Box>\n </Tooltip>\n )}\n </Box>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Impact chip — shown in section headers when impactCount > 0\n// ---------------------------------------------------------------------------\n\ninterface ChipPaletteProps {\n isDark: boolean;\n active: boolean;\n}\n\nfunction chipBackground({ isDark, active }: ChipPaletteProps): string {\n // Active: deeper amber so white text passes contrast (yellow + white fails).\n if (active) return cllImpactedAccent.dark;\n return isDark ? cllImpactedBadgeBg.dark : cllImpactedBadgeBg.light;\n}\n\nfunction chipForeground({ isDark, active }: ChipPaletteProps): string {\n if (active) return \"#fff\";\n return isDark ? cllImpactedBadgeFg.dark : cllImpactedBadgeFg.light;\n}\n\nconst CHIP_BASE_SX = {\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: \"4px\",\n ml: 0.75,\n px: \"7px\",\n py: \"1px\",\n borderRadius: \"999px\",\n fontSize: \"10px\",\n fontWeight: 600,\n letterSpacing: \"0.02em\",\n};\n\nfunction ChipDot({ color }: { color: string }) {\n return (\n <Box\n component=\"span\"\n aria-hidden=\"true\"\n sx={{\n width: \"5px\",\n height: \"5px\",\n borderRadius: \"50%\",\n backgroundColor: color,\n }}\n />\n );\n}\n\nfunction ChipButton({\n label,\n title,\n active,\n isDark,\n onClick,\n}: {\n label: string;\n title: string;\n active: boolean;\n isDark: boolean;\n onClick: () => void;\n}) {\n const palette = { isDark, active };\n return (\n <Tooltip title={title} placement=\"top\" arrow>\n <Box\n component=\"button\"\n type=\"button\"\n data-testid=\"lineage-impact-chip\"\n aria-pressed={active}\n aria-label={title}\n onClick={onClick}\n sx={{\n ...CHIP_BASE_SX,\n backgroundColor: chipBackground(palette),\n color: chipForeground(palette),\n border: \"none\",\n font: \"inherit\",\n cursor: \"pointer\",\n \"&:hover\": {\n filter: active ? \"brightness(0.95)\" : \"brightness(0.97)\",\n },\n }}\n >\n <ChipDot color={chipForeground(palette)} />\n {label}\n </Box>\n </Tooltip>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Section header\n// ---------------------------------------------------------------------------\n\nfunction SectionHeader({\n direction,\n directCount,\n impactCount,\n onlyImpact,\n onToggleOnlyImpact,\n}: {\n direction: Direction;\n directCount: number;\n /** Number of direct neighbors on this side that are part of the impact chain. */\n impactCount?: number;\n /** Current state of the per-side \"only impact\" filter toggle. */\n onlyImpact: boolean;\n /** Toggles the per-side \"only impacted\" filter. */\n onToggleOnlyImpact: () => void;\n}) {\n const isUp = direction === \"up\";\n const ArrowIcon = isUp ? MdArrowUpward : MdArrowDownward;\n const { isDark, background } = useThemeColors();\n const count = impactCount ?? 0;\n const showChip = count > 0;\n const label = isUp ? `${count} impacting` : `${count} impacted`;\n\n return (\n <Stack\n direction=\"row\"\n spacing={0.75}\n sx={{\n alignItems: \"center\",\n px: 1.5,\n py: 0.875,\n backgroundColor: background.subtle,\n borderTop: \"1px solid\",\n borderBottom: \"1px solid\",\n borderColor: \"divider\",\n fontSize: \"11px\",\n color: \"text.secondary\",\n fontWeight: 600,\n }}\n >\n <ArrowIcon size={11} />\n <Box\n component=\"span\"\n sx={{ textTransform: \"uppercase\", letterSpacing: \"0.06em\" }}\n >\n {isUp ? \"Upstream\" : \"Downstream\"}\n </Box>\n <Box component=\"span\" sx={{ color: \"text.disabled\", fontWeight: 500 }}>\n · {directCount} direct\n </Box>\n {showChip && (\n <ChipButton\n label={label}\n title={getChipTitle(onlyImpact, isUp)}\n active={onlyImpact}\n isDark={isDark}\n onClick={onToggleOnlyImpact}\n />\n )}\n </Stack>\n );\n}\n\nfunction EmptyRow({ label }: { label: string }) {\n return (\n <Box\n sx={{\n px: 1.5,\n py: 1,\n fontSize: \"11px\",\n color: \"text.disabled\",\n fontStyle: \"italic\",\n }}\n >\n {label}\n </Box>\n );\n}\n\ninterface FilterInputProps {\n direction: Direction;\n query: string;\n onChange: (next: string) => void;\n}\n\nfunction FilterInput({ direction, query, onChange }: FilterInputProps) {\n const label = direction === \"up\" ? \"Upstream\" : \"Downstream\";\n return (\n <Box\n sx={{\n px: 1.25,\n py: 0.75,\n borderBottom: \"1px solid\",\n borderColor: \"divider\",\n backgroundColor: \"background.paper\",\n }}\n >\n <Stack\n direction=\"row\"\n spacing={0.5}\n sx={{\n alignItems: \"center\",\n border: \"1px solid\",\n borderColor: \"divider\",\n borderRadius: \"4px\",\n px: 0.875,\n py: 0.25,\n }}\n >\n <MdSearch size={12} color=\"currentColor\" opacity={0.55} />\n <InputBase\n value={query}\n onChange={(e) => {\n onChange(e.target.value);\n }}\n placeholder=\"filter…\"\n inputProps={{\n \"aria-label\": `Filter ${label}`,\n style: {\n padding: 0,\n fontSize: \"11px\",\n fontFamily: \"ui-monospace, 'IBM Plex Mono', monospace\",\n },\n }}\n sx={{ flex: 1, fontSize: \"11px\" }}\n />\n </Stack>\n </Box>\n );\n}\n\ninterface ShowMoreRowProps {\n hidden: number;\n onClick: () => void;\n}\n\nfunction ShowMoreRow({ hidden, onClick }: ShowMoreRowProps) {\n return (\n <Box\n onClick={onClick}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick();\n }\n }}\n sx={{\n px: 1.5,\n py: 0.75,\n fontSize: \"11px\",\n color: \"info.main\",\n cursor: \"pointer\",\n userSelect: \"none\",\n \"&:hover\": { backgroundColor: \"action.hover\" },\n }}\n >\n + show {Math.min(PAGE_STEP, hidden)} more{\" \"}\n <Box component=\"span\" sx={{ color: \"text.disabled\", ml: 0.5 }}>\n ({hidden} hidden)\n </Box>\n </Box>\n );\n}\n\nfunction FocusCard({\n node,\n onCenterFocus,\n cllImpacted,\n}: {\n node: LineageGraphNode;\n onCenterFocus?: () => void;\n /** Drives the impacted accent on unchanged-but-impacted focus. */\n cllImpacted?: boolean;\n}) {\n const status = getChangeStatus(node);\n const effective = effectiveStatus(status, cllImpacted);\n const { isDark, background } = useThemeColors();\n const accentColor = colorForEffective(effective);\n // Pill text needs more contrast than the canvas amber on a paper bg —\n // use the deeper \"text\" amber when the status is impacted.\n const pillTextColor =\n effective === \"impacted\" ? impactedTextColor(isDark) : accentColor;\n const pillBorderColor =\n effective === \"impacted\" ? impactedTextColor(isDark) : \"divider\";\n return (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{\n alignItems: \"center\",\n px: 1.5,\n py: 1.25,\n backgroundColor: isDark ? background.emphasized : \"rgb(255 245 241)\",\n borderTop: \"1px solid\",\n borderBottom: \"1px solid\",\n borderColor: \"divider\",\n }}\n >\n <Box\n component=\"span\"\n sx={{\n width: \"4px\",\n height: \"26px\",\n borderRadius: \"2px\",\n backgroundColor: accentColor,\n flex: \"0 0 auto\",\n }}\n />\n <Box\n component=\"span\"\n sx={{\n flex: 1,\n minWidth: 0,\n fontFamily: \"ui-monospace, 'IBM Plex Mono', monospace\",\n fontSize: \"13px\",\n fontWeight: 700,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n title={node.data.name}\n >\n {node.data.name}\n </Box>\n {onCenterFocus && (\n <Tooltip title=\"Center on canvas\" placement=\"top\">\n <IconButton\n size=\"small\"\n aria-label=\"Center on canvas\"\n onClick={onCenterFocus}\n sx={{ p: 0.5, color: \"text.secondary\", flex: \"0 0 auto\" }}\n >\n <MdGpsFixed size={14} />\n </IconButton>\n </Tooltip>\n )}\n <Box\n component=\"span\"\n sx={{\n fontSize: \"10px\",\n fontWeight: 600,\n px: 0.875,\n py: \"2px\",\n borderRadius: \"3px\",\n color: pillTextColor,\n backgroundColor: \"background.paper\",\n border: \"1px solid\",\n borderColor: pillBorderColor,\n textTransform: \"capitalize\",\n flex: \"0 0 auto\",\n }}\n >\n {effective}\n </Box>\n </Stack>\n );\n}\n\ninterface PathBreadcrumbProps {\n /** Display name of the most recent previously focused node. */\n previousName: string;\n /** Display name of the currently focused node. */\n currentName: string;\n /** Click the previous-step name to jump back. */\n onJumpBack?: () => void;\n}\n\nfunction PathBreadcrumb({\n previousName,\n currentName,\n onJumpBack,\n}: PathBreadcrumbProps) {\n const { isDark } = useThemeColors();\n return (\n <Stack\n direction=\"row\"\n spacing={0.75}\n sx={{\n alignItems: \"center\",\n minWidth: 0,\n flex: 1,\n overflow: \"hidden\",\n fontFamily: \"ui-monospace, 'IBM Plex Mono', monospace\",\n fontSize: \"12px\",\n }}\n >\n <Box\n component=\"span\"\n sx={{\n color: \"text.secondary\",\n fontFamily: \"inherit\",\n flex: \"0 0 auto\",\n }}\n >\n Path\n </Box>\n <Box\n component=\"span\"\n role={onJumpBack ? \"button\" : undefined}\n tabIndex={onJumpBack ? 0 : undefined}\n onClick={onJumpBack}\n onKeyDown={\n onJumpBack\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onJumpBack();\n }\n }\n : undefined\n }\n sx={{\n color: \"text.primary\",\n cursor: onJumpBack ? \"pointer\" : \"default\",\n textDecoration: \"underline\",\n textDecorationColor: isDark\n ? \"rgb(255 255 255 / 0.35)\"\n : \"rgb(0 0 0 / 0.25)\",\n textUnderlineOffset: \"2px\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n minWidth: 0,\n \"&:hover\": onJumpBack\n ? { textDecorationColor: \"currentColor\" }\n : undefined,\n }}\n title={previousName}\n >\n {previousName}\n </Box>\n <Box\n component=\"span\"\n aria-hidden=\"true\"\n sx={{ color: \"text.disabled\", flex: \"0 0 auto\" }}\n >\n ›\n </Box>\n <Box\n component=\"span\"\n sx={{\n color: \"primary.main\",\n fontWeight: 700,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n minWidth: 0,\n }}\n title={currentName}\n >\n {currentName}\n </Box>\n </Stack>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Main component\n// ---------------------------------------------------------------------------\n\nexport function LineageTabContent({\n node,\n nodesById,\n onNavigate,\n onBack,\n onCenterFocus,\n historyTrail,\n onJumpToHistory,\n impactingNodeIds,\n impactedNodeIds,\n}: LineageTabContentProps) {\n const { background } = useThemeColors();\n\n // Per-side filter + pagination state.\n const [queryUp, setQueryUp] = useState(\"\");\n const [queryDown, setQueryDown] = useState(\"\");\n const [visibleUp, setVisibleUp] = useState(INITIAL_PAGE_SIZE);\n const [visibleDown, setVisibleDown] = useState(INITIAL_PAGE_SIZE);\n // Per-side \"only show impact\" toggle, driven by the header chip.\n const [onlyImpactUp, setOnlyImpactUp] = useState(false);\n const [onlyImpactDown, setOnlyImpactDown] = useState(false);\n\n // Reset state when the focused node changes. node.id is intentionally\n // the trigger — the effect doesn't *use* it, only watches it.\n // biome-ignore lint/correctness/useExhaustiveDependencies: node.id is the trigger, not a value used inside.\n useEffect(() => {\n setQueryUp(\"\");\n setQueryDown(\"\");\n setVisibleUp(INITIAL_PAGE_SIZE);\n setVisibleDown(INITIAL_PAGE_SIZE);\n setOnlyImpactUp(false);\n setOnlyImpactDown(false);\n }, [node.id]);\n\n const parentIds = useMemo(\n () => Object.keys(node.data.parents ?? {}),\n [node.data.parents],\n );\n const childIds = useMemo(\n () => Object.keys(node.data.children ?? {}),\n [node.data.children],\n );\n\n const filteredParentIds = useMemo(\n () => filterIds(parentIds, queryUp, nodesById),\n [parentIds, queryUp, nodesById],\n );\n const filteredChildIds = useMemo(\n () => filterIds(childIds, queryDown, nodesById),\n [childIds, queryDown, nodesById],\n );\n\n // Skip marks if the focus isn't itself in the impact chain — it has no\n // neighbors to \"impact\" or \"be impacted by\".\n const impactActive =\n (impactingNodeIds?.size ?? 0) > 0 || (impactedNodeIds?.size ?? 0) > 0;\n const focusInImpact =\n impactActive &&\n ((impactingNodeIds?.has(node.id) ?? false) ||\n (impactedNodeIds?.has(node.id) ?? false));\n\n const isImpactedNeighbor = (neighborId: string, dir: Direction): boolean => {\n if (!focusInImpact) return false;\n const set = dir === \"up\" ? impactingNodeIds : impactedNodeIds;\n return set?.has(neighborId) ?? false;\n };\n\n const upImpactCount = focusInImpact\n ? parentIds.filter((id) => impactingNodeIds?.has(id)).length\n : 0;\n const downImpactCount = focusInImpact\n ? childIds.filter((id) => impactedNodeIds?.has(id)).length\n : 0;\n\n const renderDirection = (direction: Direction) => {\n const isUp = direction === \"up\";\n const allDirect = isUp ? parentIds : childIds;\n const baseFiltered = isUp ? filteredParentIds : filteredChildIds;\n const onlyImpact = isUp ? onlyImpactUp : onlyImpactDown;\n const filtered = onlyImpact\n ? baseFiltered.filter((id) => isImpactedNeighbor(id, direction))\n : baseFiltered;\n const query = isUp ? queryUp : queryDown;\n const setQuery = isUp ? setQueryUp : setQueryDown;\n const visible = isUp ? visibleUp : visibleDown;\n const setVisible = isUp ? setVisibleUp : setVisibleDown;\n const showFilter = allDirect.length > FILTER_THRESHOLD;\n const visibleIds = filtered.slice(0, visible);\n const hidden = filtered.length - visibleIds.length;\n const emptyLabel = isUp\n ? \"(source — no upstream)\"\n : \"(leaf — no downstream)\";\n\n return (\n <>\n {showFilter && (\n <FilterInput\n direction={direction}\n query={query}\n onChange={(next) => {\n setQuery(next);\n setVisible(INITIAL_PAGE_SIZE);\n }}\n />\n )}\n {allDirect.length === 0 ? (\n <EmptyRow label={emptyLabel} />\n ) : filtered.length === 0 ? (\n <EmptyRow label=\"no matches\" />\n ) : (\n visibleIds.map((id) => (\n <DirectRow\n key={`${direction}-${id}`}\n name={getDisplayName(id, nodesById)}\n status={getChangeStatus(nodesById?.[id])}\n direction={direction}\n decorateAsImpacted={isImpactedNeighbor(id, direction)}\n dotImpacted={impactedNodeIds?.has(id) ?? false}\n onClick={onNavigate ? () => onNavigate(id) : undefined}\n />\n ))\n )}\n {hidden > 0 && (\n <ShowMoreRow\n hidden={hidden}\n onClick={() =>\n setVisible((n) => Math.min(filtered.length, n + PAGE_STEP))\n }\n />\n )}\n </>\n );\n };\n\n const hasTrail = (historyTrail?.length ?? 0) > 0;\n const showToolbar = !!onBack || hasTrail;\n\n return (\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n backgroundColor: \"background.paper\",\n }}\n data-testid=\"lineage-tab-content\"\n >\n {/* Toolbar: back + path breadcrumb + center */}\n {showToolbar && (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{\n alignItems: \"center\",\n px: 1.25,\n py: 0.75,\n borderBottom: \"1px solid\",\n borderColor: \"divider\",\n backgroundColor: background.subtle,\n minWidth: 0,\n }}\n >\n {onBack && (\n <Tooltip title=\"Back to previous node\" placement=\"top\">\n <IconButton\n size=\"small\"\n aria-label=\"Back to previous node\"\n onClick={onBack}\n sx={{\n flex: \"0 0 auto\",\n border: \"1px solid\",\n borderColor: \"divider\",\n borderRadius: \"6px\",\n px: 1,\n py: 0.375,\n color: \"text.secondary\",\n backgroundColor: \"background.paper\",\n \"&:hover\": { backgroundColor: \"action.hover\" },\n }}\n >\n <MdArrowBack size={14} />\n </IconButton>\n </Tooltip>\n )}\n {hasTrail && historyTrail ? (\n <PathBreadcrumb\n previousName={getDisplayName(\n historyTrail[historyTrail.length - 1],\n nodesById,\n )}\n currentName={node.data.name}\n onJumpBack={\n onJumpToHistory\n ? () => onJumpToHistory(historyTrail.length - 1)\n : onBack\n }\n />\n ) : (\n <Box sx={{ flex: 1 }} />\n )}\n </Stack>\n )}\n\n {/* Scrollable body */}\n <Box sx={{ flex: 1, overflowY: \"auto\", minHeight: 0 }}>\n <SectionHeader\n direction=\"up\"\n directCount={parentIds.length}\n impactCount={upImpactCount}\n onlyImpact={onlyImpactUp}\n onToggleOnlyImpact={() => {\n setOnlyImpactUp((v) => !v);\n setVisibleUp(INITIAL_PAGE_SIZE);\n }}\n />\n {renderDirection(\"up\")}\n\n <FocusCard\n node={node}\n onCenterFocus={onCenterFocus}\n cllImpacted={impactedNodeIds?.has(node.id) ?? false}\n />\n\n <SectionHeader\n direction=\"down\"\n directCount={childIds.length}\n impactCount={downImpactCount}\n onlyImpact={onlyImpactDown}\n onToggleOnlyImpact={() => {\n setOnlyImpactDown((v) => !v);\n setVisibleDown(INITIAL_PAGE_SIZE);\n }}\n />\n {renderDirection(\"down\")}\n </Box>\n </Box>\n );\n}\n","import dagre from \"@dagrejs/dagre\";\nimport { Position } from \"@xyflow/react\";\nimport {\n COLUMN_HEIGHT,\n type LineageGraph,\n type LineageGraphColumnNode,\n type LineageGraphEdge,\n type LineageGraphNode,\n type LineageGraphNodes,\n layoutWithDagre,\n type NodeColumnSetMap,\n} from \"../..\";\nimport type { ColumnLineageData } from \"../../api\";\nimport {\n type ColumnAnnotation,\n coerceCllChangeStatus,\n} from \"./computeColumnLineage\";\nimport { cllChangeStatusColors } from \"./styles\";\n\n/**\n * Convert a LineageGraph to React Flow nodes and edges with column-level lineage support\n *\n * This OSS-specific function extends the basic toReactFlow functionality with:\n * - Column-level lineage (CLL) visualization\n * - Dynamic node heights based on column count\n * - Column nodes as child nodes within parent model nodes\n *\n * @param lineageGraph - The lineage graph to convert\n * @param options - Conversion options\n * @param options.selectedNodes - Optional filter for which nodes to include\n * @param options.cll - Column-level lineage data for adding column nodes\n * @param options.existingPositions - Map of node IDs to their existing positions. If provided, nodes will preserve their positions and layout will be skipped if all nodes have positions.\n * @returns Tuple of [nodes, edges, nodeColumnSetMap] where nodeColumnSetMap tracks columns per node\n *\n * @example\n * ```tsx\n * const [nodes, edges, columnSetMap] = toReactFlow(lineageGraph, {\n * selectedNodes: [\"model.project.orders\"],\n * cll: columnLineageData,\n * });\n * ```\n */\nexport function toReactFlow(\n lineageGraph: LineageGraph,\n options?: {\n selectedNodes?: string[];\n cll?: ColumnLineageData;\n existingPositions?: Map<string, { x: number; y: number }>;\n newCllExperience?: boolean;\n columnAncestry?: Map<string, ColumnAnnotation[]>;\n },\n): [LineageGraphNodes[], LineageGraphEdge[], NodeColumnSetMap] {\n const nodes: LineageGraphNodes[] = [];\n const edges: LineageGraphEdge[] = [];\n const {\n selectedNodes,\n cll,\n existingPositions,\n newCllExperience,\n columnAncestry,\n } = options ?? {};\n\n const nodeColumnSetMap: NodeColumnSetMap = {};\n\n function getWeight(changeStatus?: string) {\n if (changeStatus === \"removed\") {\n return 0;\n } else if (changeStatus === \"added\") {\n return 2;\n } else {\n return 1;\n }\n }\n\n function compareFn(\n a: LineageGraphNode | LineageGraphEdge,\n b: LineageGraphNode | LineageGraphEdge,\n ) {\n const weightA = getWeight(a.data?.changeStatus);\n const weightB = getWeight(b.data?.changeStatus);\n\n if (weightA < weightB) {\n return -1;\n } else if (weightA > weightB) {\n return 1;\n }\n return 0;\n }\n\n const filterSet =\n selectedNodes !== undefined ? new Set(selectedNodes) : undefined;\n const sortedNodes = Object.values(lineageGraph.nodes).sort(compareFn);\n for (const node of sortedNodes) {\n if (filterSet && !filterSet.has(node.id)) {\n continue;\n }\n\n // add column nodes\n const nodeColumnSet = new Set<string>();\n let columnIndex = 0;\n if (cll && !newCllExperience) {\n const maybeCurrent = cll.current as unknown as\n | ColumnLineageData[\"current\"]\n | undefined;\n const parentMap = maybeCurrent?.parent_map[node.id] ?? new Set<string>();\n\n for (const parentKey of parentMap) {\n const source = parentKey;\n const target = node.id;\n\n edges.push({\n id: `m2c_${source}_${target}`,\n source,\n target,\n style: {\n zIndex: 9999,\n },\n });\n }\n\n const cllColumnNames = Object.entries(maybeCurrent?.columns ?? {})\n .filter(([key]) => key.startsWith(`${node.id}_`))\n .map(([key]) => key.slice(node.id.length + 1));\n\n for (const columnName of cllColumnNames) {\n const columnKey = `${node.id}_${columnName}`;\n const maybeCurrent = cll.current as unknown as\n | ColumnLineageData[\"current\"]\n | undefined;\n const column = maybeCurrent?.columns[columnKey];\n const parentMap =\n maybeCurrent?.parent_map[columnKey] ?? new Set<string>();\n\n if (column == null) {\n continue;\n }\n\n nodes.push({\n id: columnKey,\n position: { x: 10, y: 70 + columnIndex * COLUMN_HEIGHT },\n parentId: node.id,\n extent: \"parent\",\n draggable: false,\n className: \"no-track-pii-safe\",\n data: {\n node: node.data,\n column: column.name,\n type: column.type,\n transformationType: column.transformation_type,\n changeStatus: coerceCllChangeStatus(column.change_status),\n },\n style: {\n zIndex: 9999,\n },\n type: \"lineageGraphColumnNode\",\n targetPosition: Position.Left,\n sourcePosition: Position.Right,\n } as LineageGraphColumnNode);\n\n for (const parentColumn of parentMap) {\n const source = parentColumn;\n const target = columnKey;\n\n edges.push({\n id: `${source}_${target}`,\n source,\n target,\n style: {\n zIndex: 9999,\n },\n });\n }\n\n columnIndex++;\n nodeColumnSet.add(column.name);\n }\n }\n\n nodeColumnSetMap[node.id] = nodeColumnSet;\n\n let height = 60;\n if (columnIndex > 0) {\n height += 20 + columnIndex * COLUMN_HEIGHT;\n }\n\n const existingPosition = existingPositions?.get(node.id);\n nodes.unshift({\n id: node.id,\n position: existingPosition ?? { x: 0, y: 0 },\n width: 300,\n height: height,\n className: \"no-track-pii-safe\",\n data: {\n ...node.data,\n },\n type: \"lineageGraphNode\",\n targetPosition: Position.Left,\n sourcePosition: Position.Right,\n style: {\n width: 300,\n height: height,\n },\n } as LineageGraphNode);\n }\n\n const sortedEdges = Object.values(lineageGraph.edges).sort(compareFn);\n for (const edge of sortedEdges) {\n if (\n filterSet &&\n (!filterSet.has(edge.source) || !filterSet.has(edge.target))\n ) {\n continue;\n }\n\n edges.push({\n id: edge.id,\n type: \"lineageGraphEdge\",\n source: edge.source,\n target: edge.target,\n data: {\n ...edge.data,\n },\n } as LineageGraphEdge);\n }\n\n // Add column ancestry annotation nodes for new CLL experience column mode\n if (newCllExperience && columnAncestry && cll) {\n addColumnAncestryNodes(nodes, edges, columnAncestry, cll, filterSet);\n }\n\n // Only run layout if any parent node is missing a position\n const needsLayout = nodes.some(\n (node) =>\n node.type === \"lineageGraphNode\" && !existingPositions?.has(node.id),\n );\n\n if (needsLayout) {\n layout(nodes, edges);\n }\n\n return [nodes, edges, nodeColumnSetMap];\n}\n\n/**\n * Add column ancestry annotation nodes and edges to the graph.\n *\n * For each model in the ancestry map, creates child column nodes positioned\n * inside the model node, edges between ancestor columns that follow the\n * parent_map, and expands model node heights to fit their columns.\n */\nfunction addColumnAncestryNodes(\n nodes: LineageGraphNodes[],\n edges: LineageGraphEdge[],\n columnAncestry: Map<string, ColumnAnnotation[]>,\n cll: ColumnLineageData,\n filterSet: Set<string> | undefined,\n) {\n // Build columnId -> annotation lookup and set of ancestry column IDs\n const ancestryColumnIds = new Set<string>();\n const columnIdToAnnotation = new Map<\n string,\n { modelId: string; isImpacted: boolean }\n >();\n for (const [modelId, annotations] of columnAncestry) {\n // Skip columns whose model isn't in the current graph view\n if (filterSet && !filterSet.has(modelId)) continue;\n\n for (let i = 0; i < annotations.length; i++) {\n const annotation = annotations[i];\n const columnKey = `${modelId}_${annotation.column}`;\n ancestryColumnIds.add(columnKey);\n columnIdToAnnotation.set(columnKey, {\n modelId,\n isImpacted: annotation.isImpacted,\n });\n const col = cll.current.columns[columnKey];\n\n nodes.push({\n id: columnKey,\n position: { x: 10, y: 64 + i * COLUMN_HEIGHT },\n parentId: modelId,\n draggable: false,\n className: \"no-track-pii-safe\",\n data: {\n node: { id: modelId } as never,\n column: annotation.column,\n type: col?.type,\n transformationType: annotation.transformationType,\n changeStatus: annotation.changeStatus,\n isHighlighted: true,\n isFocused: false,\n isImpacted: annotation.isImpacted,\n },\n style: {\n zIndex: 9999,\n },\n type: \"lineageGraphColumnNode\",\n targetPosition: Position.Left,\n sourcePosition: Position.Right,\n } as LineageGraphColumnNode);\n }\n\n // Expand model node to fit its ancestry columns\n if (annotations.length > 0) {\n const modelNode = nodes.find(\n (n) => n.id === modelId && n.type === \"lineageGraphNode\",\n );\n if (modelNode) {\n modelNode.height = 60 + 20 + annotations.length * COLUMN_HEIGHT;\n }\n }\n }\n\n // Add edges between ancestry column nodes\n for (const columnId of ancestryColumnIds) {\n const parents = cll.current.parent_map[columnId] ?? [];\n for (const parentColumnId of parents) {\n if (ancestryColumnIds.has(parentColumnId)) {\n const sourceInfo = columnIdToAnnotation.get(parentColumnId);\n // Amber edge if source column is impacted\n const isSourceImpacted = sourceInfo?.isImpacted ?? false;\n edges.push({\n id: `ancestry_${parentColumnId}_${columnId}`,\n source: parentColumnId,\n target: columnId,\n style: {\n zIndex: 9999,\n strokeWidth: 2,\n stroke: isSourceImpacted\n ? cllChangeStatusColors.impacted\n : cllChangeStatusColors.unchanged,\n },\n });\n }\n }\n }\n}\n\n/**\n * Apply dagre layout to lineage graph nodes and edges\n *\n * This is a thin wrapper around layoutWithDagre from @datarecce/ui\n * that provides the dagre library instance.\n *\n * @param nodes - Array of lineage graph nodes\n * @param edges - Array of lineage graph edges\n * @param direction - Layout direction (\"LR\" for left-to-right, \"TB\" for top-to-bottom)\n */\nexport const layout = (\n nodes: LineageGraphNodes[],\n edges: LineageGraphEdge[],\n direction = \"LR\",\n): void => {\n layoutWithDagre(dagre, nodes, edges, direction);\n};\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport Tab from \"@mui/material/Tab\";\nimport Tabs from \"@mui/material/Tabs\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport {\n type ComponentType,\n type ReactElement,\n type ReactNode,\n useState,\n} from \"react\";\nimport { IoClose } from \"react-icons/io5\";\nimport type { NodeData } from \"../../api/info\";\nimport { DisableTooltipMessages } from \"../../constants\";\nimport { useThemeColors } from \"../../hooks\";\nimport type { ChangeCategory } from \"./nodes\";\nimport { TreatmentChip } from \"./TreatmentChip\";\nimport { getTitleRowTooltip, pickTitleChip } from \"./wholeModelTreatment\";\n\n// =============================================================================\n// TYPE DEFINITIONS\n// =============================================================================\n\n/**\n * Node data structure for NodeView.\n * Represents the data needed to display node details.\n */\nexport interface NodeViewNodeData {\n id: string;\n data: {\n name: string;\n resourceType?: string;\n changeStatus?: string;\n schema?: string;\n materialized?: string;\n change?: {\n category: string;\n columns?: Record<string, \"added\" | \"removed\" | \"modified\" | \"unknown\">;\n };\n };\n}\n\n/**\n * Run type icon configuration for dependency injection.\n * Maps run type names to their icon components.\n */\nexport interface RunTypeIconMap {\n query?: ComponentType<{ fontSize?: string }>;\n row_count?: ComponentType<{ fontSize?: string }>;\n row_count_diff?: ComponentType<{ fontSize?: string }>;\n profile?: ComponentType<{ fontSize?: string }>;\n profile_diff?: ComponentType<{ fontSize?: string }>;\n query_diff?: ComponentType<{ fontSize?: string }>;\n value_diff?: ComponentType<{ fontSize?: string }>;\n top_k_diff?: ComponentType<{ fontSize?: string }>;\n histogram_diff?: ComponentType<{ fontSize?: string }>;\n schema_diff?: ComponentType<{ fontSize?: string }>;\n}\n\n/**\n * Props for the SchemaView component (diff mode).\n */\nexport interface SchemaViewProps {\n base?: NodeData;\n current?: NodeData;\n columnChanges?: Record<\n string,\n \"added\" | \"removed\" | \"modified\" | \"unknown\"\n > | null;\n onViewCode?: () => void;\n /**\n * Optional action element rendered alongside the schema legend (e.g.\n * \"Add schema diff to checklist\" button). Diff mode only.\n */\n headerAction?: ReactNode;\n}\n\n/**\n * Props for the SingleEnvSchemaView component.\n */\nexport interface SingleEnvSchemaViewProps {\n current?: NodeData;\n}\n\n/**\n * Props for the NotificationComponent.\n */\nexport interface NotificationComponentProps {\n onClose: () => void;\n children?: ReactNode;\n}\n\n/**\n * Props for the ConnectionPopoverWrapper.\n */\nexport interface ConnectionPopoverWrapperProps {\n display: boolean;\n children: ReactNode;\n}\n\n/**\n * Callbacks for action button clicks.\n * Used for dependency injection of action handlers.\n */\nexport interface NodeViewActionCallbacks {\n /** Called when Query button is clicked */\n onQueryClick?: () => void;\n /** Called when Row Count button is clicked */\n onRowCountClick?: () => void;\n /** Called when Row Count Diff button is clicked */\n onRowCountDiffClick?: () => void;\n /** Called when Profile button is clicked */\n onProfileClick?: () => void;\n /** Called when Profile Diff button is clicked */\n onProfileDiffClick?: () => void;\n /** Called when Query Diff button is clicked */\n onQueryDiffClick?: () => void;\n /** Called when Value Diff button is clicked */\n onValueDiffClick?: () => void;\n /** Called when Top-K Diff button is clicked */\n onTopKDiffClick?: () => void;\n /** Called when Histogram Diff button is clicked */\n onHistogramDiffClick?: () => void;\n /** Called when Add Schema Diff button is clicked */\n onAddSchemaDiffClick?: () => void;\n}\n\n/**\n * Props for NodeView component.\n *\n * Uses dependency injection for:\n * - Schema view components (for different consumers)\n * - SQL view component (NodeSqlView)\n * - Action button icons\n * - Notification components\n * - Connection popover wrapper\n */\nexport interface NodeViewProps<\n TNode extends NodeViewNodeData = NodeViewNodeData,\n> {\n /** The node to display */\n node: TNode;\n /** Callback when close button is clicked */\n onCloseNode: () => void;\n /** Whether in single environment mode */\n isSingleEnv: boolean;\n /** Feature toggles for conditional UI */\n featureToggles?: {\n mode?: string | null;\n disableDatabaseQuery?: boolean;\n };\n /** On-demand model detail (columns, raw_code) fetched by the wrapper */\n modelDetail?: {\n base?: NodeData;\n current?: NodeData;\n };\n /**\n * Optional slot rendered as a \"Lineage\" tab body. When provided, a tab\n * labeled \"Lineage\" appears as the LAST tab (after Columns/Code).\n * Columns remains the default landing tab.\n * Consumers inject this to expose the focused node's upstream/downstream\n * without coupling NodeView to the lineage graph context.\n */\n lineageTabContent?: ReactNode;\n\n // =========================================================================\n // DEPENDENCY INJECTION: Components\n // =========================================================================\n\n /** Schema view component for diff mode */\n SchemaView?: ComponentType<SchemaViewProps>;\n /** Schema view component for single env mode */\n SingleEnvSchemaView?: ComponentType<SingleEnvSchemaViewProps>;\n /** Node SQL view component */\n NodeSqlView?: ComponentType<{ node: TNode }>;\n /** Resource type tag component (rendered in the header row) */\n ResourceTypeTag?: ComponentType<{ node: TNode }>;\n /** Notification component for single env mode */\n NotificationComponent?: ComponentType<NotificationComponentProps>;\n /** Wrapper component for buttons that need connection popover */\n ConnectionPopoverWrapper?: ComponentType<ConnectionPopoverWrapperProps>;\n /**\n * Optional inline display rendered after \"Row Count\" on the row count\n * action button (e.g. \"N/A → 280,844 rows\" with delta arrow). Omit when\n * no row-count data is available yet.\n */\n rowCountDisplay?: ReactNode;\n\n // =========================================================================\n // DEPENDENCY INJECTION: Icons\n // =========================================================================\n\n /** Map of run type names to icon components */\n runTypeIcons?: RunTypeIconMap;\n\n // =========================================================================\n // DEPENDENCY INJECTION: Callbacks\n // =========================================================================\n\n /** Action callbacks for button clicks */\n actionCallbacks?: NodeViewActionCallbacks;\n /** Check if an action is available */\n isActionAvailable?: (runType: string) => boolean;\n\n /** This model itself has a whole-model change — paints the brown title chip + brown left stripe. */\n isWholeModelChanged?: boolean;\n /** This model is downstream of a whole-model change — paints the amber title chip + amber left stripe. Precedence is enforced internally: `isWholeModelChanged` outranks this flag. */\n isWholeModelImpacted?: boolean;\n /** Whether the `--whole-model-impact` server flag is on. When false, no whole-model UI renders (no title chip, no left stripe). */\n wholeModelImpact?: boolean;\n /** This model is downstream of any breaking change. Only feeds the title-row hover tooltip (column-impacted kind) — no visual chip in NodeView. */\n isImpacted?: boolean;\n}\n\n// =============================================================================\n// INTERNAL COMPONENTS\n// =============================================================================\n\ninterface TabPanelProps {\n children?: ReactNode;\n index: number;\n value: number;\n}\n\nfunction TabPanel({ children, value, index }: TabPanelProps) {\n return value === index ? <>{children}</> : null;\n}\n\n// =============================================================================\n// DEFAULT IMPLEMENTATIONS\n// =============================================================================\n\n/** Default icon placeholder when no icon is provided */\nconst DefaultIcon = () => <span />;\n\n/** Default connection wrapper that just renders children */\nconst DefaultConnectionWrapper: ComponentType<\n ConnectionPopoverWrapperProps\n> = ({ children }) => <>{children}</>;\n\n/** Default check for action availability - always available */\nconst defaultIsActionAvailable = () => true;\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\n/**\n * Gets the disable reason for an action button.\n */\nfunction getDisableReason(\n isAddedOrRemoved: boolean,\n runType: string,\n isActionAvailable: (runType: string) => boolean,\n): string {\n if (isAddedOrRemoved) {\n return DisableTooltipMessages.add_or_remove;\n }\n if (!isActionAvailable(runType)) {\n return \"This action is not supported yet.\";\n }\n return \"\";\n}\n\n// =============================================================================\n// SUB-COMPONENTS\n// =============================================================================\n\ninterface SingleEnvActionButtonsProps {\n node: NodeViewNodeData;\n actionCallbacks?: NodeViewActionCallbacks;\n runTypeIcons?: RunTypeIconMap;\n isActionAvailable: (runType: string) => boolean;\n rowCountDisplay?: ReactNode;\n}\n\nfunction SingleEnvActionButtons({\n node,\n actionCallbacks,\n runTypeIcons,\n isActionAvailable,\n rowCountDisplay,\n}: SingleEnvActionButtonsProps) {\n const isAddedOrRemoved =\n node.data.changeStatus === \"added\" || node.data.changeStatus === \"removed\";\n\n const QueryIcon = runTypeIcons?.query ?? DefaultIcon;\n const RowCountIcon = runTypeIcons?.row_count ?? DefaultIcon;\n const ProfileIcon = runTypeIcons?.profile ?? DefaultIcon;\n\n return (\n <Stack\n direction=\"row\"\n sx={{\n alignItems: \"center\",\n flexWrap: \"wrap\",\n gap: 1,\n }}\n >\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<QueryIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onQueryClick}\n sx={{ textTransform: \"none\" }}\n >\n Query\n </Button>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<RowCountIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onRowCountClick}\n sx={{ textTransform: \"none\" }}\n >\n Row Count\n {rowCountDisplay != null && <>:&nbsp;{rowCountDisplay}</>}\n </Button>\n <MuiTooltip\n title={getDisableReason(isAddedOrRemoved, \"profile\", isActionAvailable)}\n placement=\"top\"\n >\n <span>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<ProfileIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onProfileClick}\n disabled={isAddedOrRemoved}\n sx={{ textTransform: \"none\" }}\n >\n Profile\n </Button>\n </span>\n </MuiTooltip>\n </Stack>\n );\n}\n\n/**\n * \"Add schema diff to checklist\" button — rendered inside the Columns tab\n * next to the schema legend (not in the header row).\n */\ninterface AddSchemaDiffButtonProps {\n onClick?: () => void;\n Icon: ComponentType<{ fontSize?: string }>;\n}\n\nfunction AddSchemaDiffButton({ onClick, Icon }: AddSchemaDiffButtonProps) {\n return (\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<Icon fontSize=\"small\" />}\n onClick={onClick}\n sx={{ textTransform: \"none\" }}\n >\n Add schema diff to checklist\n </Button>\n );\n}\n\ninterface DiffActionButtonsProps {\n node: NodeViewNodeData;\n actionCallbacks?: NodeViewActionCallbacks;\n runTypeIcons?: RunTypeIconMap;\n featureToggles?: NodeViewProps[\"featureToggles\"];\n isActionAvailable: (runType: string) => boolean;\n ConnectionPopoverWrapper: ComponentType<{\n display: boolean;\n children: ReactNode;\n }>;\n rowCountDisplay?: ReactNode;\n}\n\nfunction DiffActionButtons({\n node,\n actionCallbacks,\n runTypeIcons,\n featureToggles,\n isActionAvailable,\n ConnectionPopoverWrapper,\n rowCountDisplay,\n}: DiffActionButtonsProps) {\n const metadataOnly = featureToggles?.mode === \"metadata only\";\n const isAddedOrRemoved =\n node.data.changeStatus === \"added\" || node.data.changeStatus === \"removed\";\n\n const QueryDiffIcon = runTypeIcons?.query_diff ?? DefaultIcon;\n const RowCountDiffIcon = runTypeIcons?.row_count_diff ?? DefaultIcon;\n const ProfileDiffIcon = runTypeIcons?.profile_diff ?? DefaultIcon;\n const ValueDiffIcon = runTypeIcons?.value_diff ?? DefaultIcon;\n const TopKDiffIcon = runTypeIcons?.top_k_diff ?? DefaultIcon;\n const HistogramDiffIcon = runTypeIcons?.histogram_diff ?? DefaultIcon;\n\n const wrapButton = (\n button: ReactElement<{\n ref?: React.Ref<HTMLElement>;\n [key: string]: unknown;\n }>,\n runType: string,\n ) => {\n if (metadataOnly) {\n return (\n <ConnectionPopoverWrapper display={true}>\n {button}\n </ConnectionPopoverWrapper>\n );\n }\n\n const tooltipContent = getDisableReason(\n isAddedOrRemoved,\n runType,\n isActionAvailable,\n );\n return (\n <MuiTooltip title={tooltipContent} placement=\"top\">\n <span>{button}</span>\n </MuiTooltip>\n );\n };\n\n return (\n <Stack\n direction=\"row\"\n sx={{\n alignItems: \"center\",\n flexWrap: \"wrap\",\n gap: 2,\n }}\n >\n <Typography\n variant=\"caption\"\n sx={{\n fontWeight: \"bold\",\n }}\n >\n Diff\n </Typography>\n <Stack\n direction=\"row\"\n sx={{\n alignItems: \"center\",\n flexWrap: \"wrap\",\n gap: 1,\n width: \"93%\",\n }}\n >\n <ConnectionPopoverWrapper display={metadataOnly}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<RowCountDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onRowCountDiffClick}\n disabled={featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Row Count\n {rowCountDisplay != null && <>:&nbsp;{rowCountDisplay}</>}\n </Button>\n </ConnectionPopoverWrapper>\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<ProfileDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onProfileDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Profile\n </Button>,\n \"profile_diff\",\n )}\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<ValueDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onValueDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Value\n </Button>,\n \"value_diff\",\n )}\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<TopKDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onTopKDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Top-K\n </Button>,\n \"top_k_diff\",\n )}\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<HistogramDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onHistogramDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Histogram\n </Button>,\n \"histogram_diff\",\n )}\n <ConnectionPopoverWrapper display={metadataOnly}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<QueryDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onQueryDiffClick}\n disabled={featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Query\n </Button>\n </ConnectionPopoverWrapper>\n </Stack>\n </Stack>\n );\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * NodeView Component\n *\n * Displays detailed information about a lineage node including:\n * - Node name and metadata\n * - Action buttons for various operations (Query, Profile, Diff, etc.)\n * - Tabs for Columns and Code views\n *\n * Uses dependency injection for:\n * - Schema view components (different for OSS vs Cloud)\n * - Action button handlers\n * - Icon components from run registry\n * - Connection popover for database setup prompts\n *\n * @example\n * ```tsx\n * import { NodeView } from '@datarecce/ui/advanced';\n *\n * <NodeView\n * node={selectedNode}\n * onCloseNode={() => setSelectedNode(null)}\n * isSingleEnv={false}\n * SchemaView={MySchemaView}\n * NodeSqlView={MyNodeSqlView}\n * actionCallbacks={{\n * onQueryClick: handleQuery,\n * onProfileDiffClick: handleProfileDiff,\n * }}\n * />\n * ```\n */\nexport function NodeView<TNode extends NodeViewNodeData>({\n node,\n onCloseNode,\n isSingleEnv,\n featureToggles,\n modelDetail,\n lineageTabContent,\n // Injected components\n SchemaView,\n SingleEnvSchemaView,\n NodeSqlView,\n ResourceTypeTag,\n NotificationComponent,\n ConnectionPopoverWrapper = DefaultConnectionWrapper,\n // Injected icons\n runTypeIcons,\n // Injected callbacks\n actionCallbacks,\n isActionAvailable = defaultIsActionAvailable,\n isWholeModelChanged = false,\n isWholeModelImpacted = false,\n wholeModelImpact = false,\n isImpacted = false,\n rowCountDisplay,\n}: NodeViewProps<TNode>) {\n const withColumns =\n node.data.resourceType === \"model\" ||\n node.data.resourceType === \"seed\" ||\n node.data.resourceType === \"source\" ||\n node.data.resourceType === \"snapshot\";\n\n const [isNotificationOpen, setIsNotificationOpen] = useState(true);\n const [tabValue, setTabValue] = useState(0);\n\n const { base, current } = modelDetail ?? {};\n const hasSchemaChanges =\n !isSingleEnv &&\n node.data.change?.columns != null &&\n Object.keys(node.data.change.columns).length > 0;\n // DRC-3263: uses server-computed changeStatus instead of comparing raw_code\n // strings. This is intentionally broader — state:modified includes config and\n // description changes, not just code. Verified equivalent on jaffle-shop-expand\n // (1060 nodes). If a project has config-only changes, the dot may appear even\n // though raw_code is identical — acceptable since the node IS modified.\n const hasCodeChanges = !isSingleEnv && node.data.changeStatus === \"modified\";\n\n const isModelSeedOrSnapshot =\n node.data.resourceType === \"model\" ||\n node.data.resourceType === \"seed\" ||\n node.data.resourceType === \"snapshot\";\n\n const showAddSchemaDiff =\n !isSingleEnv &&\n isModelSeedOrSnapshot &&\n actionCallbacks?.onAddSchemaDiffClick != null;\n const SchemaDiffIcon = runTypeIcons?.schema_diff ?? DefaultIcon;\n\n // useTheme().palette.mode === \"dark\" does NOT work with this codebase's\n // MUI colorSchemes setup — useThemeColors() is the correct accessor.\n const { isDark } = useThemeColors();\n const treatmentInputs = {\n wholeModelImpact,\n isWholeModelChanged,\n isWholeModelImpacted,\n isImpacted,\n changeCategory: node.data.change?.category as ChangeCategory | undefined,\n };\n // pickTitleChip returns null for per-column kinds — those paint on the\n // LineageNode graph badge instead.\n const titleChip = pickTitleChip(treatmentInputs, isDark);\n const titleRowTooltip = getTitleRowTooltip(\n {\n name: node.data.name,\n resourceType: node.data.resourceType,\n materialized: node.data.materialized,\n },\n treatmentInputs,\n );\n\n return (\n <Box\n className={titleChip ? \"cll-experience\" : undefined}\n sx={{\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n // Reserve the 3px stripe even when no treatment applies, so the\n // content doesn't shift horizontally when navigating between models\n // with and without whole-model treatment.\n borderLeft: `3px solid ${titleChip ? titleChip.tokens.stripeAccent : \"transparent\"}`,\n }}\n >\n {/* Header row: name, type tag, close button */}\n <Stack\n direction=\"row\"\n sx={{\n alignItems: \"center\",\n px: 2,\n py: 1.5,\n gap: 1,\n }}\n >\n <MuiTooltip title={titleRowTooltip} placement=\"top\">\n {titleChip ? (\n <TreatmentChip\n tokens={titleChip.tokens}\n variant=\"titleChip\"\n testId={`whole-model-${titleChip.kind}-title-chip`}\n sx={{\n flex: \"0 1 auto\",\n mr: \"auto\",\n minWidth: 0,\n }}\n >\n <Typography\n variant=\"subtitle1\"\n component=\"span\"\n className=\"no-track-pii-safe\"\n sx={{\n fontWeight: 600,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n color: \"inherit\",\n }}\n >\n {node.data.name}\n </Typography>\n </TreatmentChip>\n ) : (\n <Typography\n component=\"span\"\n variant=\"subtitle1\"\n className=\"no-track-pii-safe\"\n sx={{\n fontWeight: 600,\n flex: \"0 1 auto\",\n mr: \"auto\",\n minWidth: 0,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {node.data.name}\n </Typography>\n )}\n </MuiTooltip>\n {ResourceTypeTag && (\n <Box sx={{ color: \"text.secondary\", flexShrink: 0 }}>\n <ResourceTypeTag node={node} />\n </Box>\n )}\n <IconButton size=\"small\" onClick={onCloseNode} sx={{ flexShrink: 0 }}>\n <IoClose />\n </IconButton>\n </Stack>\n {/* Action buttons row */}\n {isModelSeedOrSnapshot && (\n <Box sx={{ pl: 2, py: 1 }}>\n {isSingleEnv ? (\n <SingleEnvActionButtons\n node={node}\n actionCallbacks={actionCallbacks}\n runTypeIcons={runTypeIcons}\n isActionAvailable={isActionAvailable}\n rowCountDisplay={rowCountDisplay}\n />\n ) : (\n <DiffActionButtons\n node={node}\n actionCallbacks={actionCallbacks}\n runTypeIcons={runTypeIcons}\n featureToggles={featureToggles}\n isActionAvailable={isActionAvailable}\n ConnectionPopoverWrapper={ConnectionPopoverWrapper}\n rowCountDisplay={rowCountDisplay}\n />\n )}\n </Box>\n )}\n {/* Content area: tabs for columns and code */}\n {withColumns && (\n <Box\n sx={{\n overflow: \"auto\",\n display: \"flex\",\n flexDirection: \"column\",\n flex: 1,\n minHeight: 0,\n }}\n >\n {/* Notification for single env mode */}\n {isSingleEnv && isNotificationOpen && NotificationComponent && (\n <Box sx={{ p: 1.5 }}>\n <NotificationComponent\n onClose={() => setIsNotificationOpen(false)}\n >\n <Typography variant=\"body2\">\n Enable the Recce Checklist and start adding checks for better\n data validation and review.\n </Typography>\n </NotificationComponent>\n </Box>\n )}\n\n {/* Tabs — \"Columns\" is always index 0 (default landing tab) */}\n <Tabs\n value={tabValue}\n onChange={(_, newValue) => setTabValue(newValue)}\n sx={{ borderBottom: 1, borderColor: \"divider\" }}\n >\n <Tab\n label={\n <Box\n component=\"span\"\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 0.75,\n }}\n >\n Columns\n {hasSchemaChanges && (\n <Box\n component=\"span\"\n sx={{\n color: \"amber.main\",\n fontSize: \"0.5rem\",\n lineHeight: 1,\n }}\n >\n ●\n </Box>\n )}\n </Box>\n }\n />\n <Tab\n label={\n <Box\n component=\"span\"\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 0.75,\n }}\n >\n Code\n {hasCodeChanges && (\n <Box\n component=\"span\"\n sx={{\n color: \"amber.main\",\n fontSize: \"0.5rem\",\n lineHeight: 1,\n }}\n >\n ●\n </Box>\n )}\n </Box>\n }\n />\n {lineageTabContent && <Tab label=\"Lineage\" />}\n </Tabs>\n\n {/* Tab panels — Columns=0, Code=1, Lineage=2 (when present) */}\n <Box sx={{ overflow: \"auto\", height: \"calc(100% - 48px)\" }}>\n <TabPanel value={tabValue} index={0}>\n <Box sx={{ overflowY: \"auto\", height: \"100%\" }}>\n {isSingleEnv\n ? SingleEnvSchemaView && (\n <SingleEnvSchemaView current={current} />\n )\n : SchemaView && (\n <SchemaView\n base={base}\n current={current}\n columnChanges={node.data.change?.columns}\n onViewCode={() => setTabValue(1)}\n headerAction={\n showAddSchemaDiff ? (\n <AddSchemaDiffButton\n onClick={actionCallbacks?.onAddSchemaDiffClick}\n Icon={SchemaDiffIcon}\n />\n ) : undefined\n }\n />\n )}\n </Box>\n </TabPanel>\n <TabPanel value={tabValue} index={1}>\n <Box sx={{ height: \"100%\" }}>\n {NodeSqlView && <NodeSqlView node={node} />}\n </Box>\n </TabPanel>\n {lineageTabContent && (\n <TabPanel value={tabValue} index={2}>\n <Box sx={{ height: \"100%\" }}>{lineageTabContent}</Box>\n </TabPanel>\n )}\n </Box>\n </Box>\n )}\n </Box>\n );\n}\n","// @datarecce/ui/advanced - Lower-level components for custom composition\n// These exports may change between minor versions\n\n\"use client\";\n\n/**\n * Version marker for the advanced surface.\n */\nexport const ADVANCED_API_VERSION = \"0.2.0\";\n\n// =============================================================================\n// LINEAGE UTILITIES\n// =============================================================================\n// NOTE: Lineage types canonical in @datarecce/ui/types\n// NOTE: Lineage utilities canonical in @datarecce/ui/contexts\n\n/**\n * Lineage graph types for advanced consumers.\n * @deprecated Import from @datarecce/ui/types instead\n */\nexport type {\n EnvInfo,\n LineageGraph,\n LineageGraphColumnNode,\n LineageGraphEdge,\n LineageGraphNode,\n LineageGraphNodes,\n} from \"./contexts/lineage/types\";\n/**\n * Lineage graph type guards.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n isLineageGraphColumnNode,\n isLineageGraphNode,\n} from \"./contexts/lineage/types\";\n/**\n * Graph building and layout utilities.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n buildLineageGraph,\n COLUMN_HEIGHT,\n getNeighborSet,\n intersect,\n layoutWithDagre,\n type NodeColumnSetMap,\n selectDownstream,\n selectUpstream,\n toReactFlowBasic,\n union,\n} from \"./contexts/lineage/utils\";\n\n// =============================================================================\n// LINEAGE CANVAS\n// =============================================================================\n\n/**\n * Determine whether a model node is \"impacted\" for the new CLL experience.\n *\n * A node is impacted if ANY of:\n * 1. CLL analysis marks the node as impacted (node.impacted is truthy)\n * 2. Any column belonging to this node has a non-null change_status\n * 3. The model itself has a non-null changeStatus (added/removed/modified)\n */\nexport { type ColumnAnnotation } from \"./components/lineage/computeColumnLineage\";\nexport { computeImpactedColumns } from \"./components/lineage/computeImpactedColumns\";\nexport { computeIsImpacted } from \"./components/lineage/computeIsImpacted\";\n/**\n * Zoom and display constants for the lineage view.\n *\n * @remarks\n * Use with LineageCanvas fitViewOptions to match production behavior.\n */\nexport {\n DIM_FILTER,\n EXPLORE_MIN_ZOOM,\n FIT_VIEW_PADDING,\n LEGIBLE_MIN_ZOOM,\n} from \"./components/lineage/config/zoomConstants\";\n/**\n * Low-level lineage canvas component for custom graph rendering.\n *\n * @remarks\n * Exports: LineageCanvas, LineageCanvasProps.\n */\nexport {\n LineageCanvas,\n type LineageCanvasProps,\n} from \"./components/lineage/LineageCanvas\";\n/**\n * Lineage tab body — direct upstream/downstream + breadcrumb path —\n * intended to be injected into NodeView's `lineageTabContent` slot.\n */\nexport {\n LineageTabContent,\n type LineageTabContentProps,\n} from \"./components/lineage/LineageTabContent\";\n/**\n * OSS-specific toReactFlow with Column-Level Lineage (CLL) support.\n *\n * Converts a LineageGraph to React Flow nodes and edges, including\n * column nodes when CLL data is provided.\n *\n * @example\n * ```tsx\n * import { toReactFlow } from '@datarecce/ui/advanced';\n *\n * const [nodes, edges, columnSetMap] = toReactFlow(lineageGraph, {\n * cll: columnLineageData,\n * });\n * ```\n */\nexport { layout, toReactFlow } from \"./components/lineage/lineage\";\n/**\n * Row-count tags rendered inside `NodeView`. They read row-count data from\n * `LineageGraphProvider`'s `runsAggregated` prop, keyed by `node.id`.\n *\n * `RowCountSummary` is an inline-content variant (no pill background, no\n * refresh button) that accepts either a single-env `RowCount` or a\n * `RowCountDiff`. Intended for embedding in buttons or other tight layouts\n * via `NodeView`'s `rowCountDisplay` slot.\n */\nexport {\n RowCountDiffTag,\n RowCountSummary,\n RowCountTag,\n} from \"./components/lineage/NodeTag\";\n/**\n * Node detail panel with Columns and Code tabs.\n */\nexport {\n NodeView,\n type NodeViewActionCallbacks,\n type NodeViewNodeData,\n type NodeViewProps,\n type RunTypeIconMap,\n type SchemaViewProps,\n} from \"./components/lineage/NodeView\";\n\n// =============================================================================\n// CONTEXT HOOKS\n// =============================================================================\n// NOTE: Context hooks canonical in @datarecce/ui/contexts\n// NOTE: Context types canonical in @datarecce/ui/types\n\n/**\n * Recce action context hooks and types.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n type RecceActionContextType,\n useRecceActionContext,\n} from \"./contexts/action\";\n/**\n * Idle timeout context hooks and types.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n type IdleTimeoutContextType,\n useIdleTimeout,\n} from \"./contexts/idle\";\n/**\n * Recce instance context hooks and types.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n type InstanceInfoType,\n useRecceInstanceContext,\n useRecceInstanceInfo,\n} from \"./contexts/instance\";\n/**\n * Lineage context hooks for direct access.\n * @deprecated Import from @datarecce/ui/contexts instead\n */\nexport {\n useLineageGraphContext,\n useRunsAggregated,\n} from \"./contexts/lineage\";\n\n// =============================================================================\n// THEME UTILITIES\n// =============================================================================\n\n/**\n * Theme color hook for advanced consumers.\n *\n * @remarks\n * Exports: useThemeColors.\n */\nexport { useThemeColors } from \"./hooks/useThemeColors\";\n\n/**\n * Theme color palette exports.\n * @deprecated Import from @datarecce/ui/theme instead\n * @deprecated Types (ColorShade, SemanticColorVariant) canonical in @datarecce/ui/types\n */\nexport {\n type ColorShade,\n colors,\n type SemanticColorVariant,\n} from \"./theme/colors\";\n"],"mappings":";0lCAUA,SAAgB,GAAuB,EAAqC,CAC1E,GAAM,CAAE,UAAS,cAAe,EAAI,QAE9B,EAAO,IAAI,IAEjB,SAAS,EAAW,EAA2B,CAC7C,IAAM,EAAS,EAAK,IAAI,CAAQ,EAChC,GAAI,IAAW,UAAW,MAAO,GACjC,GAAI,IAAW,IAAA,GAAW,OAAO,EAMjC,GAJA,EAAK,IAAI,EAAU,SAAS,EAGhB,EAAQ,EACb,EAAE,cAEP,OADA,EAAK,IAAI,EAAU,EAAI,EAChB,GAIT,IAAM,EAAU,EAAW,IAAa,CAAC,EACzC,IAAK,IAAM,KAAU,EACnB,GAAI,EAAW,CAAM,EAEnB,OADA,EAAK,IAAI,EAAU,EAAI,EAChB,GAKX,OADA,EAAK,IAAI,EAAU,EAAK,EACjB,EACT,CAEA,IAAM,EAAW,IAAI,IACrB,IAAK,IAAM,KAAY,OAAO,KAAK,CAAO,EACpC,EAAW,CAAQ,GACrB,EAAS,IAAI,CAAQ,EAIzB,OAAO,CACT,CCrCA,SAAgB,GACd,EACA,EACA,EACA,EACS,CAET,GAAI,EAAc,MAAO,GACzB,GAAI,CAAC,EAAK,MAAO,GAIjB,GADgB,EAAI,QAAQ,MAAM,EACvB,EAAE,SAAU,MAAO,GAG9B,IAAM,EAAO,EACP,EAAS,GAAG,EAAO,GACzB,IAAK,IAAM,KAAS,EAClB,GAAI,EAAM,WAAW,CAAM,EAAG,MAAO,GAGvC,MAAO,EACT,CCSA,MAAM,GAAa,CACjB,MAAO,yBACP,KAAM,wBACR,EAKA,SAAS,EAAkB,EAAyB,CAClD,OAAO,EAAS,EAAmB,KAAO,EAAmB,KAC/D,CAgDA,SAAS,GAAgB,EAAkD,CACzE,OAAO,GAAM,KAAK,cAAgB,WACpC,CAGA,SAAS,GACP,EACA,EACiB,CACjB,OAAO,IAAW,aAAe,EAAc,WAAa,CAC9D,CAEA,SAAS,GAAkB,EAAiC,CAC1D,OAAO,IAAW,WACd,EAAsB,SACtB,EAAmB,EACzB,CAEA,SAAS,EACP,EACA,EACQ,CACR,OAAO,IAAY,EAAG,EAAE,KAAK,MAAQ,CACvC,CAGA,SAAS,GACP,EACA,EACA,EACU,CACV,IAAM,EAAI,EAAM,KAAK,CAAC,CAAC,YAAY,EAEnC,OADK,EACE,EAAI,OAAQ,GACjB,EAAe,EAAI,CAAS,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,CACxD,EAHe,CAIjB,CAEA,SAAS,GAAa,EAAqB,EAAuB,CAEhE,OADI,EAAmB,kBAChB,EAAO,6BAA+B,2BAC/C,CAMA,SAAS,GAAU,CACjB,SACA,eAKC,CACD,IAAM,EAAY,GAAgB,EAAQ,CAAW,EACrD,OACE,EAAC,EAAD,CACE,UAAU,OACV,cAAY,qBACZ,cAAa,EACb,GAAI,CACF,MAAO,MACP,OAAQ,MACR,aAAc,MACd,gBAAiB,GAAkB,CAAS,EAC5C,KAAM,UACR,CACD,CAAA,CAEL,CAcA,SAAS,GAAU,CACjB,OACA,SACA,YACA,qBACA,cACA,WACiB,CACjB,GAAM,CAAE,UAAW,EAAe,EAC5B,EACJ,IAAc,KAAO,qBAAuB,yBAC9C,OACE,EAAC,EAAD,CACW,UACT,GAAI,CACF,SAAU,WACV,QAAS,OACT,WAAY,SACZ,IAAK,IACL,GAAI,IACJ,GAAI,KACJ,GAAI,GACJ,OAAQ,EAAU,UAAY,UAC9B,SAAU,OACV,WAAY,2CACZ,WAAY,IACZ,MAAO,eACP,SAAU,EACV,gBAAiB,EACb,EACE,GAAW,KACX,GAAW,MACb,IAAA,GACJ,UAAW,EAAU,CAAE,gBAAiB,cAAe,EAAI,IAAA,EAC7D,WAtBF,CAwBG,GACC,EAAC,EAAD,CACE,cAAY,OACZ,GAAI,CACF,SAAU,WACV,KAAM,EACN,IAAK,EACL,OAAQ,EACR,MAAO,MACP,gBAAiB,EAAsB,QACzC,CACD,CAAA,EAEH,EAAC,GAAD,CAAmB,SAAQ,YAAa,CAAc,CAAA,EACtD,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CACF,SAAU,SACV,aAAc,WACd,WAAY,SACZ,SAAU,EACV,KAAM,CACR,EACA,MAAO,WAEN,CACE,CAAA,EACJ,GACC,EAAC,EAAD,CAAS,MAAO,EAAS,UAAU,MAAM,MAAA,YACvC,EAAC,EAAD,CACE,UAAU,OACV,aAAY,EACZ,cAAY,sBACZ,GAAI,CACF,QAAS,cACT,WAAY,SACZ,eAAgB,SAChB,MAAO,OACP,OAAQ,OACR,MAAO,EAAkB,CAAM,EAC/B,KAAM,UACR,WAGA,EAAC,EAAD,CAAiB,KAAM,EAAK,CAAA,CACzB,CAAA,CACE,CAAA,CAER,GAET,CAWA,SAAS,GAAe,CAAE,SAAQ,UAAoC,CAGpE,OADI,EAAe,EAAkB,KAC9B,EAAS,EAAmB,KAAO,EAAmB,KAC/D,CAEA,SAAS,GAAe,CAAE,SAAQ,UAAoC,CAEpE,OADI,EAAe,OACZ,EAAS,EAAmB,KAAO,EAAmB,KAC/D,CAEA,MAAM,GAAe,CACnB,QAAS,cACT,WAAY,SACZ,IAAK,MACL,GAAI,IACJ,GAAI,MACJ,GAAI,MACJ,aAAc,QACd,SAAU,OACV,WAAY,IACZ,cAAe,QACjB,EAEA,SAAS,GAAQ,CAAE,SAA4B,CAC7C,OACE,EAAC,EAAD,CACE,UAAU,OACV,cAAY,OACZ,GAAI,CACF,MAAO,MACP,OAAQ,MACR,aAAc,MACd,gBAAiB,CACnB,CACD,CAAA,CAEL,CAEA,SAAS,GAAW,CAClB,QACA,QACA,SACA,SACA,WAOC,CACD,IAAM,EAAU,CAAE,SAAQ,QAAO,EACjC,OACE,EAAC,EAAD,CAAgB,QAAO,UAAU,MAAM,MAAA,YACrC,EAAC,EAAD,CACE,UAAU,SACV,KAAK,SACL,cAAY,sBACZ,eAAc,EACd,aAAY,EACH,UACT,GAAI,CACF,GAAG,GACH,gBAAiB,GAAe,CAAO,EACvC,MAAO,GAAe,CAAO,EAC7B,OAAQ,OACR,KAAM,UACN,OAAQ,UACR,UAAW,CACT,OAAQ,EAAS,mBAAqB,kBACxC,CACF,WAjBF,CAmBE,EAAC,GAAD,CAAS,MAAO,GAAe,CAAO,CAAI,CAAA,EACzC,CACE,GACE,CAAA,CAEb,CAMA,SAAS,GAAc,CACrB,YACA,cACA,cACA,aACA,sBAUC,CACD,IAAM,EAAO,IAAc,KACrB,EAAY,EAAO,GAAgB,EACnC,CAAE,SAAQ,cAAe,EAAe,EACxC,EAAQ,GAAe,EACvB,EAAW,EAAQ,EACnB,EAAQ,EAAO,GAAG,EAAM,YAAc,GAAG,EAAM,WAErD,OACE,EAAC,EAAD,CACE,UAAU,MACV,QAAS,IACT,GAAI,CACF,WAAY,SACZ,GAAI,IACJ,GAAI,KACJ,gBAAiB,EAAW,OAC5B,UAAW,YACX,aAAc,YACd,YAAa,UACb,SAAU,OACV,MAAO,iBACP,WAAY,GACd,WAdF,CAgBE,EAAC,EAAD,CAAW,KAAM,EAAK,CAAA,EACtB,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CAAE,cAAe,YAAa,cAAe,QAAS,WAEzD,EAAO,WAAa,YAClB,CAAA,EACL,EAAC,EAAD,CAAK,UAAU,OAAO,GAAI,CAAE,MAAO,gBAAiB,WAAY,GAAI,WAApE,CAAuE,KAClE,EAAY,SACZ,IACJ,GACC,EAAC,GAAD,CACS,QACP,MAAO,GAAa,EAAY,CAAI,EACpC,OAAQ,EACA,SACR,QAAS,CACV,CAAA,CAEE,GAEX,CAEA,SAAS,GAAS,CAAE,SAA4B,CAC9C,OACE,EAAC,EAAD,CACE,GAAI,CACF,GAAI,IACJ,GAAI,EACJ,SAAU,OACV,MAAO,gBACP,UAAW,QACb,WAEC,CACE,CAAA,CAET,CAQA,SAAS,GAAY,CAAE,YAAW,QAAO,YAA8B,CAErE,OACE,EAAC,EAAD,CACE,GAAI,CACF,GAAI,KACJ,GAAI,IACJ,aAAc,YACd,YAAa,UACb,gBAAiB,kBACnB,WAEA,EAAC,EAAD,CACE,UAAU,MACV,QAAS,GACT,GAAI,CACF,WAAY,SACZ,OAAQ,YACR,YAAa,UACb,aAAc,MACd,GAAI,KACJ,GAAI,GACN,WAVF,CAYE,EAAC,GAAD,CAAU,KAAM,GAAI,MAAM,eAAe,QAAS,GAAO,CAAA,EACzD,EAAC,GAAD,CACE,MAAO,EACP,SAAW,GAAM,CACf,EAAS,EAAE,OAAO,KAAK,CACzB,EACA,YAAY,UACZ,WAAY,CACV,aAAc,UA/BV,IAAc,KAAO,WAAa,eAgCtC,MAAO,CACL,QAAS,EACT,SAAU,OACV,WAAY,0CACd,CACF,EACA,GAAI,CAAE,KAAM,EAAG,SAAU,MAAO,CACjC,CAAA,CACI,GACJ,CAAA,CAET,CAOA,SAAS,GAAY,CAAE,SAAQ,WAA6B,CAC1D,OACE,EAAC,EAAD,CACW,UACT,KAAK,SACL,SAAU,EACV,UAAY,GAAM,EACZ,EAAE,MAAQ,SAAW,EAAE,MAAQ,OACjC,EAAE,eAAe,EACjB,EAAQ,EAEZ,EACA,GAAI,CACF,GAAI,IACJ,GAAI,IACJ,SAAU,OACV,MAAO,YACP,OAAQ,UACR,WAAY,OACZ,UAAW,CAAE,gBAAiB,cAAe,CAC/C,WAlBF,CAmBC,UACS,KAAK,IAAI,GAAW,CAAM,EAAE,QAAM,IAC1C,EAAC,EAAD,CAAK,UAAU,OAAO,GAAI,CAAE,MAAO,gBAAiB,GAAI,EAAI,WAA5D,CAA+D,IAC3D,EAAO,UACN,GACF,GAET,CAEA,SAAS,GAAU,CACjB,OACA,gBACA,eAMC,CAED,IAAM,EAAY,GADH,GAAgB,CACQ,EAAG,CAAW,EAC/C,CAAE,SAAQ,cAAe,EAAe,EACxC,EAAc,GAAkB,CAAS,EAGzC,EACJ,IAAc,WAAa,EAAkB,CAAM,EAAI,EACnD,EACJ,IAAc,WAAa,EAAkB,CAAM,EAAI,UACzD,OACE,EAAC,EAAD,CACE,UAAU,MACV,QAAS,EACT,GAAI,CACF,WAAY,SACZ,GAAI,IACJ,GAAI,KACJ,gBAAiB,EAAS,EAAW,WAAa,mBAClD,UAAW,YACX,aAAc,YACd,YAAa,SACf,WAXF,CAaE,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CACF,MAAO,MACP,OAAQ,OACR,aAAc,MACd,gBAAiB,EACjB,KAAM,UACR,CACD,CAAA,EACD,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CACF,KAAM,EACN,SAAU,EACV,WAAY,2CACZ,SAAU,OACV,WAAY,IACZ,SAAU,SACV,aAAc,WACd,WAAY,QACd,EACA,MAAO,EAAK,KAAK,cAEhB,EAAK,KAAK,IACR,CAAA,EACJ,GACC,EAAC,EAAD,CAAS,MAAM,mBAAmB,UAAU,eAC1C,EAAC,EAAD,CACE,KAAK,QACL,aAAW,mBACX,QAAS,EACT,GAAI,CAAE,EAAG,GAAK,MAAO,iBAAkB,KAAM,UAAW,WAExD,EAAC,GAAD,CAAY,KAAM,EAAK,CAAA,CACb,CAAA,CACL,CAAA,EAEX,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CACF,SAAU,OACV,WAAY,IACZ,GAAI,KACJ,GAAI,MACJ,aAAc,MACd,MAAO,EACP,gBAAiB,mBACjB,OAAQ,YACR,YAAa,EACb,cAAe,aACf,KAAM,UACR,WAEC,CACE,CAAA,CACA,GAEX,CAWA,SAAS,GAAe,CACtB,eACA,cACA,cACsB,CACtB,GAAM,CAAE,UAAW,EAAe,EAClC,OACE,EAAC,EAAD,CACE,UAAU,MACV,QAAS,IACT,GAAI,CACF,WAAY,SACZ,SAAU,EACV,KAAM,EACN,SAAU,SACV,WAAY,2CACZ,SAAU,MACZ,WAVF,CAYE,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CACF,MAAO,iBACP,WAAY,UACZ,KAAM,UACR,WACD,MAEI,CAAA,EACL,EAAC,EAAD,CACE,UAAU,OACV,KAAM,EAAa,SAAW,IAAA,GAC9B,SAAU,EAAa,EAAI,IAAA,GAC3B,QAAS,EACT,UACE,EACK,GAAM,EACD,EAAE,MAAQ,SAAW,EAAE,MAAQ,OACjC,EAAE,eAAe,EACjB,EAAW,EAEf,EACA,IAAA,GAEN,GAAI,CACF,MAAO,eACP,OAAQ,EAAa,UAAY,UACjC,eAAgB,YAChB,oBAAqB,EACjB,0BACA,oBACJ,oBAAqB,MACrB,SAAU,SACV,aAAc,WACd,WAAY,SACZ,SAAU,EACV,UAAW,EACP,CAAE,oBAAqB,cAAe,EACtC,IAAA,EACN,EACA,MAAO,WAEN,CACE,CAAA,EACL,EAAC,EAAD,CACE,UAAU,OACV,cAAY,OACZ,GAAI,CAAE,MAAO,gBAAiB,KAAM,UAAW,WAChD,GAEI,CAAA,EACL,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CACF,MAAO,eACP,WAAY,IACZ,SAAU,SACV,aAAc,WACd,WAAY,SACZ,SAAU,CACZ,EACA,MAAO,WAEN,CACE,CAAA,CACA,GAEX,CAMA,SAAgB,GAAkB,CAChC,OACA,YACA,aACA,SACA,gBACA,eACA,kBACA,mBACA,mBACyB,CACzB,GAAM,CAAE,cAAe,EAAe,EAGhC,CAAC,EAAS,GAAc,EAAS,EAAE,EACnC,CAAC,EAAW,GAAgB,EAAS,EAAE,EACvC,CAAC,EAAW,GAAgB,EAAS,CAAiB,EACtD,CAAC,EAAa,GAAkB,EAAS,CAAiB,EAE1D,CAAC,EAAc,GAAmB,EAAS,EAAK,EAChD,CAAC,EAAgB,GAAqB,EAAS,EAAK,EAK1D,MAAgB,CACd,EAAW,EAAE,EACb,EAAa,EAAE,EACf,EAAa,CAAiB,EAC9B,EAAe,CAAiB,EAChC,EAAgB,EAAK,EACrB,EAAkB,EAAK,CACzB,EAAG,CAAC,EAAK,EAAE,CAAC,EAEZ,IAAM,EAAY,MACV,OAAO,KAAK,EAAK,KAAK,SAAW,CAAC,CAAC,EACzC,CAAC,EAAK,KAAK,OAAO,CACpB,EACM,EAAW,MACT,OAAO,KAAK,EAAK,KAAK,UAAY,CAAC,CAAC,EAC1C,CAAC,EAAK,KAAK,QAAQ,CACrB,EAEM,EAAoB,MAClB,GAAU,EAAW,EAAS,CAAS,EAC7C,CAAC,EAAW,EAAS,CAAS,CAChC,EACM,EAAmB,MACjB,GAAU,EAAU,EAAW,CAAS,EAC9C,CAAC,EAAU,EAAW,CAAS,CACjC,EAMM,IADH,GAAkB,MAAQ,GAAK,IAAM,GAAiB,MAAQ,GAAK,MAGlE,GAAkB,IAAI,EAAK,EAAE,GAAK,MACjC,GAAiB,IAAI,EAAK,EAAE,GAAK,KAEhC,GAAsB,EAAoB,IACzC,GACO,IAAQ,KAAO,EAAmB,EAAA,EAClC,IAAI,CAAU,GAAK,GAFJ,GAKvB,EAAgB,EAClB,EAAU,OAAQ,GAAO,GAAkB,IAAI,CAAE,CAAC,CAAC,CAAC,OACpD,EACE,EAAkB,EACpB,EAAS,OAAQ,GAAO,GAAiB,IAAI,CAAE,CAAC,CAAC,CAAC,OAClD,EAEE,EAAmB,GAAyB,CAChD,IAAM,EAAO,IAAc,KACrB,EAAY,EAAO,EAAY,EAC/B,EAAe,EAAO,EAAoB,EAE1C,GADa,EAAO,EAAe,GAErC,EAAa,OAAQ,GAAO,EAAmB,EAAI,CAAS,CAAC,EAC7D,EACE,EAAQ,EAAO,EAAU,EACzB,EAAW,EAAO,EAAa,EAC/B,EAAU,EAAO,EAAY,EAC7B,EAAa,EAAO,EAAe,EACnC,EAAa,EAAU,OAAS,EAChC,EAAa,EAAS,MAAM,EAAG,CAAO,EACtC,EAAS,EAAS,OAAS,EAAW,OACtC,EAAa,EACf,yBACA,yBAEJ,OACE,EAAA,EAAA,CAAA,SAAA,CACG,GACC,EAAC,GAAD,CACa,YACJ,QACP,SAAW,GAAS,CAClB,EAAS,CAAI,EACb,EAAW,CAAiB,CAC9B,CACD,CAAA,EAEF,EAAU,SAAW,EACpB,EAAC,GAAD,CAAU,MAAO,CAAa,CAAA,EAC5B,EAAS,SAAW,EACtB,EAAC,GAAD,CAAU,MAAM,YAAc,CAAA,EAE9B,EAAW,IAAK,GACd,EAAC,GAAD,CAEE,KAAM,EAAe,EAAI,CAAS,EAClC,OAAQ,GAAgB,IAAY,EAAG,EAC5B,YACX,mBAAoB,EAAmB,EAAI,CAAS,EACpD,YAAa,GAAiB,IAAI,CAAE,GAAK,GACzC,QAAS,MAAmB,EAAW,CAAE,EAAI,IAAA,EAC9C,EAPM,GAAG,EAAU,GAAG,GAOtB,CACF,EAEF,EAAS,GACR,EAAC,GAAD,CACU,SACR,YACE,EAAY,GAAM,KAAK,IAAI,EAAS,OAAQ,EAAI,EAAS,CAAC,CAE7D,CAAA,CAEH,CAAA,CAAA,CAEN,EAEM,GAAY,GAAc,QAAU,GAAK,EAG/C,OACE,EAAC,EAAD,CACE,GAAI,CACF,OAAQ,OACR,QAAS,OACT,cAAe,SACf,gBAAiB,kBACnB,EACA,cAAY,+BAPd,EAHkB,CAAC,CAAC,GAAU,IAc1B,EAAC,EAAD,CACE,UAAU,MACV,QAAS,EACT,GAAI,CACF,WAAY,SACZ,GAAI,KACJ,GAAI,IACJ,aAAc,YACd,YAAa,UACb,gBAAiB,EAAW,OAC5B,SAAU,CACZ,WAXF,CAaG,GACC,EAAC,EAAD,CAAS,MAAM,wBAAwB,UAAU,eAC/C,EAAC,EAAD,CACE,KAAK,QACL,aAAW,wBACX,QAAS,EACT,GAAI,CACF,KAAM,WACN,OAAQ,YACR,YAAa,UACb,aAAc,MACd,GAAI,EACJ,GAAI,KACJ,MAAO,iBACP,gBAAiB,mBACjB,UAAW,CAAE,gBAAiB,cAAe,CAC/C,WAEA,EAAC,GAAD,CAAa,KAAM,EAAK,CAAA,CACd,CAAA,CACL,CAAA,EAEV,GAAY,EACX,EAAC,GAAD,CACE,aAAc,EACZ,EAAa,EAAa,OAAS,GACnC,CACF,EACA,YAAa,EAAK,KAAK,KACvB,WACE,MACU,EAAgB,EAAa,OAAS,CAAC,EAC7C,CAEP,CAAA,EAED,EAAC,EAAD,CAAK,GAAI,CAAE,KAAM,CAAE,CAAI,CAAA,CAEpB,IAIT,EAAC,EAAD,CAAK,GAAI,CAAE,KAAM,EAAG,UAAW,OAAQ,UAAW,CAAE,WAApD,CACE,EAAC,GAAD,CACE,UAAU,KACV,YAAa,EAAU,OACvB,YAAa,EACb,WAAY,EACZ,uBAA0B,CACxB,EAAiB,GAAM,CAAC,CAAC,EACzB,EAAa,CAAiB,CAChC,CACD,CAAA,EACA,EAAgB,IAAI,EAErB,EAAC,GAAD,CACQ,OACS,gBACf,YAAa,GAAiB,IAAI,EAAK,EAAE,GAAK,EAC/C,CAAA,EAED,EAAC,GAAD,CACE,UAAU,OACV,YAAa,EAAS,OACtB,YAAa,EACb,WAAY,EACZ,uBAA0B,CACxB,EAAmB,GAAM,CAAC,CAAC,EAC3B,EAAe,CAAiB,CAClC,CACD,CAAA,EACA,EAAgB,MAAM,CACpB,GACF,GAET,CC75BA,SAAgB,GACd,EACA,EAO6D,CAC7D,IAAM,EAA6B,CAAC,EAC9B,EAA4B,CAAC,EAC7B,CACJ,gBACA,MACA,oBACA,mBACA,kBACE,GAAW,CAAC,EAEV,EAAqC,CAAC,EAE5C,SAAS,EAAU,EAAuB,CAMtC,OALE,IAAiB,UACZ,EACE,IAAiB,QACnB,EAEA,CAEX,CAEA,SAAS,EACP,EACA,EACA,CACA,IAAM,EAAU,EAAU,EAAE,MAAM,YAAY,EACxC,EAAU,EAAU,EAAE,MAAM,YAAY,EAO9C,OALI,EAAU,EACL,GACF,EAAI,EAAU,EAIvB,CAEA,IAAM,EACJ,IAAkB,IAAA,GAAqC,IAAA,GAAzB,IAAI,IAAI,CAAa,EAC/C,EAAc,OAAO,OAAO,EAAa,KAAK,CAAC,CAAC,KAAK,CAAS,EACpE,IAAK,IAAM,KAAQ,EAAa,CAC9B,GAAI,GAAa,CAAC,EAAU,IAAI,EAAK,EAAE,EACrC,SAIF,IAAM,EAAgB,IAAI,IACtB,EAAc,EAClB,GAAI,GAAO,CAAC,EAAkB,CAC5B,IAAM,EAAe,EAAI,QAGnB,EAAY,GAAc,WAAW,EAAK,KAAO,IAAI,IAE3D,IAAK,IAAM,KAAa,EAAW,CACjC,IAAM,EAAS,EACT,EAAS,EAAK,GAEpB,EAAM,KAAK,CACT,GAAI,OAAO,EAAO,GAAG,IACrB,SACA,SACA,MAAO,CACL,OAAQ,IACV,CACF,CAAC,CACH,CAEA,IAAM,EAAiB,OAAO,QAAQ,GAAc,SAAW,CAAC,CAAC,CAAC,CAC/D,QAAQ,CAAC,KAAS,EAAI,WAAW,GAAG,EAAK,GAAG,EAAE,CAAC,CAAC,CAChD,KAAK,CAAC,KAAS,EAAI,MAAM,EAAK,GAAG,OAAS,CAAC,CAAC,EAE/C,IAAK,IAAM,KAAc,EAAgB,CACvC,IAAM,EAAY,GAAG,EAAK,GAAG,GAAG,IAC1B,EAAe,EAAI,QAGnB,EAAS,GAAc,QAAQ,GAC/B,EACJ,GAAc,WAAW,IAAc,IAAI,IAEzC,MAAU,KAId,GAAM,KAAK,CACT,GAAI,EACJ,SAAU,CAAE,EAAG,GAAI,EAAG,GAAK,EAAA,EAA4B,EACvD,SAAU,EAAK,GACf,OAAQ,SACR,UAAW,GACX,UAAW,oBACX,KAAM,CACJ,KAAM,EAAK,KACX,OAAQ,EAAO,KACf,KAAM,EAAO,KACb,mBAAoB,EAAO,oBAC3B,aAAc,EAAsB,EAAO,aAAa,CAC1D,EACA,MAAO,CACL,OAAQ,IACV,EACA,KAAM,yBACN,eAAgB,EAAS,KACzB,eAAgB,EAAS,KAC3B,CAA2B,EAE3B,IAAK,IAAM,KAAgB,EAAW,CACpC,IAAM,EAAS,EACT,EAAS,EAEf,EAAM,KAAK,CACT,GAAI,GAAG,EAAO,GAAG,IACjB,SACA,SACA,MAAO,CACL,OAAQ,IACV,CACF,CAAC,CACH,CAEA,IACA,EAAc,IAAI,EAAO,IAAI,CAjBF,CAkB7B,CACF,CAEA,EAAiB,EAAK,IAAM,EAE5B,IAAI,EAAS,GACT,EAAc,IAChB,GAAU,GAAK,EAAA,IAGjB,IAAM,EAAmB,GAAmB,IAAI,EAAK,EAAE,EACvD,EAAM,QAAQ,CACZ,GAAI,EAAK,GACT,SAAU,GAAoB,CAAE,EAAG,EAAG,EAAG,CAAE,EAC3C,MAAO,IACC,SACR,UAAW,oBACX,KAAM,CACJ,GAAG,EAAK,IACV,EACA,KAAM,mBACN,eAAgB,EAAS,KACzB,eAAgB,EAAS,MACzB,MAAO,CACL,MAAO,IACC,QACV,CACF,CAAqB,CACvB,CAEA,IAAM,EAAc,OAAO,OAAO,EAAa,KAAK,CAAC,CAAC,KAAK,CAAS,EACpE,IAAK,IAAM,KAAQ,EAEf,IACC,CAAC,EAAU,IAAI,EAAK,MAAM,GAAK,CAAC,EAAU,IAAI,EAAK,MAAM,IAK5D,EAAM,KAAK,CACT,GAAI,EAAK,GACT,KAAM,mBACN,OAAQ,EAAK,OACb,OAAQ,EAAK,OACb,KAAM,CACJ,GAAG,EAAK,IACV,CACF,CAAqB,EAkBvB,OAdI,GAAoB,GAAkB,GACxC,GAAuB,EAAO,EAAO,EAAgB,EAAK,CAAS,EAIjD,EAAM,KACvB,GACC,EAAK,OAAS,oBAAsB,CAAC,GAAmB,IAAI,EAAK,EAAE,CAGzD,GACZ,GAAO,EAAO,CAAK,EAGd,CAAC,EAAO,EAAO,CAAgB,CACxC,CASA,SAAS,GACP,EACA,EACA,EACA,EACA,EACA,CAEA,IAAM,EAAoB,IAAI,IACxB,EAAuB,IAAI,IAIjC,IAAK,GAAM,CAAC,EAAS,KAAgB,EAE/B,QAAa,CAAC,EAAU,IAAI,CAAO,GAEvC,KAAK,IAAI,EAAI,EAAG,EAAI,EAAY,OAAQ,IAAK,CAC3C,IAAM,EAAa,EAAY,GACzB,EAAY,GAAG,EAAQ,GAAG,EAAW,SAC3C,EAAkB,IAAI,CAAS,EAC/B,EAAqB,IAAI,EAAW,CAClC,UACA,WAAY,EAAW,UACzB,CAAC,EACD,IAAM,EAAM,EAAI,QAAQ,QAAQ,GAEhC,EAAM,KAAK,CACT,GAAI,EACJ,SAAU,CAAE,EAAG,GAAI,EAAG,GAAK,EAAA,EAAkB,EAC7C,SAAU,EACV,UAAW,GACX,UAAW,oBACX,KAAM,CACJ,KAAM,CAAE,GAAI,CAAQ,EACpB,OAAQ,EAAW,OACnB,KAAM,GAAK,KACX,mBAAoB,EAAW,mBAC/B,aAAc,EAAW,aACzB,cAAe,GACf,UAAW,GACX,WAAY,EAAW,UACzB,EACA,MAAO,CACL,OAAQ,IACV,EACA,KAAM,yBACN,eAAgB,EAAS,KACzB,eAAgB,EAAS,KAC3B,CAA2B,CAC7B,CAGA,GAAI,EAAY,OAAS,EAAG,CAC1B,IAAM,EAAY,EAAM,KACrB,GAAM,EAAE,KAAO,GAAW,EAAE,OAAS,kBACxC,EACI,IACF,EAAU,OAAS,GAAU,EAAY,OAAA,GAE7C,CAVA,CAcF,IAAK,IAAM,KAAY,EAAmB,CACxC,IAAM,EAAU,EAAI,QAAQ,WAAW,IAAa,CAAC,EACrD,IAAK,IAAM,KAAkB,EAC3B,GAAI,EAAkB,IAAI,CAAc,EAAG,CAGzC,IAAM,EAFa,EAAqB,IAAI,CAEV,CAAC,EAAE,YAAc,GACnD,EAAM,KAAK,CACT,GAAI,YAAY,EAAe,GAAG,IAClC,OAAQ,EACR,OAAQ,EACR,MAAO,CACL,OAAQ,KACR,YAAa,EACb,OAAQ,EACJ,EAAsB,SACtB,EAAsB,SAC5B,CACF,CAAC,CACH,CAEJ,CACF,CAYA,MAAa,IACX,EACA,EACA,EAAY,OACH,CACT,EAAgB,GAAO,EAAO,EAAO,CAAS,CAChD,EC7HA,SAAS,EAAS,CAAE,WAAU,QAAO,SAAwB,CAC3D,OAAO,IAAU,EAAQ,EAAA,EAAA,CAAG,UAAW,CAAA,EAAI,IAC7C,CAOA,MAAM,MAAoB,EAAC,OAAD,CAAO,CAAA,EAG3B,IAED,CAAE,cAAe,EAAA,EAAA,CAAG,UAAW,CAAA,EAG9B,OAAiC,GASvC,SAAS,EACP,EACA,EACA,EACQ,CAOR,OANI,EACK,GAAuB,cAE3B,EAAkB,CAAO,EAGvB,GAFE,mCAGX,CAcA,SAAS,GAAuB,CAC9B,OACA,kBACA,eACA,oBACA,mBAC8B,CAC9B,IAAM,EACJ,EAAK,KAAK,eAAiB,SAAW,EAAK,KAAK,eAAiB,UAE7D,EAAY,GAAc,OAAS,EACnC,EAAe,GAAc,WAAa,EAC1C,EAAc,GAAc,SAAW,EAE7C,OACE,EAAC,EAAD,CACE,UAAU,MACV,GAAI,CACF,WAAY,SACZ,SAAU,OACV,IAAK,CACP,WANF,CAQE,EAAC,EAAD,CACE,KAAK,SACL,QAAQ,WACR,MAAM,UACN,UAAW,EAAC,EAAD,CAAW,SAAS,OAAS,CAAA,EACxC,QAAS,GAAiB,aAC1B,GAAI,CAAE,cAAe,MAAO,WAC7B,OAEO,CAAA,EACR,EAAC,EAAD,CACE,KAAK,SACL,QAAQ,WACR,MAAM,UACN,UAAW,EAAC,EAAD,CAAc,SAAS,OAAS,CAAA,EAC3C,QAAS,GAAiB,gBAC1B,GAAI,CAAE,cAAe,MAAO,WAN9B,CAOC,YAEE,GAAmB,MAAQ,EAAA,EAAA,CAAA,SAAA,CAAE,QAAQ,CAAkB,CAAA,CAAA,CAClD,IACR,EAACA,EAAD,CACE,MAAO,EAAiB,EAAkB,UAAW,CAAiB,EACtE,UAAU,eAEV,EAAC,OAAD,CAAA,SACE,EAAC,EAAD,CACE,KAAK,SACL,QAAQ,WACR,MAAM,UACN,UAAW,EAAC,EAAD,CAAa,SAAS,OAAS,CAAA,EAC1C,QAAS,GAAiB,eAC1B,SAAU,EACV,GAAI,CAAE,cAAe,MAAO,WAC7B,SAEO,CAAA,CACJ,CAAA,CACI,CAAA,CACP,GAEX,CAWA,SAAS,GAAoB,CAAE,UAAS,QAAkC,CACxE,OACE,EAAC,EAAD,CACE,KAAK,SACL,QAAQ,WACR,MAAM,UACN,UAAW,EAAC,EAAD,CAAM,SAAS,OAAS,CAAA,EAC1B,UACT,GAAI,CAAE,cAAe,MAAO,WAC7B,8BAEO,CAAA,CAEZ,CAeA,SAAS,GAAkB,CACzB,OACA,kBACA,eACA,iBACA,oBACA,2BACA,mBACyB,CACzB,IAAM,EAAe,GAAgB,OAAS,gBACxC,EACJ,EAAK,KAAK,eAAiB,SAAW,EAAK,KAAK,eAAiB,UAE7D,EAAgB,GAAc,YAAc,EAC5C,EAAmB,GAAc,gBAAkB,EACnD,EAAkB,GAAc,cAAgB,EAChD,EAAgB,GAAc,YAAc,EAC5C,EAAe,GAAc,YAAc,EAC3C,EAAoB,GAAc,gBAAkB,EAEpD,GACJ,EAIA,IAEI,EAEA,EAAC,EAAD,CAA0B,QAAS,YAChC,CACuB,CAAA,EAU5B,EAACA,EAAD,CAAY,MANS,EACrB,EACA,EACA,CAGgC,EAAG,UAAU,eAC3C,EAAC,OAAD,CAAA,SAAO,CAAa,CAAA,CACV,CAAA,EAIhB,OACE,EAAC,EAAD,CACE,UAAU,MACV,GAAI,CACF,WAAY,SACZ,SAAU,OACV,IAAK,CACP,WANF,CAQE,EAAC,EAAD,CACE,QAAQ,UACR,GAAI,CACF,WAAY,MACd,WACD,MAEW,CAAA,EACZ,EAAC,EAAD,CACE,UAAU,MACV,GAAI,CACF,WAAY,SACZ,SAAU,OACV,IAAK,EACL,MAAO,KACT,WAPF,CASE,EAAC,EAAD,CAA0B,QAAS,WACjC,EAAC,EAAD,CACE,KAAK,SACL,QAAQ,WACR,MAAM,UACN,UAAW,EAAC,EAAD,CAAkB,SAAS,OAAS,CAAA,EAC/C,QAAS,GAAiB,oBAC1B,SAAU,GAAgB,qBAC1B,GAAI,CAAE,cAAe,MAAO,WAP9B,CAQC,YAEE,GAAmB,MAAQ,EAAA,EAAA,CAAA,SAAA,CAAE,QAAQ,CAAkB,CAAA,CAAA,CAClD,GACgB,CAAA,EACzB,EACC,EAAC,EAAD,CACE,KAAK,SACL,QAAQ,WACR,MAAM,UACN,UAAW,EAAC,EAAD,CAAiB,SAAS,OAAS,CAAA,EAC9C,QAAS,GAAiB,mBAC1B,SAAU,GAAoB,GAAgB,qBAC9C,GAAI,CAAE,cAAe,MAAO,WAC7B,SAEO,CAAA,EACR,cACF,EACC,EACC,EAAC,EAAD,CACE,KAAK,SACL,QAAQ,WACR,MAAM,UACN,UAAW,EAAC,EAAD,CAAe,SAAS,OAAS,CAAA,EAC5C,QAAS,GAAiB,iBAC1B,SAAU,GAAoB,GAAgB,qBAC9C,GAAI,CAAE,cAAe,MAAO,WAC7B,OAEO,CAAA,EACR,YACF,EACC,EACC,EAAC,EAAD,CACE,KAAK,SACL,QAAQ,WACR,MAAM,UACN,UAAW,EAAC,EAAD,CAAc,SAAS,OAAS,CAAA,EAC3C,QAAS,GAAiB,gBAC1B,SAAU,GAAoB,GAAgB,qBAC9C,GAAI,CAAE,cAAe,MAAO,WAC7B,OAEO,CAAA,EACR,YACF,EACC,EACC,EAAC,EAAD,CACE,KAAK,SACL,QAAQ,WACR,MAAM,UACN,UAAW,EAAC,EAAD,CAAmB,SAAS,OAAS,CAAA,EAChD,QAAS,GAAiB,qBAC1B,SAAU,GAAoB,GAAgB,qBAC9C,GAAI,CAAE,cAAe,MAAO,WAC7B,WAEO,CAAA,EACR,gBACF,EACA,EAAC,EAAD,CAA0B,QAAS,WACjC,EAAC,EAAD,CACE,KAAK,SACL,QAAQ,WACR,MAAM,UACN,UAAW,EAAC,EAAD,CAAe,SAAS,OAAS,CAAA,EAC5C,QAAS,GAAiB,iBAC1B,SAAU,GAAgB,qBAC1B,GAAI,CAAE,cAAe,MAAO,WAC7B,OAEO,CAAA,CACgB,CAAA,CACrB,GACF,GAEX,CAqCA,SAAgB,GAAyC,CACvD,OACA,cACA,cACA,iBACA,cACA,oBAEA,aACA,sBACA,cACA,kBACA,wBACA,2BAA2B,GAE3B,eAEA,kBACA,oBAAoB,GACpB,sBAAsB,GACtB,uBAAuB,GACvB,mBAAmB,GACnB,aAAa,GACb,mBACuB,CACvB,IAAM,EACJ,EAAK,KAAK,eAAiB,SAC3B,EAAK,KAAK,eAAiB,QAC3B,EAAK,KAAK,eAAiB,UAC3B,EAAK,KAAK,eAAiB,WAEvB,CAAC,EAAoB,GAAyB,EAAS,EAAI,EAC3D,CAAC,EAAU,GAAe,EAAS,CAAC,EAEpC,CAAE,OAAM,WAAY,GAAe,CAAC,EACpC,EACJ,CAAC,GACD,EAAK,KAAK,QAAQ,SAAW,MAC7B,OAAO,KAAK,EAAK,KAAK,OAAO,OAAO,CAAC,CAAC,OAAS,EAM3C,EAAiB,CAAC,GAAe,EAAK,KAAK,eAAiB,WAE5D,EACJ,EAAK,KAAK,eAAiB,SAC3B,EAAK,KAAK,eAAiB,QAC3B,EAAK,KAAK,eAAiB,WAEvB,GACJ,CAAC,GACD,GACA,GAAiB,sBAAwB,KACrC,GAAiB,GAAc,aAAe,EAI9C,CAAE,WAAW,EAAe,EAC5B,EAAkB,CACtB,mBACA,sBACA,uBACA,aACA,eAAgB,EAAK,KAAK,QAAQ,QACpC,EAGM,EAAY,EAAc,EAAiB,EAAM,EACjD,EAAkB,EACtB,CACE,KAAM,EAAK,KAAK,KAChB,aAAc,EAAK,KAAK,aACxB,aAAc,EAAK,KAAK,YAC1B,EACA,CACF,EAEA,OACE,EAAC,EAAD,CACE,UAAW,EAAY,iBAAmB,IAAA,GAC1C,GAAI,CACF,OAAQ,OACR,QAAS,OACT,cAAe,SAIf,WAAY,aAAa,EAAY,EAAU,OAAO,aAAe,eACvE,WAVF,CAaE,EAAC,EAAD,CACE,UAAU,MACV,GAAI,CACF,WAAY,SACZ,GAAI,EACJ,GAAI,IACJ,IAAK,CACP,WAPF,CASE,EAACA,EAAD,CAAY,MAAO,EAAiB,UAAU,eAC3C,EACC,EAAC,EAAD,CACE,OAAQ,EAAU,OAClB,QAAQ,YACR,OAAQ,eAAe,EAAU,KAAK,aACtC,GAAI,CACF,KAAM,WACN,GAAI,OACJ,SAAU,CACZ,WAEA,EAAC,EAAD,CACE,QAAQ,YACR,UAAU,OACV,UAAU,oBACV,GAAI,CACF,WAAY,IACZ,SAAU,SACV,aAAc,WACd,WAAY,SACZ,MAAO,SACT,WAEC,EAAK,KAAK,IACD,CAAA,CACC,CAAA,EAEf,EAAC,EAAD,CACE,UAAU,OACV,QAAQ,YACR,UAAU,oBACV,GAAI,CACF,WAAY,IACZ,KAAM,WACN,GAAI,OACJ,SAAU,EACV,SAAU,SACV,aAAc,WACd,WAAY,QACd,WAEC,EAAK,KAAK,IACD,CAAA,CAEJ,CAAA,EACX,GACC,EAAC,EAAD,CAAK,GAAI,CAAE,MAAO,iBAAkB,WAAY,CAAE,WAChD,EAAC,EAAD,CAAuB,MAAO,CAAA,CAC3B,CAAA,EAEP,EAAC,EAAD,CAAY,KAAK,QAAQ,QAAS,EAAa,GAAI,CAAE,WAAY,CAAE,WACjE,EAAC,GAAD,CAAU,CAAA,CACA,CAAA,CACP,IAEN,GACC,EAAC,EAAD,CAAK,GAAI,CAAE,GAAI,EAAG,GAAI,CAAE,WACrB,EACC,EAAC,GAAD,CACQ,OACW,kBACH,eACK,oBACF,iBAClB,CAAA,EAED,EAAC,GAAD,CACQ,OACW,kBACH,eACE,iBACG,oBACO,2BACT,iBAClB,CAAA,CAEA,CAAA,EAGN,GACC,EAAC,EAAD,CACE,GAAI,CACF,SAAU,OACV,QAAS,OACT,cAAe,SACf,KAAM,EACN,UAAW,CACb,WAPF,CAUG,GAAe,GAAsB,GACpC,EAAC,EAAD,CAAK,GAAI,CAAE,EAAG,GAAI,WAChB,EAAC,EAAD,CACE,YAAe,EAAsB,EAAK,WAE1C,EAAC,EAAD,CAAY,QAAQ,iBAAQ,2FAGhB,CAAA,CACS,CAAA,CACpB,CAAA,EAIP,EAAC,GAAD,CACE,MAAO,EACP,UAAW,EAAG,IAAa,EAAY,CAAQ,EAC/C,GAAI,CAAE,aAAc,EAAG,YAAa,SAAU,WAHhD,CAKE,EAAC,EAAD,CACE,MACE,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CACF,QAAS,OACT,WAAY,SACZ,IAAK,GACP,WANF,CAOC,UAEE,GACC,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CACF,MAAO,aACP,SAAU,SACV,WAAY,CACd,WACD,GAEI,CAAA,CAEJ,GAER,CAAA,EACD,EAAC,EAAD,CACE,MACE,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CACF,QAAS,OACT,WAAY,SACZ,IAAK,GACP,WANF,CAOC,OAEE,GACC,EAAC,EAAD,CACE,UAAU,OACV,GAAI,CACF,MAAO,aACP,SAAU,SACV,WAAY,CACd,WACD,GAEI,CAAA,CAEJ,GAER,CAAA,EACA,GAAqB,EAAC,EAAD,CAAK,MAAM,SAAW,CAAA,CACxC,IAGN,EAAC,EAAD,CAAK,GAAI,CAAE,SAAU,OAAQ,OAAQ,mBAAoB,WAAzD,CACE,EAAC,EAAD,CAAU,MAAO,EAAU,MAAO,WAChC,EAAC,EAAD,CAAK,GAAI,CAAE,UAAW,OAAQ,OAAQ,MAAO,WAC1C,EACG,GACE,EAAC,EAAD,CAA8B,SAAU,CAAA,EAE1C,GACE,EAAC,EAAD,CACQ,OACG,UACT,cAAe,EAAK,KAAK,QAAQ,QACjC,eAAkB,EAAY,CAAC,EAC/B,aACE,GACE,EAAC,GAAD,CACE,QAAS,GAAiB,qBAC1B,KAAM,EACP,CAAA,EACC,IAAA,EAEP,CAAA,CAEJ,CAAA,CACG,CAAA,EACV,EAAC,EAAD,CAAU,MAAO,EAAU,MAAO,WAChC,EAAC,EAAD,CAAK,GAAI,CAAE,OAAQ,MAAO,WACvB,GAAe,EAAC,EAAD,CAAmB,MAAO,CAAA,CACvC,CAAA,CACG,CAAA,EACT,GACC,EAAC,EAAD,CAAU,MAAO,EAAU,MAAO,WAChC,EAAC,EAAD,CAAK,GAAI,CAAE,OAAQ,MAAO,WAAI,CAAuB,CAAA,CAC7C,CAAA,CAET,GACF,GAEJ,GAET,CCh3BA,MAAa,GAAuB"}
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ import{d as e,f as t}from"./keepAlive-Bowms1oa.js";import{n,t as r}from"./fetchClient-Bf9Q3QKq.js";import{useQuery as i}from"@tanstack/react-query";import{useEffect as a,useState as o}from"react";function s(e){return e.type===`simple`}function c(e){return e.type===`query`}function l(e){return e.type===`query_base`}function u(e){return e.type===`query_diff`}function d(e){return e.type===`value_diff`}function ee(e){return e.type===`value_diff_detail`}function te(e){return e.type===`schema_diff`}function ne(e){return e.type===`profile`}function re(e){return e.type===`profile_diff`}function ie(e){return e.type===`row_count`}function ae(e){return e.type===`row_count_diff`}function f(e){return e.type===`lineage_diff`}function p(e){return e.type===`top_k_diff`}function m(e){return e.type===`histogram_diff`}const h=[`simple`,`query`,`query_base`,`query_diff`,`value_diff`,`value_diff_detail`,`schema_diff`,`profile`,`profile_diff`,`row_count`,`row_count_diff`,`lineage_diff`,`top_k_diff`,`histogram_diff`,`profile_distribution`];function g(e){return h.includes(e)}const _=[`query`,`query_base`,`query_diff`,`row_count`,`row_count_diff`,`profile`,`profile_diff`,`value_diff`,`value_diff_detail`,`top_k_diff`,`histogram_diff`];function v(e){return _.includes(e)}async function y(e,t,n,r){let i=n?.trackProps?{...n.trackProps}:{};return(await r.post(`/api/runs`,{type:e,params:t,nowait:n?.nowait,track_props:i})).data}async function b(e,t){return(await t.get(`/api/runs/${e}`)).data}async function x(e,t,n){return D((await n.get(`/api/runs/${e}/wait`,{params:{timeout:t}})).data)}async function S(e,t){try{await t.post(`/api/runs/${e}/cancel`)}catch{}}async function C(e,t,n){return(await n.post(`/api/checks/${e}/run`,{nowait:t?.nowait})).data}async function w(e,t,n,r){return(await r.post(`/api/runs/search`,{type:e,params:t,limit:n})).data}async function T(e){return(await e.get(`/api/runs`)).data}async function E(e){return(await e.post(`/api/runs/aggregate`,{})).data}function D(e){if(e.result==null)return e;if(c(e)&&e.result){let t=e.result;t.columns&&=t.columns.map(e=>e.key?e:{...e,key:e.name})}return e}async function O(e,t,n){return await y(`query`,e,t,n)}async function k(e,t,n){return await y(`query_base`,e,t,n)}async function A(e,t,n){return await y(`query_diff`,e,t,n)}async function j(e,t){return(await t.get(`/api/checks/${e}/events`)).data}async function M(e,t,n){return(await n.get(`/api/checks/${e}/events/${t}`)).data}async function N(e,t,n){return(await n.post(`/api/checks/${e}/events`,{content:t})).data}async function P(e,t,n,r){return(await r.patch(`/api/checks/${e}/events/${t}`,{content:n})).data}async function F(e,t,n){await n.delete(`/api/checks/${e}/events/${t}`)}function I(e){return e.event_type===`comment`}function L(e){return e.event_type===`approval_change`||e.event_type===`description_change`||e.event_type===`name_change`}function R(e){return e.event_type===`check_created`||e.event_type===`preset_applied`}function z(e){let t=e.actor.fullname||e.actor.login||`Someone`;switch(e.event_type){case`check_created`:return`${t} created this check`;case`comment`:return e.is_deleted?`Comment deleted`:e.content||``;case`approval_change`:return e.new_value===`true`?`${t} approved this check`:`${t} unapproved this check`;case`description_change`:return`${t} updated the description`;case`name_change`:return`${t} renamed this check`;case`preset_applied`:return`${t} applied a preset`;default:return`${t} made a change`}}function B(e){switch(e.event_type){case`check_created`:return`create`;case`comment`:return`comment`;case`approval_change`:return e.new_value===`true`?`approve`:`unapprove`;case`description_change`:case`name_change`:return`edit`;case`preset_applied`:return`preset`;default:return`edit`}}const V=r({baseURL:``});async function H(e){return(await e.post(`/api/checks`,{type:`simple`})).data}async function U(e,t,n){return(await n.post(`/api/checks`,{run_id:e,view_options:t})).data}async function W(e){return(await e.get(`/api/checks`)).data}async function G(e,t){return(await t.get(`/api/checks/${e}`)).data}async function K(e,t,n){return(await n.patch(`/api/checks/${e}`,t)).data}async function q(e,t){return(await t.delete(`/api/checks/${e}`)).data}async function oe(e,t){await t.post(`/api/checks/reorder`,e)}async function se(e,t){await t.post(`/api/checks/${e}/mark-as-preset`)}function ce(n){let r=e()?.apiClient??V;return i({queryKey:t.checks(),queryFn:()=>W(r),enabled:n})}async function le(e,t){return(await t.post(`/api/cll`,e)).data}function ue(e){return e.source_session_id!==null&&(e.source_session_id!==e.current_base_session_id||e.source_session_updated_at!==e.current_base_updated_at)}async function de(e){return(await e.get(`/api/info`)).data}async function fe(e,t){return(await t.get(`/api/models/${e}`)).data}async function pe(e){await e.post(`/api/refresh-base`)}async function me(e,t){return(await t.post(`/api/checks`,{type:`lineage_diff`,params:{},view_options:e})).data}async function J(e,t,n){return await y(`row_count_diff`,e,t,n)}async function he(e,t){return(await t.get(`/api/models/${e}/row_count`)).data}async function ge(e,t){let{result:n}=await Y([e],t);return n[e]}async function Y(e,t){if(e.length===0)throw Error(`No model names provided`);let{run_id:n}=await J({node_names:e},{nowait:!0},t);return{runId:n,result:(await x(n,void 0,t)).result}}async function _e(e,t,n){return await y(`profile_diff`,e,t,n)}async function ve(e,t,n){return await y(`profile_distribution`,e,t,n)}async function ye(e,t){return(await t.post(`/api/checks`,{type:`schema_diff`,params:e})).data}async function be(e,t){return(await t.post(`/api/select`,e)).data}async function xe(e,t){return(await t.post(`/api/save-as`,e)).data}async function Se(e,t){return(await t.post(`/api/rename`,e)).data}async function Ce(e){return(await e.post(`/api/export`)).data}async function X(e,t,n){let r=new FormData;return r.append(`file`,e),r.append(`checks_only`,(!!t).toString()),(await n.post(`/api/import`,r)).data}async function we(e){return(await e.get(`/api/sync`)).status===208}async function Te(e,t){try{let n=await t.post(`/api/sync`,e);if(n.status===202)return{status:`accepted`};if(n.status===208)return{status:`syncing`}}catch(e){if(n(e)&&e.status===409)return{status:`conflict`}}throw Error(`Failed to sync state`)}async function Ee(e){return(await e.post(`/api/share`)).data}const Z=`recce-`,De={bypassSaveOverwrite:`${Z}-bypass-save-overwrite`,previewChangeFeedbackID:`${Z}-preview-change-feedback`,prepareEnvGuideID:`${Z}-prepare-env`,snapshotBaseIntroSeen:`${Z}-snapshot-base-intro-seen`},Q=`recce`,Oe={recommendationIgnored:`${Q}-recommendation-ignored`,recommendationShowed:`${Q}-recommendation-showed`,prevRefreshTimeStamp:`${Q}-prev-refresh-timestamp`,lineageNotificationDismissed:`${Q}-lineage-notification-dismissed`};async function ke(e,t,n){return await y(`value_diff`,e,t,n)}async function Ae(e,t,n){return await y(`value_diff_detail`,e,t,n)}const je=r({baseURL:``});async function $(e){return(await e.get(`/api/version`)).data}function Me(){let[t,n]=o(``),[r,i]=o(``),s=e()?.apiClient??je;return a(()=>{async function e(){try{let{version:e,latestVersion:t}=await $(s);n(e),i(t)}catch(e){console.error(`Error fetching version number:`,e)}}e()},[s]),{version:t,latestVersion:r}}export{T as $,q as A,z as B,fe as C,le as D,pe as E,K as F,j as G,I as H,ce as I,k as J,P as K,N as L,W as M,se as N,U as O,oe as P,b as Q,F as R,me as S,ue as T,L as U,B as V,R as W,E as X,A as Y,S as Z,ve as _,g as _t,De as a,m as at,Y as b,v as bt,X as c,ne as ct,xe as d,c as dt,w as et,Ee as f,ae as ft,_e as g,p as gt,ye as h,s as ht,Ae as i,h as it,G as j,H as k,we as l,l as lt,be as m,te as mt,Me as n,C as nt,Oe as o,f as ot,Te as p,ie as pt,O as q,ke as r,x as rt,Ce as s,re as st,$ as t,y as tt,Se as u,u as ut,he as v,ee as vt,de as w,J as x,ge as y,d as yt,M as z};
3
+ //# sourceMappingURL=api-DTKI1Y_n.js.map