drizzle-cube 0.4.40 → 0.4.41

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 (161) hide show
  1. package/dist/cli/index.cjs +439 -0
  2. package/dist/client/charts/ChartLoader.d.ts +18 -3
  3. package/dist/client/charts/chartConfigRegistry.d.ts +11 -1
  4. package/dist/client/charts/chartPlugin.d.ts +74 -0
  5. package/dist/client/charts/lazyChartConfigRegistry.d.ts +10 -0
  6. package/dist/client/charts.js +13 -15
  7. package/dist/client/chunks/{DashboardEditModal-iGhIvNP6.js → DashboardEditModal-D_7J62sG.js} +683 -685
  8. package/dist/client/chunks/DashboardEditModal-D_7J62sG.js.map +1 -0
  9. package/dist/client/chunks/{FieldSearchModal-BxQ5JhWz.js → FieldSearchModal-5Qz6vvTz.js} +15 -16
  10. package/dist/client/chunks/{FieldSearchModal-BxQ5JhWz.js.map → FieldSearchModal-5Qz6vvTz.js.map} +1 -1
  11. package/dist/client/chunks/{RetentionCombinedChart-D4Yf1TnQ.js → RetentionCombinedChart-B1hUYaXt.js} +2 -2
  12. package/dist/client/chunks/{RetentionCombinedChart-D4Yf1TnQ.js.map → RetentionCombinedChart-B1hUYaXt.js.map} +1 -1
  13. package/dist/client/chunks/{RetentionHeatmap-BoGY6mlZ.js → RetentionHeatmap-Dn2ocjVf.js} +2 -2
  14. package/dist/client/chunks/{RetentionHeatmap-BoGY6mlZ.js.map → RetentionHeatmap-Dn2ocjVf.js.map} +1 -1
  15. package/dist/client/chunks/{analysis-builder-DEGG5NwE.js → analysis-builder-DPJJiHVx.js} +1506 -1564
  16. package/dist/client/chunks/analysis-builder-DPJJiHVx.js.map +1 -0
  17. package/dist/client/chunks/{analysis-builder-shared-BxHYfTzo.js → analysis-builder-shared-C-C-rOgu.js} +762 -778
  18. package/dist/client/chunks/analysis-builder-shared-C-C-rOgu.js.map +1 -0
  19. package/dist/client/chunks/{chart-bar-Bx4oKlqo.js → chart-bar-DVzmau1G.js} +1 -1
  20. package/dist/client/chunks/{chart-bar-Bx4oKlqo.js.map → chart-bar-DVzmau1G.js.map} +1 -1
  21. package/dist/client/chunks/{chart-box-plot-CVIi1aM5.js → chart-box-plot-CCmbHv1Y.js} +1 -1
  22. package/dist/client/chunks/{chart-box-plot-CVIi1aM5.js.map → chart-box-plot-CCmbHv1Y.js.map} +1 -1
  23. package/dist/client/chunks/{chart-bubble-DvyG15UB.js → chart-bubble-S6qSwWdK.js} +1 -1
  24. package/dist/client/chunks/{chart-bubble-DvyG15UB.js.map → chart-bubble-S6qSwWdK.js.map} +1 -1
  25. package/dist/client/chunks/{chart-candlestick-caHyxB9O.js → chart-candlestick-BNKbDruo.js} +1 -1
  26. package/dist/client/chunks/{chart-candlestick-caHyxB9O.js.map → chart-candlestick-BNKbDruo.js.map} +1 -1
  27. package/dist/client/chunks/{chart-config-activity-grid-USo7JrPh.js → chart-config-activity-grid-BSWS08cI.js} +1 -1
  28. package/dist/client/chunks/{chart-config-activity-grid-USo7JrPh.js.map → chart-config-activity-grid-BSWS08cI.js.map} +1 -1
  29. package/dist/client/chunks/{chart-config-area-D_ZufYzg.js → chart-config-area-DKwgcHp4.js} +1 -1
  30. package/dist/client/chunks/{chart-config-area-D_ZufYzg.js.map → chart-config-area-DKwgcHp4.js.map} +1 -1
  31. package/dist/client/chunks/{chart-config-bar-BCi2Wmd6.js → chart-config-bar-deTjEhap.js} +1 -1
  32. package/dist/client/chunks/{chart-config-bar-BCi2Wmd6.js.map → chart-config-bar-deTjEhap.js.map} +1 -1
  33. package/dist/client/chunks/{chart-config-box-plot-afKLzJSp.js → chart-config-box-plot-DU4iWk3V.js} +1 -1
  34. package/dist/client/chunks/{chart-config-box-plot-afKLzJSp.js.map → chart-config-box-plot-DU4iWk3V.js.map} +1 -1
  35. package/dist/client/chunks/{chart-config-bubble-CgbBjPv8.js → chart-config-bubble-B8FSHSW-.js} +1 -1
  36. package/dist/client/chunks/{chart-config-bubble-CgbBjPv8.js.map → chart-config-bubble-B8FSHSW-.js.map} +1 -1
  37. package/dist/client/chunks/{chart-config-candlestick-7boGjZ-A.js → chart-config-candlestick-BGfyWFft.js} +1 -1
  38. package/dist/client/chunks/{chart-config-candlestick-7boGjZ-A.js.map → chart-config-candlestick-BGfyWFft.js.map} +1 -1
  39. package/dist/client/chunks/{chart-config-data-table-Cl7sBasW.js → chart-config-data-table-DKRcGa8t.js} +1 -1
  40. package/dist/client/chunks/{chart-config-data-table-Cl7sBasW.js.map → chart-config-data-table-DKRcGa8t.js.map} +1 -1
  41. package/dist/client/chunks/{chart-config-funnel-CXPYQtTl.js → chart-config-funnel-Bt4iGFo_.js} +1 -1
  42. package/dist/client/chunks/{chart-config-funnel-CXPYQtTl.js.map → chart-config-funnel-Bt4iGFo_.js.map} +1 -1
  43. package/dist/client/chunks/{chart-config-gauge-DUNEUCvh.js → chart-config-gauge-Bk4Jjp3W.js} +1 -1
  44. package/dist/client/chunks/{chart-config-gauge-DUNEUCvh.js.map → chart-config-gauge-Bk4Jjp3W.js.map} +1 -1
  45. package/dist/client/chunks/{chart-config-heat-map-BFf1tO11.js → chart-config-heat-map-CkHsqkrY.js} +1 -1
  46. package/dist/client/chunks/{chart-config-heat-map-BFf1tO11.js.map → chart-config-heat-map-CkHsqkrY.js.map} +1 -1
  47. package/dist/client/chunks/{chart-config-kpi-delta-C5k2waIJ.js → chart-config-kpi-delta-CkUX98JV.js} +1 -1
  48. package/dist/client/chunks/{chart-config-kpi-delta-C5k2waIJ.js.map → chart-config-kpi-delta-CkUX98JV.js.map} +1 -1
  49. package/dist/client/chunks/{chart-config-kpi-number-DptOyhk0.js → chart-config-kpi-number-DcxyiUgs.js} +1 -1
  50. package/dist/client/chunks/{chart-config-kpi-number-DptOyhk0.js.map → chart-config-kpi-number-DcxyiUgs.js.map} +1 -1
  51. package/dist/client/chunks/{chart-config-kpi-text-D9DdVWqd.js → chart-config-kpi-text-DI4mj8CN.js} +1 -1
  52. package/dist/client/chunks/{chart-config-kpi-text-D9DdVWqd.js.map → chart-config-kpi-text-DI4mj8CN.js.map} +1 -1
  53. package/dist/client/chunks/{chart-config-line-B3NgLF7K.js → chart-config-line--j7-dLue.js} +1 -1
  54. package/dist/client/chunks/{chart-config-line-B3NgLF7K.js.map → chart-config-line--j7-dLue.js.map} +1 -1
  55. package/dist/client/chunks/{chart-config-markdown-tlfivQTt.js → chart-config-markdown-DUjvVjV4.js} +1 -1
  56. package/dist/client/chunks/{chart-config-markdown-tlfivQTt.js.map → chart-config-markdown-DUjvVjV4.js.map} +1 -1
  57. package/dist/client/chunks/{chart-config-measure-profile-D7XDwrU2.js → chart-config-measure-profile-B9FKBNGc.js} +1 -1
  58. package/dist/client/chunks/{chart-config-measure-profile-D7XDwrU2.js.map → chart-config-measure-profile-B9FKBNGc.js.map} +1 -1
  59. package/dist/client/chunks/{chart-config-pie-wY0B52PC.js → chart-config-pie-yU4jipl9.js} +1 -1
  60. package/dist/client/chunks/{chart-config-pie-wY0B52PC.js.map → chart-config-pie-yU4jipl9.js.map} +1 -1
  61. package/dist/client/chunks/{chart-config-radar-DRpJBy1M.js → chart-config-radar-R9Fkc8wL.js} +1 -1
  62. package/dist/client/chunks/{chart-config-radar-DRpJBy1M.js.map → chart-config-radar-R9Fkc8wL.js.map} +1 -1
  63. package/dist/client/chunks/{chart-config-radial-bar-DCUpXv9G.js → chart-config-radial-bar-DeoXfpIp.js} +1 -1
  64. package/dist/client/chunks/{chart-config-radial-bar-DCUpXv9G.js.map → chart-config-radial-bar-DeoXfpIp.js.map} +1 -1
  65. package/dist/client/chunks/{chart-config-sankey-CdOhlm4h.js → chart-config-sankey-CXEsxo6s.js} +1 -1
  66. package/dist/client/chunks/{chart-config-sankey-CdOhlm4h.js.map → chart-config-sankey-CXEsxo6s.js.map} +1 -1
  67. package/dist/client/chunks/{chart-config-scatter-B2su_x8f.js → chart-config-scatter-MVUFupub.js} +1 -1
  68. package/dist/client/chunks/{chart-config-scatter-B2su_x8f.js.map → chart-config-scatter-MVUFupub.js.map} +1 -1
  69. package/dist/client/chunks/{chart-config-sunburst-BPdjbk18.js → chart-config-sunburst-Z_gqIY5u.js} +1 -1
  70. package/dist/client/chunks/{chart-config-sunburst-BPdjbk18.js.map → chart-config-sunburst-Z_gqIY5u.js.map} +1 -1
  71. package/dist/client/chunks/{chart-config-tree-map-Cbsh2fe2.js → chart-config-tree-map-BD-xAeIy.js} +1 -1
  72. package/dist/client/chunks/{chart-config-tree-map-Cbsh2fe2.js.map → chart-config-tree-map-BD-xAeIy.js.map} +1 -1
  73. package/dist/client/chunks/{chart-config-waterfall-DGmuZfQF.js → chart-config-waterfall-CHwVkXZc.js} +1 -1
  74. package/dist/client/chunks/{chart-config-waterfall-DGmuZfQF.js.map → chart-config-waterfall-CHwVkXZc.js.map} +1 -1
  75. package/dist/client/chunks/{chart-data-table-CW_qZDpt.js → chart-data-table-D4s27-U3.js} +1452 -826
  76. package/dist/client/chunks/chart-data-table-D4s27-U3.js.map +1 -0
  77. package/dist/client/chunks/{chart-gauge-BLLJqeXo.js → chart-gauge-BFhc4i_f.js} +1 -1
  78. package/dist/client/chunks/{chart-gauge-BLLJqeXo.js.map → chart-gauge-BFhc4i_f.js.map} +1 -1
  79. package/dist/client/chunks/{chart-heat-map-f2fM2mDC.js → chart-heat-map-BOMQeUDL.js} +1 -1
  80. package/dist/client/chunks/{chart-heat-map-f2fM2mDC.js.map → chart-heat-map-BOMQeUDL.js.map} +1 -1
  81. package/dist/client/chunks/{chart-kpi-delta-BPexzOe7.js → chart-kpi-delta-DzGNnIcW.js} +4 -4
  82. package/dist/client/chunks/{chart-kpi-delta-BPexzOe7.js.map → chart-kpi-delta-DzGNnIcW.js.map} +1 -1
  83. package/dist/client/chunks/{chart-kpi-number-BBtGBtZL.js → chart-kpi-number-sHtgbE_f.js} +5 -5
  84. package/dist/client/chunks/{chart-kpi-number-BBtGBtZL.js.map → chart-kpi-number-sHtgbE_f.js.map} +1 -1
  85. package/dist/client/chunks/{chart-kpi-text-BqHhmJEB.js → chart-kpi-text-Bmk-GzVJ.js} +2 -2
  86. package/dist/client/chunks/{chart-kpi-text-BqHhmJEB.js.map → chart-kpi-text-Bmk-GzVJ.js.map} +1 -1
  87. package/dist/client/chunks/{chart-line-CPhQRMZ7.js → chart-line-Bkl5WQAw.js} +1 -1
  88. package/dist/client/chunks/{chart-line-CPhQRMZ7.js.map → chart-line-Bkl5WQAw.js.map} +1 -1
  89. package/dist/client/chunks/{chart-markdown-B2X4IwLO.js → chart-markdown-7MNetRtX.js} +1 -1
  90. package/dist/client/chunks/{chart-markdown-B2X4IwLO.js.map → chart-markdown-7MNetRtX.js.map} +1 -1
  91. package/dist/client/chunks/{chart-measure-profile-CVlqGslU.js → chart-measure-profile-B7h6vQo4.js} +1 -1
  92. package/dist/client/chunks/{chart-measure-profile-CVlqGslU.js.map → chart-measure-profile-B7h6vQo4.js.map} +1 -1
  93. package/dist/client/chunks/{chart-pie-DafSc9sE.js → chart-pie-Do2YnCxl.js} +1 -1
  94. package/dist/client/chunks/{chart-pie-DafSc9sE.js.map → chart-pie-Do2YnCxl.js.map} +1 -1
  95. package/dist/client/chunks/{chart-radar-Dz9F5k-B.js → chart-radar-C7gQkH70.js} +1 -1
  96. package/dist/client/chunks/{chart-radar-Dz9F5k-B.js.map → chart-radar-C7gQkH70.js.map} +1 -1
  97. package/dist/client/chunks/{chart-radial-bar-N3MNUL7o.js → chart-radial-bar-DHqCck3x.js} +1 -1
  98. package/dist/client/chunks/{chart-radial-bar-N3MNUL7o.js.map → chart-radial-bar-DHqCck3x.js.map} +1 -1
  99. package/dist/client/chunks/{chart-scatter-J2JNi88S.js → chart-scatter-YSHOUfXf.js} +1 -1
  100. package/dist/client/chunks/{chart-scatter-J2JNi88S.js.map → chart-scatter-YSHOUfXf.js.map} +1 -1
  101. package/dist/client/chunks/{chart-sunburst-D1NFQjqk.js → chart-sunburst-BGhJ4fui.js} +1 -1
  102. package/dist/client/chunks/{chart-sunburst-D1NFQjqk.js.map → chart-sunburst-BGhJ4fui.js.map} +1 -1
  103. package/dist/client/chunks/{chart-tree-map-CbYjko2s.js → chart-tree-map-BlhcXK1F.js} +1 -1
  104. package/dist/client/chunks/{chart-tree-map-CbYjko2s.js.map → chart-tree-map-BlhcXK1F.js.map} +1 -1
  105. package/dist/client/chunks/{chart-waterfall-Z65TGMUO.js → chart-waterfall-BWCAzlPq.js} +1 -1
  106. package/dist/client/chunks/{chart-waterfall-Z65TGMUO.js.map → chart-waterfall-BWCAzlPq.js.map} +1 -1
  107. package/dist/client/chunks/{charts-core-CJlGzwsW.js → charts-core-Cy3rHADX.js} +1 -1
  108. package/dist/client/chunks/{charts-core-CJlGzwsW.js.map → charts-core-Cy3rHADX.js.map} +1 -1
  109. package/dist/client/chunks/{core-DJrniqct.js → core-BdWfCZ3y.js} +1 -1
  110. package/dist/client/chunks/{core-DJrniqct.js.map → core-BdWfCZ3y.js.map} +1 -1
  111. package/dist/client/chunks/{dist-DDBeV_JI.js → dist-BWPE2m_X.js} +1 -1
  112. package/dist/client/chunks/{dist-DDBeV_JI.js.map → dist-BWPE2m_X.js.map} +1 -1
  113. package/dist/client/chunks/{javascript-BBwTSo6e.js → javascript-O1RIRkZr.js} +1 -1
  114. package/dist/client/chunks/{javascript-BBwTSo6e.js.map → javascript-O1RIRkZr.js.map} +1 -1
  115. package/dist/client/chunks/{json-BpTrLZSh.js → json-C5bX2tt1.js} +1 -1
  116. package/dist/client/chunks/{json-BpTrLZSh.js.map → json-C5bX2tt1.js.map} +1 -1
  117. package/dist/client/chunks/{retention-UEXlSdZ-.js → retention-YhT1Oohi.js} +1 -1
  118. package/dist/client/chunks/{retention-UEXlSdZ-.js.map → retention-YhT1Oohi.js.map} +1 -1
  119. package/dist/client/chunks/{schema-visualization-CwiEtwdu.js → schema-visualization-BJ8HrNqB.js} +11 -11
  120. package/dist/client/chunks/{schema-visualization-CwiEtwdu.js.map → schema-visualization-BJ8HrNqB.js.map} +1 -1
  121. package/dist/client/chunks/{sql-B0chxcEK.js → sql-D2qikO5q.js} +1 -1
  122. package/dist/client/chunks/{sql-B0chxcEK.js.map → sql-D2qikO5q.js.map} +1 -1
  123. package/dist/client/chunks/{syntaxHighlighting-BLl0ch4A.js → syntaxHighlighting-BYYWYmjr.js} +2 -2
  124. package/dist/client/chunks/{syntaxHighlighting-BLl0ch4A.js.map → syntaxHighlighting-BYYWYmjr.js.map} +1 -1
  125. package/dist/client/chunks/{useDebounce-C_wstEud.js → useDebounce-EWynD0lC.js} +12 -12
  126. package/dist/client/chunks/{useDebounce-C_wstEud.js.map → useDebounce-EWynD0lC.js.map} +1 -1
  127. package/dist/client/chunks/{useDirtyStateTracking-CgKZWkel.js → useDirtyStateTracking-KAjwj1Ht.js} +1 -1
  128. package/dist/client/chunks/{useDirtyStateTracking-CgKZWkel.js.map → useDirtyStateTracking-KAjwj1Ht.js.map} +1 -1
  129. package/dist/client/chunks/{useExplainAI-C9ytXRIC.js → useExplainAI-BBTJWQVu.js} +14 -14
  130. package/dist/client/chunks/{useExplainAI-C9ytXRIC.js.map → useExplainAI-BBTJWQVu.js.map} +1 -1
  131. package/dist/client/chunks/{useNotebookLayout-BFZ_33Kb.js → useNotebookLayout-DKkMenhj.js} +1 -1
  132. package/dist/client/chunks/{useNotebookLayout-BFZ_33Kb.js.map → useNotebookLayout-DKkMenhj.js.map} +1 -1
  133. package/dist/client/chunks/{utils-3FNmeZJR.js → utils-DMyRayr_.js} +2 -2
  134. package/dist/client/chunks/{utils-3FNmeZJR.js.map → utils-DMyRayr_.js.map} +1 -1
  135. package/dist/client/chunks/{vendor-BPRWulB7.js → vendor-iY25ogTA.js} +40 -40
  136. package/dist/client/chunks/{vendor-BPRWulB7.js.map → vendor-iY25ogTA.js.map} +1 -1
  137. package/dist/client/components.js +3 -3
  138. package/dist/client/hooks.js +5 -5
  139. package/dist/client/icons/registry.d.ts +6 -0
  140. package/dist/client/icons.js +2 -2
  141. package/dist/client/index.d.ts +3 -1
  142. package/dist/client/index.js +160 -161
  143. package/dist/client/index.js.map +1 -1
  144. package/dist/client/providers/CubeProvider.d.ts +4 -1
  145. package/dist/client/providers.js +2 -2
  146. package/dist/client/types.d.ts +2 -1
  147. package/dist/client/utils.js +6 -6
  148. package/dist/client-bundle-stats.html +1 -1
  149. package/package.json +6 -2
  150. package/dist/client/chunks/DashboardEditModal-iGhIvNP6.js.map +0 -1
  151. package/dist/client/chunks/RetentionCombinedChart.config-DprbXd1N.js +0 -56
  152. package/dist/client/chunks/RetentionCombinedChart.config-DprbXd1N.js.map +0 -1
  153. package/dist/client/chunks/RetentionHeatmap.config-cbaNExVy.js +0 -25
  154. package/dist/client/chunks/RetentionHeatmap.config-cbaNExVy.js.map +0 -1
  155. package/dist/client/chunks/analysis-builder-DEGG5NwE.js.map +0 -1
  156. package/dist/client/chunks/analysis-builder-shared-BxHYfTzo.js.map +0 -1
  157. package/dist/client/chunks/chart-data-table-CW_qZDpt.js.map +0 -1
  158. package/dist/client/chunks/charts-loader-gZjOqZwG.js +0 -259
  159. package/dist/client/chunks/charts-loader-gZjOqZwG.js.map +0 -1
  160. package/dist/client/chunks/lazyChartConfigRegistry-BjhxDaSf.js +0 -149
  161. package/dist/client/chunks/lazyChartConfigRegistry-BjhxDaSf.js.map +0 -1
@@ -129,4 +129,4 @@ var s = {
129
129
  //#endregion
130
130
  export { s as a, a as c, l as i, o as l, e as n, i as o, c as r, n as s, t };
131
131
 
132
- //# sourceMappingURL=retention-UEXlSdZ-.js.map
132
+ //# sourceMappingURL=retention-YhT1Oohi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"retention-UEXlSdZ-.js","names":[],"sources":["../../../src/client/types/retention.ts"],"sourcesContent":["/**\n * Retention Analysis Types\n *\n * Types for Retention analysis mode which tracks cohort-based retention over time.\n * Retention analysis measures what percentage of users from each cohort return\n * in subsequent time periods.\n */\n\nimport type { Filter } from '../types'\nimport type { FunnelBindingKey } from './funnel'\n\n// ============================================================================\n// Date Range Types\n// ============================================================================\n\n/**\n * Date range for cohort analysis (REQUIRED)\n * Matches server's RetentionDateRange interface\n */\nexport interface DateRange {\n /** Start date (inclusive), ISO 8601 format (YYYY-MM-DD) */\n start: string\n /** End date (inclusive), ISO 8601 format (YYYY-MM-DD) */\n end: string\n}\n\n/**\n * Preset date range type\n */\nexport type DateRangePreset =\n | 'last_30_days'\n | 'last_3_months'\n | 'last_6_months'\n | 'last_12_months'\n | 'this_year'\n | 'last_year'\n | 'custom'\n\n/**\n * Preset date range options for the UI\n */\nexport const RETENTION_DATE_RANGE_PRESETS: { value: DateRangePreset; label: string }[] = [\n { value: 'last_30_days', label: 'Last 30 days' },\n { value: 'last_3_months', label: 'Last 3 months' },\n { value: 'last_6_months', label: 'Last 6 months' },\n { value: 'last_12_months', label: 'Last 12 months' },\n { value: 'this_year', label: 'This year' },\n { value: 'last_year', label: 'Last year' },\n { value: 'custom', label: 'Custom range' },\n]\n\n/**\n * Default preset for retention date range\n */\nexport const DEFAULT_DATE_RANGE_PRESET: DateRangePreset = 'last_3_months'\n\n/**\n * Calculate date range from a preset value\n * Returns ISO date strings (YYYY-MM-DD)\n */\nexport function getDateRangeFromPreset(preset: DateRangePreset): DateRange {\n const now = new Date()\n const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n\n switch (preset) {\n case 'last_30_days': {\n const start = new Date(today)\n start.setDate(start.getDate() - 30)\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(today),\n }\n }\n case 'last_3_months': {\n // Start of 3 months ago to end of last month\n const start = new Date(today.getFullYear(), today.getMonth() - 3, 1)\n const end = new Date(today.getFullYear(), today.getMonth(), 0) // Last day of previous month\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(end),\n }\n }\n case 'last_6_months': {\n const start = new Date(today.getFullYear(), today.getMonth() - 6, 1)\n const end = new Date(today.getFullYear(), today.getMonth(), 0)\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(end),\n }\n }\n case 'last_12_months': {\n const start = new Date(today.getFullYear(), today.getMonth() - 12, 1)\n const end = new Date(today.getFullYear(), today.getMonth(), 0)\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(end),\n }\n }\n case 'this_year': {\n const start = new Date(today.getFullYear(), 0, 1)\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(today),\n }\n }\n case 'last_year': {\n const start = new Date(today.getFullYear() - 1, 0, 1)\n const end = new Date(today.getFullYear() - 1, 11, 31)\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(end),\n }\n }\n case 'custom':\n default:\n // For custom, return last 3 months as fallback\n return getDateRangeFromPreset('last_3_months')\n }\n}\n\n/**\n * Format a Date object to ISO date string (YYYY-MM-DD)\n */\nfunction formatDateToISO(date: Date): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n return `${year}-${month}-${day}`\n}\n\n/**\n * Detect which preset a date range matches, if any\n */\nexport function detectDateRangePreset(range: DateRange): DateRangePreset {\n for (const preset of RETENTION_DATE_RANGE_PRESETS) {\n if (preset.value === 'custom') continue\n const presetRange = getDateRangeFromPreset(preset.value)\n if (presetRange.start === range.start && presetRange.end === range.end) {\n return preset.value\n }\n }\n return 'custom'\n}\n\n// ============================================================================\n// Server Query Format\n// ============================================================================\n\n/**\n * Server retention query format\n * This is the shape sent to the server for execution\n * Wrapped in { retention: {...} } similar to funnel queries\n */\nexport interface ServerRetentionQuery {\n retention: RetentionQueryConfig\n}\n\n/**\n * Retention query configuration\n * Contains all parameters needed for server-side retention analysis\n *\n * Simplified Mixpanel-style format:\n * - Single cube and timestamp dimension\n * - Single cohort (date range defines the cohort, not granularity)\n * - Optional breakdown dimension for segmentation\n */\nexport interface RetentionQueryConfig {\n /**\n * Single timestamp dimension for the analysis.\n * String format (e.g., 'Events.timestamp'),\n * Object format for multi-cube with explicit cube reference.\n */\n timeDimension: string | { cube: string; dimension: string }\n\n /**\n * Binding key - dimension that links users across events.\n * This is typically a user ID or other entity identifier.\n * String for single-cube (e.g., 'Events.userId'),\n * Array for multi-cube with different column names per cube.\n */\n bindingKey: string | { cube: string; dimension: string }[]\n\n /**\n * Date range for cohort analysis (REQUIRED).\n * Users who first performed the cohort action within this range are included.\n */\n dateRange: DateRange\n\n /**\n * Granularity for viewing retention periods.\n * Determines how retention periods are measured (day/week/month).\n */\n granularity: RetentionGranularity\n\n /**\n * Number of periods to calculate (e.g., 12 for 12 weeks).\n * Period 0 is always the cohort entry period.\n */\n periods: number\n\n /**\n * Retention type:\n * - 'classic': User returned exactly in period N (bounded)\n * - 'rolling': User returned in period N or any later period (unbounded)\n */\n retentionType: RetentionType\n\n /**\n * Optional filters on cohort entry events.\n * Applied when identifying which users enter the cohort.\n */\n cohortFilters?: Filter | Filter[]\n\n /**\n * Optional filters on return activity events.\n * Applied when checking for user activity in each period.\n */\n activityFilters?: Filter | Filter[]\n\n /**\n * Optional breakdown dimensions for segmenting the cohort.\n * When provided, retention is calculated per breakdown value combination.\n * e.g., [\"Events.country\"] or [\"Events.country\", \"Events.plan\"]\n */\n breakdownDimensions?: string[]\n}\n\n// ============================================================================\n// Enums and Unions\n// ============================================================================\n\n/**\n * Supported granularity levels for retention analysis\n */\nexport type RetentionGranularity = 'day' | 'week' | 'month'\n\n/**\n * Retention calculation types\n * - classic: User active exactly in period N\n * - rolling: User active in period N or any later period\n */\nexport type RetentionType = 'classic' | 'rolling'\n\n// ============================================================================\n// Result Types\n// ============================================================================\n\n/**\n * Single retention data point returned from server\n * Results are returned as a flat array; client transforms to matrix if needed\n */\nexport interface RetentionResultRow {\n /** Period number (0 = cohort entry, 1 = first retention period, etc.) */\n period: number\n\n /** Number of users in the cohort */\n cohortSize: number\n\n /** Number of users retained in this period */\n retainedUsers: number\n\n /** Retention rate as decimal (0-1), e.g., 0.45 for 45% */\n retentionRate: number\n\n /** Breakdown value when breakdown dimension is specified (e.g., \"US\", \"UK\") */\n breakdownValue?: string | null\n}\n\n/**\n * Retention chart data format for visualization\n * Supports both heatmap and line chart modes\n */\nexport interface RetentionChartData {\n rows: RetentionResultRow[]\n /** Period numbers (0 to periods) */\n periods: number[]\n /** Breakdown values when breakdown dimension is specified */\n breakdownValues?: string[]\n /** Summary statistics */\n summary?: RetentionSummary\n /** Granularity of retention periods (day/week/month) for period label formatting */\n granularity?: RetentionGranularity\n /** Human-readable label extracted from the binding key dimension (e.g., \"userId\" from \"Users.userId\") */\n bindingKeyLabel?: string\n}\n\n/**\n * Summary statistics for retention analysis\n */\nexport interface RetentionSummary {\n /** Total unique users in the cohort */\n totalUsers: number\n /** Average retention rate across all periods for period 1 */\n avgPeriod1Retention: number\n /** Highest retention rate for period 1 */\n maxPeriod1Retention: number\n /** Lowest retention rate for period 1 */\n minPeriod1Retention: number\n /** Number of breakdown segments (1 if no breakdown) */\n segmentCount?: number\n}\n\n// ============================================================================\n// Breakdown Types\n// ============================================================================\n\n/**\n * Breakdown item for retention analysis (single dimension)\n * Follows Mixpanel pattern - one breakdown dimension only\n */\nexport interface RetentionBreakdownItem {\n /** Full dimension name (e.g., \"Events.country\") */\n field: string\n /** Display label for the dimension */\n label?: string\n}\n\n// ============================================================================\n// Slice State (for Zustand store)\n// ============================================================================\n\n/**\n * Retention mode state for the AnalysisBuilder store\n * Simplified Mixpanel-style with single global configuration\n *\n * Key simplifications from previous version:\n * - Single cube for all (no separate cohort/activity cubes)\n * - Single timestamp dimension\n * - Single cohort with breakdown support (no cohort explosion)\n * - Granularity = viewing periods only\n */\nexport interface RetentionSliceState {\n /** Single cube for retention analysis */\n retentionCube: string | null\n\n /** Binding key that identifies entities (reuses funnel binding key type) */\n retentionBindingKey: FunnelBindingKey | null\n\n /** Single timestamp dimension for both cohort entry and activity */\n retentionTimeDimension: string | null\n\n /** Date range for cohort analysis (REQUIRED) */\n retentionDateRange: DateRange\n\n /** Filters that define who enters the cohort */\n retentionCohortFilters: Filter[]\n\n /** Filters that define what counts as a return */\n retentionActivityFilters: Filter[]\n\n /** Optional breakdown dimensions for segmenting the cohort */\n retentionBreakdowns: RetentionBreakdownItem[]\n\n /** Granularity for viewing retention periods (day/week/month) */\n retentionViewGranularity: RetentionGranularity\n\n /** Number of periods to analyze (1-52) */\n retentionPeriods: number\n\n /** Type of retention calculation */\n retentionType: RetentionType\n}\n\n/**\n * Retention slice actions for the store\n */\nexport interface RetentionSliceActions {\n /** Set the single cube for retention analysis */\n setRetentionCube: (cube: string | null) => void\n /** Set the retention binding key */\n setRetentionBindingKey: (key: FunnelBindingKey | null) => void\n /** Set the single timestamp dimension */\n setRetentionTimeDimension: (dim: string | null) => void\n /** Set the date range (REQUIRED) */\n setRetentionDateRange: (range: DateRange) => void\n /** Set all cohort filters at once */\n setRetentionCohortFilters: (filters: Filter[]) => void\n /** Add a cohort filter */\n addRetentionCohortFilter: (filter: Filter) => void\n /** Remove a cohort filter by index */\n removeRetentionCohortFilter: (index: number) => void\n /** Update a cohort filter by index */\n updateRetentionCohortFilter: (index: number, filter: Filter) => void\n /** Set all activity filters at once */\n setRetentionActivityFilters: (filters: Filter[]) => void\n /** Add an activity filter */\n addRetentionActivityFilter: (filter: Filter) => void\n /** Remove an activity filter by index */\n removeRetentionActivityFilter: (index: number) => void\n /** Update an activity filter by index */\n updateRetentionActivityFilter: (index: number, filter: Filter) => void\n /** Set all breakdown dimensions */\n setRetentionBreakdowns: (breakdowns: RetentionBreakdownItem[]) => void\n /** Add a breakdown dimension */\n addRetentionBreakdown: (breakdown: RetentionBreakdownItem) => void\n /** Remove a breakdown dimension by field name */\n removeRetentionBreakdown: (field: string) => void\n /** Set the view granularity */\n setRetentionViewGranularity: (granularity: RetentionGranularity) => void\n /** Set the number of periods */\n setRetentionPeriods: (periods: number) => void\n /** Set the retention type */\n setRetentionType: (type: RetentionType) => void\n /** Check if in retention mode (analysisType === 'retention') */\n isRetentionMode: () => boolean\n /** Check if retention mode is properly configured and ready for execution */\n isRetentionModeEnabled: () => boolean\n /** Build ServerRetentionQuery from retention state */\n buildRetentionQuery: () => ServerRetentionQuery | null\n /** Get validation errors explaining why retention query cannot be built */\n getRetentionValidation: () => { isValid: boolean; errors: string[]; warnings: string[] }\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Type guard to check if data is retention chart data\n */\nexport function isRetentionData(data: unknown): data is RetentionChartData {\n if (!data || typeof data !== 'object') return false\n const d = data as Record<string, unknown>\n return (\n Array.isArray(d.rows) &&\n Array.isArray(d.periods)\n )\n}\n\n/**\n * Type guard to detect server retention query format\n * Used to distinguish { retention: {...} } from CubeQuery, etc.\n */\nexport function isServerRetentionQuery(obj: unknown): obj is ServerRetentionQuery {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'retention' in obj &&\n typeof (obj as { retention: unknown }).retention === 'object'\n )\n}\n\n/**\n * Type guard for retention result row\n */\nexport function isRetentionResultRow(row: unknown): row is RetentionResultRow {\n if (!row || typeof row !== 'object') return false\n const r = row as Record<string, unknown>\n return (\n typeof r.cohortPeriod === 'string' &&\n typeof r.period === 'number' &&\n typeof r.cohortSize === 'number' &&\n typeof r.retainedUsers === 'number' &&\n typeof r.retentionRate === 'number'\n )\n}\n\n// ============================================================================\n// Default Values\n// ============================================================================\n\n/**\n * Default retention slice state for store initialization\n */\nexport const defaultRetentionSliceState: RetentionSliceState = {\n retentionCube: null,\n retentionBindingKey: null,\n retentionTimeDimension: null,\n retentionDateRange: getDateRangeFromPreset(DEFAULT_DATE_RANGE_PRESET),\n retentionCohortFilters: [],\n retentionActivityFilters: [],\n retentionBreakdowns: [],\n retentionViewGranularity: 'week',\n retentionPeriods: 12,\n retentionType: 'classic',\n}\n\n/**\n * Minimum and maximum values for retention periods\n */\nexport const RETENTION_MIN_PERIODS = 1\nexport const RETENTION_MAX_PERIODS = 52\n\n/**\n * Available granularity options\n */\nexport const RETENTION_GRANULARITY_OPTIONS: { value: RetentionGranularity; label: string }[] = [\n { value: 'day', label: 'Daily' },\n { value: 'week', label: 'Weekly' },\n { value: 'month', label: 'Monthly' },\n]\n\n/**\n * Available retention type options\n */\nexport const RETENTION_TYPE_OPTIONS: { value: RetentionType; label: string; description: string }[] = [\n {\n value: 'classic',\n label: 'Classic',\n description: 'User was active in exactly period N',\n },\n {\n value: 'rolling',\n label: 'Rolling',\n description: 'User was active in period N or later',\n },\n]\n"],"mappings":"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"}
1
+ {"version":3,"file":"retention-YhT1Oohi.js","names":[],"sources":["../../../src/client/types/retention.ts"],"sourcesContent":["/**\n * Retention Analysis Types\n *\n * Types for Retention analysis mode which tracks cohort-based retention over time.\n * Retention analysis measures what percentage of users from each cohort return\n * in subsequent time periods.\n */\n\nimport type { Filter } from '../types'\nimport type { FunnelBindingKey } from './funnel'\n\n// ============================================================================\n// Date Range Types\n// ============================================================================\n\n/**\n * Date range for cohort analysis (REQUIRED)\n * Matches server's RetentionDateRange interface\n */\nexport interface DateRange {\n /** Start date (inclusive), ISO 8601 format (YYYY-MM-DD) */\n start: string\n /** End date (inclusive), ISO 8601 format (YYYY-MM-DD) */\n end: string\n}\n\n/**\n * Preset date range type\n */\nexport type DateRangePreset =\n | 'last_30_days'\n | 'last_3_months'\n | 'last_6_months'\n | 'last_12_months'\n | 'this_year'\n | 'last_year'\n | 'custom'\n\n/**\n * Preset date range options for the UI\n */\nexport const RETENTION_DATE_RANGE_PRESETS: { value: DateRangePreset; label: string }[] = [\n { value: 'last_30_days', label: 'Last 30 days' },\n { value: 'last_3_months', label: 'Last 3 months' },\n { value: 'last_6_months', label: 'Last 6 months' },\n { value: 'last_12_months', label: 'Last 12 months' },\n { value: 'this_year', label: 'This year' },\n { value: 'last_year', label: 'Last year' },\n { value: 'custom', label: 'Custom range' },\n]\n\n/**\n * Default preset for retention date range\n */\nexport const DEFAULT_DATE_RANGE_PRESET: DateRangePreset = 'last_3_months'\n\n/**\n * Calculate date range from a preset value\n * Returns ISO date strings (YYYY-MM-DD)\n */\nexport function getDateRangeFromPreset(preset: DateRangePreset): DateRange {\n const now = new Date()\n const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n\n switch (preset) {\n case 'last_30_days': {\n const start = new Date(today)\n start.setDate(start.getDate() - 30)\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(today),\n }\n }\n case 'last_3_months': {\n // Start of 3 months ago to end of last month\n const start = new Date(today.getFullYear(), today.getMonth() - 3, 1)\n const end = new Date(today.getFullYear(), today.getMonth(), 0) // Last day of previous month\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(end),\n }\n }\n case 'last_6_months': {\n const start = new Date(today.getFullYear(), today.getMonth() - 6, 1)\n const end = new Date(today.getFullYear(), today.getMonth(), 0)\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(end),\n }\n }\n case 'last_12_months': {\n const start = new Date(today.getFullYear(), today.getMonth() - 12, 1)\n const end = new Date(today.getFullYear(), today.getMonth(), 0)\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(end),\n }\n }\n case 'this_year': {\n const start = new Date(today.getFullYear(), 0, 1)\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(today),\n }\n }\n case 'last_year': {\n const start = new Date(today.getFullYear() - 1, 0, 1)\n const end = new Date(today.getFullYear() - 1, 11, 31)\n return {\n start: formatDateToISO(start),\n end: formatDateToISO(end),\n }\n }\n case 'custom':\n default:\n // For custom, return last 3 months as fallback\n return getDateRangeFromPreset('last_3_months')\n }\n}\n\n/**\n * Format a Date object to ISO date string (YYYY-MM-DD)\n */\nfunction formatDateToISO(date: Date): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n return `${year}-${month}-${day}`\n}\n\n/**\n * Detect which preset a date range matches, if any\n */\nexport function detectDateRangePreset(range: DateRange): DateRangePreset {\n for (const preset of RETENTION_DATE_RANGE_PRESETS) {\n if (preset.value === 'custom') continue\n const presetRange = getDateRangeFromPreset(preset.value)\n if (presetRange.start === range.start && presetRange.end === range.end) {\n return preset.value\n }\n }\n return 'custom'\n}\n\n// ============================================================================\n// Server Query Format\n// ============================================================================\n\n/**\n * Server retention query format\n * This is the shape sent to the server for execution\n * Wrapped in { retention: {...} } similar to funnel queries\n */\nexport interface ServerRetentionQuery {\n retention: RetentionQueryConfig\n}\n\n/**\n * Retention query configuration\n * Contains all parameters needed for server-side retention analysis\n *\n * Simplified Mixpanel-style format:\n * - Single cube and timestamp dimension\n * - Single cohort (date range defines the cohort, not granularity)\n * - Optional breakdown dimension for segmentation\n */\nexport interface RetentionQueryConfig {\n /**\n * Single timestamp dimension for the analysis.\n * String format (e.g., 'Events.timestamp'),\n * Object format for multi-cube with explicit cube reference.\n */\n timeDimension: string | { cube: string; dimension: string }\n\n /**\n * Binding key - dimension that links users across events.\n * This is typically a user ID or other entity identifier.\n * String for single-cube (e.g., 'Events.userId'),\n * Array for multi-cube with different column names per cube.\n */\n bindingKey: string | { cube: string; dimension: string }[]\n\n /**\n * Date range for cohort analysis (REQUIRED).\n * Users who first performed the cohort action within this range are included.\n */\n dateRange: DateRange\n\n /**\n * Granularity for viewing retention periods.\n * Determines how retention periods are measured (day/week/month).\n */\n granularity: RetentionGranularity\n\n /**\n * Number of periods to calculate (e.g., 12 for 12 weeks).\n * Period 0 is always the cohort entry period.\n */\n periods: number\n\n /**\n * Retention type:\n * - 'classic': User returned exactly in period N (bounded)\n * - 'rolling': User returned in period N or any later period (unbounded)\n */\n retentionType: RetentionType\n\n /**\n * Optional filters on cohort entry events.\n * Applied when identifying which users enter the cohort.\n */\n cohortFilters?: Filter | Filter[]\n\n /**\n * Optional filters on return activity events.\n * Applied when checking for user activity in each period.\n */\n activityFilters?: Filter | Filter[]\n\n /**\n * Optional breakdown dimensions for segmenting the cohort.\n * When provided, retention is calculated per breakdown value combination.\n * e.g., [\"Events.country\"] or [\"Events.country\", \"Events.plan\"]\n */\n breakdownDimensions?: string[]\n}\n\n// ============================================================================\n// Enums and Unions\n// ============================================================================\n\n/**\n * Supported granularity levels for retention analysis\n */\nexport type RetentionGranularity = 'day' | 'week' | 'month'\n\n/**\n * Retention calculation types\n * - classic: User active exactly in period N\n * - rolling: User active in period N or any later period\n */\nexport type RetentionType = 'classic' | 'rolling'\n\n// ============================================================================\n// Result Types\n// ============================================================================\n\n/**\n * Single retention data point returned from server\n * Results are returned as a flat array; client transforms to matrix if needed\n */\nexport interface RetentionResultRow {\n /** Period number (0 = cohort entry, 1 = first retention period, etc.) */\n period: number\n\n /** Number of users in the cohort */\n cohortSize: number\n\n /** Number of users retained in this period */\n retainedUsers: number\n\n /** Retention rate as decimal (0-1), e.g., 0.45 for 45% */\n retentionRate: number\n\n /** Breakdown value when breakdown dimension is specified (e.g., \"US\", \"UK\") */\n breakdownValue?: string | null\n}\n\n/**\n * Retention chart data format for visualization\n * Supports both heatmap and line chart modes\n */\nexport interface RetentionChartData {\n rows: RetentionResultRow[]\n /** Period numbers (0 to periods) */\n periods: number[]\n /** Breakdown values when breakdown dimension is specified */\n breakdownValues?: string[]\n /** Summary statistics */\n summary?: RetentionSummary\n /** Granularity of retention periods (day/week/month) for period label formatting */\n granularity?: RetentionGranularity\n /** Human-readable label extracted from the binding key dimension (e.g., \"userId\" from \"Users.userId\") */\n bindingKeyLabel?: string\n}\n\n/**\n * Summary statistics for retention analysis\n */\nexport interface RetentionSummary {\n /** Total unique users in the cohort */\n totalUsers: number\n /** Average retention rate across all periods for period 1 */\n avgPeriod1Retention: number\n /** Highest retention rate for period 1 */\n maxPeriod1Retention: number\n /** Lowest retention rate for period 1 */\n minPeriod1Retention: number\n /** Number of breakdown segments (1 if no breakdown) */\n segmentCount?: number\n}\n\n// ============================================================================\n// Breakdown Types\n// ============================================================================\n\n/**\n * Breakdown item for retention analysis (single dimension)\n * Follows Mixpanel pattern - one breakdown dimension only\n */\nexport interface RetentionBreakdownItem {\n /** Full dimension name (e.g., \"Events.country\") */\n field: string\n /** Display label for the dimension */\n label?: string\n}\n\n// ============================================================================\n// Slice State (for Zustand store)\n// ============================================================================\n\n/**\n * Retention mode state for the AnalysisBuilder store\n * Simplified Mixpanel-style with single global configuration\n *\n * Key simplifications from previous version:\n * - Single cube for all (no separate cohort/activity cubes)\n * - Single timestamp dimension\n * - Single cohort with breakdown support (no cohort explosion)\n * - Granularity = viewing periods only\n */\nexport interface RetentionSliceState {\n /** Single cube for retention analysis */\n retentionCube: string | null\n\n /** Binding key that identifies entities (reuses funnel binding key type) */\n retentionBindingKey: FunnelBindingKey | null\n\n /** Single timestamp dimension for both cohort entry and activity */\n retentionTimeDimension: string | null\n\n /** Date range for cohort analysis (REQUIRED) */\n retentionDateRange: DateRange\n\n /** Filters that define who enters the cohort */\n retentionCohortFilters: Filter[]\n\n /** Filters that define what counts as a return */\n retentionActivityFilters: Filter[]\n\n /** Optional breakdown dimensions for segmenting the cohort */\n retentionBreakdowns: RetentionBreakdownItem[]\n\n /** Granularity for viewing retention periods (day/week/month) */\n retentionViewGranularity: RetentionGranularity\n\n /** Number of periods to analyze (1-52) */\n retentionPeriods: number\n\n /** Type of retention calculation */\n retentionType: RetentionType\n}\n\n/**\n * Retention slice actions for the store\n */\nexport interface RetentionSliceActions {\n /** Set the single cube for retention analysis */\n setRetentionCube: (cube: string | null) => void\n /** Set the retention binding key */\n setRetentionBindingKey: (key: FunnelBindingKey | null) => void\n /** Set the single timestamp dimension */\n setRetentionTimeDimension: (dim: string | null) => void\n /** Set the date range (REQUIRED) */\n setRetentionDateRange: (range: DateRange) => void\n /** Set all cohort filters at once */\n setRetentionCohortFilters: (filters: Filter[]) => void\n /** Add a cohort filter */\n addRetentionCohortFilter: (filter: Filter) => void\n /** Remove a cohort filter by index */\n removeRetentionCohortFilter: (index: number) => void\n /** Update a cohort filter by index */\n updateRetentionCohortFilter: (index: number, filter: Filter) => void\n /** Set all activity filters at once */\n setRetentionActivityFilters: (filters: Filter[]) => void\n /** Add an activity filter */\n addRetentionActivityFilter: (filter: Filter) => void\n /** Remove an activity filter by index */\n removeRetentionActivityFilter: (index: number) => void\n /** Update an activity filter by index */\n updateRetentionActivityFilter: (index: number, filter: Filter) => void\n /** Set all breakdown dimensions */\n setRetentionBreakdowns: (breakdowns: RetentionBreakdownItem[]) => void\n /** Add a breakdown dimension */\n addRetentionBreakdown: (breakdown: RetentionBreakdownItem) => void\n /** Remove a breakdown dimension by field name */\n removeRetentionBreakdown: (field: string) => void\n /** Set the view granularity */\n setRetentionViewGranularity: (granularity: RetentionGranularity) => void\n /** Set the number of periods */\n setRetentionPeriods: (periods: number) => void\n /** Set the retention type */\n setRetentionType: (type: RetentionType) => void\n /** Check if in retention mode (analysisType === 'retention') */\n isRetentionMode: () => boolean\n /** Check if retention mode is properly configured and ready for execution */\n isRetentionModeEnabled: () => boolean\n /** Build ServerRetentionQuery from retention state */\n buildRetentionQuery: () => ServerRetentionQuery | null\n /** Get validation errors explaining why retention query cannot be built */\n getRetentionValidation: () => { isValid: boolean; errors: string[]; warnings: string[] }\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Type guard to check if data is retention chart data\n */\nexport function isRetentionData(data: unknown): data is RetentionChartData {\n if (!data || typeof data !== 'object') return false\n const d = data as Record<string, unknown>\n return (\n Array.isArray(d.rows) &&\n Array.isArray(d.periods)\n )\n}\n\n/**\n * Type guard to detect server retention query format\n * Used to distinguish { retention: {...} } from CubeQuery, etc.\n */\nexport function isServerRetentionQuery(obj: unknown): obj is ServerRetentionQuery {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'retention' in obj &&\n typeof (obj as { retention: unknown }).retention === 'object'\n )\n}\n\n/**\n * Type guard for retention result row\n */\nexport function isRetentionResultRow(row: unknown): row is RetentionResultRow {\n if (!row || typeof row !== 'object') return false\n const r = row as Record<string, unknown>\n return (\n typeof r.cohortPeriod === 'string' &&\n typeof r.period === 'number' &&\n typeof r.cohortSize === 'number' &&\n typeof r.retainedUsers === 'number' &&\n typeof r.retentionRate === 'number'\n )\n}\n\n// ============================================================================\n// Default Values\n// ============================================================================\n\n/**\n * Default retention slice state for store initialization\n */\nexport const defaultRetentionSliceState: RetentionSliceState = {\n retentionCube: null,\n retentionBindingKey: null,\n retentionTimeDimension: null,\n retentionDateRange: getDateRangeFromPreset(DEFAULT_DATE_RANGE_PRESET),\n retentionCohortFilters: [],\n retentionActivityFilters: [],\n retentionBreakdowns: [],\n retentionViewGranularity: 'week',\n retentionPeriods: 12,\n retentionType: 'classic',\n}\n\n/**\n * Minimum and maximum values for retention periods\n */\nexport const RETENTION_MIN_PERIODS = 1\nexport const RETENTION_MAX_PERIODS = 52\n\n/**\n * Available granularity options\n */\nexport const RETENTION_GRANULARITY_OPTIONS: { value: RetentionGranularity; label: string }[] = [\n { value: 'day', label: 'Daily' },\n { value: 'week', label: 'Weekly' },\n { value: 'month', label: 'Monthly' },\n]\n\n/**\n * Available retention type options\n */\nexport const RETENTION_TYPE_OPTIONS: { value: RetentionType; label: string; description: string }[] = [\n {\n value: 'classic',\n label: 'Classic',\n description: 'User was active in exactly period N',\n },\n {\n value: 'rolling',\n label: 'Rolling',\n description: 'User was active in period N or later',\n },\n]\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { n as e } from "./rolldown-runtime-lc2dmIiU.js";
2
- import { c as t, d as n } from "./chart-data-table-CW_qZDpt.js";
2
+ import { A as t, c as n } from "./chart-data-table-D4s27-U3.js";
3
3
  import { Suspense as r, createContext as i, lazy as a, useCallback as o, useContext as s, useEffect as c, useMemo as l, useRef as u, useState as d } from "react";
4
4
  import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
5
5
  //#region src/client/components/SchemaVisualization/xyflowContext.tsx
@@ -74,12 +74,12 @@ function S(e) {
74
74
  //#endregion
75
75
  //#region src/client/components/SchemaVisualization/CubeNode.tsx
76
76
  function C({ data: e }) {
77
- let { Handle: t, Position: r } = _(), { cube: i, onFieldClick: a, onCubeClick: o, isHighlighted: s, highlightedFields: c, searchTerm: l, selectedField: u } = e, d = (e, t, n) => {
77
+ let { Handle: n, Position: r } = _(), { cube: i, onFieldClick: a, onCubeClick: o, isHighlighted: s, highlightedFields: c, searchTerm: l, selectedField: u } = e, d = (e, t, n) => {
78
78
  a && a(i.name, t, n, {
79
79
  x: e.clientX,
80
80
  y: e.clientY
81
81
  });
82
- }, f = n("info"), h = (e) => {
82
+ }, f = t("info"), h = (e) => {
83
83
  e.stopPropagation(), o && o(i.name, {
84
84
  x: e.clientX,
85
85
  y: e.clientY
@@ -233,28 +233,28 @@ function C({ data: e }) {
233
233
  })
234
234
  })]
235
235
  }),
236
- /* @__PURE__ */ p(t, {
236
+ /* @__PURE__ */ p(n, {
237
237
  type: "source",
238
238
  position: r.Right,
239
239
  id: "right",
240
240
  className: "dc:opacity-0",
241
241
  isConnectable: !1
242
242
  }),
243
- /* @__PURE__ */ p(t, {
243
+ /* @__PURE__ */ p(n, {
244
244
  type: "target",
245
245
  position: r.Left,
246
246
  id: "left",
247
247
  className: "dc:opacity-0",
248
248
  isConnectable: !1
249
249
  }),
250
- /* @__PURE__ */ p(t, {
250
+ /* @__PURE__ */ p(n, {
251
251
  type: "source",
252
252
  position: r.Bottom,
253
253
  id: "bottom",
254
254
  className: "dc:opacity-0",
255
255
  isConnectable: !1
256
256
  }),
257
- /* @__PURE__ */ p(t, {
257
+ /* @__PURE__ */ p(n, {
258
258
  type: "target",
259
259
  position: r.Top,
260
260
  id: "top",
@@ -556,8 +556,8 @@ function k({ cube: e }) {
556
556
  ]
557
557
  });
558
558
  }
559
- function ee({ selection: e, meta: t, onClose: r }) {
560
- let i = n("close"), a = t.cubes.find((t) => t.name === e.cubeName);
559
+ function ee({ selection: e, meta: n, onClose: r }) {
560
+ let i = t("close"), a = n.cubes.find((t) => t.name === e.cubeName);
561
561
  if (!a) return null;
562
562
  let o, s, c, l;
563
563
  if (e.fieldType === "cube" || !e.fieldName) o = a.title || a.name, s = "var(--dc-accent)", c = "color-mix(in srgb, var(--dc-accent) 10%, var(--dc-surface))", l = /* @__PURE__ */ p(k, { cube: a });
@@ -778,7 +778,7 @@ function oe(e) {
778
778
  }
779
779
  }
780
780
  function B({ className: e = "", onFieldClick: r, highlightedCubes: i, highlightedFields: a, searchTerm: s, height: f = "100%" }) {
781
- let { ReactFlow: h, Controls: g, MiniMap: v, Background: y, applyNodeChanges: b } = _(), { meta: x, metaLoading: S, metaError: C } = t(), w = i || z, T = a || z, [E, D] = d(null), [O, k] = d(s || ""), [te, A] = d(0), [j, M] = d(null), [N, P] = d(null), F = u(null), I = !r, [L, R] = d({}), [B, se] = d(!1), ce = n("search"), le = n("close"), V = s === void 0 ? O : s;
781
+ let { ReactFlow: h, Controls: g, MiniMap: v, Background: y, applyNodeChanges: b } = _(), { meta: x, metaLoading: S, metaError: C } = n(), w = i || z, T = a || z, [E, D] = d(null), [O, k] = d(s || ""), [te, A] = d(0), [j, M] = d(null), [N, P] = d(null), F = u(null), I = !r, [L, R] = d({}), [B, se] = d(!1), ce = t("search"), le = t("close"), V = s === void 0 ? O : s;
782
782
  c(() => {
783
783
  try {
784
784
  let e = localStorage.getItem("drizzle-cube-erd-node-positions");
@@ -1076,4 +1076,4 @@ function B({ className: e = "", onFieldClick: r, highlightedCubes: i, highlighte
1076
1076
  //#endregion
1077
1077
  export { S as n, R as t };
1078
1078
 
1079
- //# sourceMappingURL=schema-visualization-CwiEtwdu.js.map
1079
+ //# sourceMappingURL=schema-visualization-BJ8HrNqB.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-visualization-CwiEtwdu.js","names":[],"sources":["../../../src/client/components/SchemaVisualization/xyflowContext.tsx","../../../src/client/components/SchemaVisualization/SchemaVisualizationLazy.tsx","../../../src/client/components/SchemaVisualization/CubeNode.tsx","../../../src/client/components/SchemaVisualization/RelationshipEdge.tsx","../../../src/client/components/SchemaVisualization/FieldDetailPanel.tsx","../../../src/client/components/SchemaVisualization/useERDLayout.ts","../../../src/client/components/SchemaVisualization/index.tsx"],"sourcesContent":["/**\n * React context for @xyflow/react modules.\n *\n * All xyflow imports are dynamic (via SchemaVisualizationLazy) so that\n * the built schema-visualization chunk has NO static imports from\n * @xyflow/react. This prevents consuming projects from failing at\n * build time when @xyflow/react is not installed.\n */\n\nimport { createContext, useContext } from 'react'\n\nexport type XyflowModule = typeof import('@xyflow/react')\n\nconst XyflowContext = createContext<XyflowModule | null>(null)\n\nexport const XyflowProvider = XyflowContext.Provider\n\nexport function useXyflow(): XyflowModule {\n const ctx = useContext(XyflowContext)\n if (!ctx) throw new Error('useXyflow must be used within XyflowProvider')\n return ctx\n}\n","/**\n * Lazy-loaded SchemaVisualization with graceful fallback\n * when @xyflow/react is not installed.\n *\n * All @xyflow/react imports are dynamic here so the built chunk\n * has NO static imports from @xyflow/react. This prevents consuming\n * projects from failing at build time when xyflow is not installed.\n */\n\nimport { lazy, Suspense, useState, useEffect } from 'react'\nimport type { SchemaVisualizationProps } from './index'\nimport { XyflowProvider } from './xyflowContext'\nimport type { XyflowModule } from './xyflowContext'\n\nlet loadFailed = false\n\nfunction MissingDependencyFallback(_props: SchemaVisualizationProps) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:h-full dc:p-8\">\n <div className=\"dc:text-center dc:max-w-md\">\n <div className=\"dc:text-4xl dc:mb-4\">&#128269;</div>\n <h3 className=\"dc:text-lg dc:font-semibold text-dc-text dc:mb-2\">\n Schema Visualization requires additional packages\n </h3>\n <p className=\"dc:text-sm text-dc-text-secondary dc:mb-4\">\n Install the required dependencies to enable the interactive schema diagram:\n </p>\n <code className=\"dc:block dc:px-4 dc:py-2 dc:rounded bg-dc-surface-secondary dc:text-sm dc:font-mono text-dc-text dc:border border-dc-border\">\n npm install @xyflow/react elkjs\n </code>\n </div>\n </div>\n )\n}\n\nfunction LoadingFallback() {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:h-full\">\n <div className=\"dc:text-center\">\n <div className=\"dc:animate-spin dc:rounded-full dc:h-8 dc:w-8 dc:border-b-2 border-dc-accent dc:mx-auto dc:mb-2\" />\n <p className=\"dc:text-sm text-dc-text-muted\">Loading schema visualization...</p>\n </div>\n </div>\n )\n}\n\nconst LazySchemaVisualization = lazy(async () => {\n const mod = await import('./index')\n return { default: mod.SchemaVisualization }\n})\n\nexport function SchemaVisualizationLazy(props: SchemaVisualizationProps) {\n const [xyflow, setXyflow] = useState<XyflowModule | null>(null)\n const [failed, setFailed] = useState(loadFailed)\n\n useEffect(() => {\n if (loadFailed) return\n let cancelled = false\n\n // Dynamic specifier so webpack/Next.js won't try to resolve this optional dependency at build time\n const specifier = ['@xyflow', 'react'].join('/')\n import(/* @vite-ignore */ specifier)\n .then((mod) => {\n if (!cancelled) setXyflow(mod as XyflowModule)\n })\n .catch(() => {\n loadFailed = true\n if (!cancelled) setFailed(true)\n })\n\n return () => { cancelled = true }\n }, [])\n\n if (failed) {\n return <MissingDependencyFallback {...props} />\n }\n\n if (!xyflow) {\n return <LoadingFallback />\n }\n\n return (\n <XyflowProvider value={xyflow}>\n <Suspense fallback={<LoadingFallback />}>\n <LazySchemaVisualization {...props} />\n </Suspense>\n </XyflowProvider>\n )\n}\n\nexport function isSchemaVisualizationAvailable(): boolean {\n return !loadFailed\n}\n","import type { MouseEvent } from 'react'\nimport type { CubeMetaCube } from '../../types'\nimport { getIcon } from '../../icons'\nimport { useXyflow } from './xyflowContext'\n\ninterface CubeNodeData {\n cube: CubeMetaCube\n onFieldClick?: (cubeName: string, fieldName: string, fieldType: 'measure' | 'dimension', pos?: { x: number; y: number }) => void\n onCubeClick?: (cubeName: string, pos?: { x: number; y: number }) => void\n isHighlighted: boolean\n highlightedFields: string[]\n searchTerm?: string\n selectedField?: { cubeName: string; fieldName: string | null } | null\n [key: string]: unknown\n}\n\ninterface CubeNodeProps {\n data: CubeNodeData\n}\n\nexport function CubeNode({ data }: CubeNodeProps) {\n const { Handle, Position } = useXyflow()\n const { cube, onFieldClick, onCubeClick, isHighlighted, highlightedFields, searchTerm, selectedField } = data\n\n const handleFieldClick = (e: MouseEvent, fieldName: string, fieldType: 'measure' | 'dimension') => {\n if (onFieldClick) {\n onFieldClick(cube.name, fieldName, fieldType, { x: e.clientX, y: e.clientY })\n }\n }\n\n const InfoIcon = getIcon('info')\n\n const handleCubeInfoClick = (e: MouseEvent) => {\n e.stopPropagation() // don't start drag\n if (onCubeClick) {\n onCubeClick(cube.name, { x: e.clientX, y: e.clientY })\n }\n }\n\n const isFieldSelected = (fieldName: string) => {\n if (!selectedField) return false\n return selectedField.cubeName === cube.name && selectedField.fieldName === fieldName\n }\n\n const isCubeSelected = selectedField?.cubeName === cube.name && selectedField?.fieldName === null\n\n const isFieldHighlighted = (fullFieldName: string) => {\n return highlightedFields.includes(fullFieldName)\n }\n\n const isFieldSearchMatch = (field: { name: string; title?: string }) => {\n if (!searchTerm?.trim()) return true\n const term = searchTerm.toLowerCase()\n return (\n field.name.toLowerCase().includes(term) ||\n (field.title && field.title.toLowerCase().includes(term))\n )\n }\n\n const cubeHasMatches = () => {\n if (!searchTerm?.trim()) return true\n return cube.measures.some(m => isFieldSearchMatch(m)) ||\n cube.dimensions.some(d => isFieldSearchMatch(d))\n }\n\n const hasCubeMatches = cubeHasMatches()\n\n const getFieldClasses = (\n field: { name: string; title?: string; type?: string },\n highlighted: boolean,\n _fieldType: 'measure' | 'dimension'\n ) => {\n const fieldName = field.name.split('.')[1] || field.name\n const selected = isFieldSelected(fieldName)\n const base = 'dc:px-4 dc:py-2 dc:text-xs dc:cursor-pointer dc:transition-all dc:border-b border-dc-border last:dc:border-b-0 nodrag nopan'\n\n if (selected) {\n return `${base} bg-dc-accent-bg text-dc-accent dc:font-semibold dc:ring-1 dc:ring-inset ring-dc-accent`\n }\n\n if (!hasCubeMatches && searchTerm?.trim()) {\n if (highlighted) return `${base} bg-dc-accent-bg text-dc-accent dc:font-semibold`\n return `${base} dc:hover:bg-dc-surface-hover text-dc-text-secondary`\n }\n\n if (searchTerm?.trim() && !isFieldSearchMatch(field)) {\n return `${base} dc:opacity-40 dc:hover:opacity-60 text-dc-text-muted`\n }\n\n if (searchTerm?.trim() && isFieldSearchMatch(field) && !highlighted) {\n return `${base} dc:font-bold dc:hover:bg-dc-accent-bg`\n }\n\n if (highlighted) {\n return `${base} bg-dc-accent-bg text-dc-accent dc:font-semibold`\n }\n\n return `${base} dc:hover:bg-dc-surface-hover text-dc-text-secondary`\n }\n\n const timeDimensions = cube.dimensions.filter(d => d.type === 'time')\n const regularDimensions = cube.dimensions.filter(d => d.type !== 'time')\n\n return (\n <div\n className={`\n dc:border-2 dc:rounded-lg dc:shadow-lg dc:min-w-[280px] dc:overflow-hidden dc:transition-all\n ${!hasCubeMatches && searchTerm?.trim() ? 'dc:opacity-30 dc:grayscale' : ''}\n ${isHighlighted ? 'border-dc-accent dc:ring-2 ring-dc-accent' : 'border-dc-border'}\n `}\n style={{ backgroundColor: 'var(--dc-surface)' }}\n >\n {/* Cube Header */}\n <div className={`dc:px-4 dc:py-3 dc:transition-colors ${\n isHighlighted ? 'bg-dc-accent-bg' : 'bg-dc-surface-secondary'\n }`}>\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <div>\n <h3 className=\"dc:font-semibold text-dc-text dc:text-sm\">\n {cube.title || cube.name}\n </h3>\n {cube.description && (\n <p className=\"dc:text-xs text-dc-text-muted dc:mt-1 dc:line-clamp-2\">{cube.description}</p>\n )}\n </div>\n {onCubeClick && (\n <button\n className={`dc:ml-2 dc:p-1 dc:rounded dc:transition-colors nodrag nopan ${\n isCubeSelected\n ? 'bg-dc-accent-bg text-dc-accent'\n : 'text-dc-text-muted dc:hover:text-dc-text dc:hover:bg-dc-surface-hover'\n }`}\n onClick={handleCubeInfoClick}\n title=\"Cube info\"\n >\n <InfoIcon className=\"dc:w-5 dc:h-5\" />\n </button>\n )}\n {!onCubeClick && (\n <div className=\"dc:text-xs text-dc-text-muted dc:ml-2\">\n <div>{cube.measures.length}M</div>\n <div>{cube.dimensions.length}D</div>\n </div>\n )}\n </div>\n </div>\n\n {/* Measures */}\n {cube.measures.length > 0 && (\n <div className=\"dc:border-t border-dc-border\">\n <div className=\"dc:px-4 dc:py-1.5 dc:border-b border-dc-border\" style={{ backgroundColor: 'color-mix(in srgb, var(--dc-warning) 10%, var(--dc-surface))' }}>\n <h4 className=\"dc:text-xs dc:font-medium text-dc-text-secondary dc:flex dc:items-center\">\n <span className=\"dc:w-2 dc:h-2 bg-dc-warning dc:rounded-full dc:mr-2\" />\n Measures ({cube.measures.length})\n </h4>\n </div>\n <div className=\"dc:max-h-64 dc:overflow-y-auto nowheel\">\n {cube.measures.map((measure) => {\n const fieldName = measure.name.split('.')[1] || measure.name\n const highlighted = isFieldHighlighted(measure.name)\n return (\n <div\n key={measure.name}\n className={getFieldClasses(measure, highlighted, 'measure')}\n onClick={(e) => handleFieldClick(e, fieldName, 'measure')}\n title={measure.title}\n >\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:font-mono dc:truncate\">\n {measure.shortTitle || measure.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted dc:ml-2 dc:text-[10px] dc:uppercase\">\n {measure.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Time Dimensions */}\n {timeDimensions.length > 0 && (\n <div className=\"dc:border-t border-dc-border\">\n <div className=\"dc:px-4 dc:py-1.5 dc:border-b border-dc-border\" style={{ backgroundColor: 'color-mix(in srgb, var(--dc-accent) 10%, var(--dc-surface))' }}>\n <h4 className=\"dc:text-xs dc:font-medium text-dc-text-secondary dc:flex dc:items-center\">\n <span className=\"dc:w-2 dc:h-2 bg-dc-accent dc:rounded-full dc:mr-2\" />\n Time Dimensions ({timeDimensions.length})\n </h4>\n </div>\n <div className=\"dc:max-h-64 dc:overflow-y-auto nowheel\">\n {timeDimensions.map((dimension) => {\n const fieldName = dimension.name.split('.')[1] || dimension.name\n const highlighted = isFieldHighlighted(dimension.name)\n return (\n <div\n key={dimension.name}\n className={getFieldClasses(dimension, highlighted, 'dimension')}\n onClick={(e) => handleFieldClick(e, fieldName, 'dimension')}\n title={dimension.title}\n >\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:font-mono dc:truncate\">\n {dimension.shortTitle || dimension.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted dc:ml-2 dc:text-[10px] dc:uppercase\">\n {dimension.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Regular Dimensions */}\n {regularDimensions.length > 0 && (\n <div className=\"dc:border-t border-dc-border\">\n <div className=\"dc:px-4 dc:py-1.5 dc:border-b border-dc-border\" style={{ backgroundColor: 'color-mix(in srgb, var(--dc-success) 10%, var(--dc-surface))' }}>\n <h4 className=\"dc:text-xs dc:font-medium text-dc-text-secondary dc:flex dc:items-center\">\n <span className=\"dc:w-2 dc:h-2 bg-dc-success dc:rounded-full dc:mr-2\" />\n Dimensions ({regularDimensions.length})\n </h4>\n </div>\n <div className=\"dc:max-h-64 dc:overflow-y-auto nowheel\">\n {regularDimensions.map((dimension) => {\n const fieldName = dimension.name.split('.')[1] || dimension.name\n const highlighted = isFieldHighlighted(dimension.name)\n return (\n <div\n key={dimension.name}\n className={getFieldClasses(dimension, highlighted, 'dimension')}\n onClick={(e) => handleFieldClick(e, fieldName, 'dimension')}\n title={dimension.title}\n >\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:font-mono dc:truncate\">\n {dimension.shortTitle || dimension.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted dc:ml-2 dc:text-[10px] dc:uppercase\">\n {dimension.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Connection handles - hidden */}\n <Handle type=\"source\" position={Position.Right} id=\"right\" className=\"dc:opacity-0\" isConnectable={false} />\n <Handle type=\"target\" position={Position.Left} id=\"left\" className=\"dc:opacity-0\" isConnectable={false} />\n <Handle type=\"source\" position={Position.Bottom} id=\"bottom\" className=\"dc:opacity-0\" isConnectable={false} />\n <Handle type=\"target\" position={Position.Top} id=\"top\" className=\"dc:opacity-0\" isConnectable={false} />\n </div>\n )\n}\n","import type { Edge, EdgeProps } from '@xyflow/react'\nimport type { CubeMetaRelationship } from '../../types'\nimport { useXyflow } from './xyflowContext'\n\ninterface RelationshipEdgeData {\n relationship: CubeMetaRelationship\n joinFields: Array<{\n sourceField: string\n targetField: string\n }>\n [key: string]: unknown\n}\n\nexport type RelationshipEdgeType = Edge<RelationshipEdgeData, 'relationshipEdge'>\n\nexport function RelationshipEdge({\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n style = {},\n data,\n markerEnd,\n}: EdgeProps<RelationshipEdgeType>) {\n const { getBezierPath, BaseEdge, EdgeLabelRenderer } = useXyflow()\n const [edgePath, labelX, labelY] = getBezierPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n })\n\n if (!data) return null\n\n const { relationship, joinFields } = data\n\n const getSymbol = (rel: string) => {\n switch (rel) {\n case 'belongsTo': return '\\u2208' // belongs to\n case 'hasOne': return '1:1'\n case 'hasMany': return '1:M'\n case 'belongsToMany': return 'M:M'\n default: return '?'\n }\n }\n\n const getColor = (rel: string) => {\n switch (rel) {\n case 'belongsTo': return '#10b981'\n case 'hasOne': return '#3b82f6'\n case 'hasMany': return '#f59e0b'\n case 'belongsToMany': return '#8b5cf6'\n default: return '#6b7280'\n }\n }\n\n const color = getColor(relationship.relationship)\n const symbol = getSymbol(relationship.relationship)\n\n return (\n <>\n <BaseEdge path={edgePath} markerEnd={markerEnd} style={{ ...style, stroke: color }} />\n <EdgeLabelRenderer>\n <div\n style={{\n position: 'absolute',\n transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n fontSize: 10,\n pointerEvents: 'all',\n }}\n className=\"nodrag nopan\"\n >\n <div\n className=\"dc:border-2 dc:rounded-md dc:px-2 dc:py-1 dc:shadow-xs\"\n style={{ backgroundColor: 'var(--dc-surface)', borderColor: color }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:font-bold dc:text-xs dc:mb-1\" style={{ color }}>\n {symbol}\n </div>\n <div className=\"dc:text-[9px] text-dc-text-muted dc:leading-tight\">\n {joinFields.map((field, index) => (\n <div key={index} className=\"dc:font-mono\">\n {field.sourceField} → {field.targetField}\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n </EdgeLabelRenderer>\n </>\n )\n}\n","import type { ReactNode } from 'react'\nimport type { CubeMetaCube, CubeMetaMeasure, CubeMetaDimension } from '../../types'\nimport { getIcon } from '../../icons'\n\nexport interface FieldSelection {\n cubeName: string\n fieldName: string | null // null = cube-level selection\n fieldType: 'measure' | 'dimension' | 'cube'\n}\n\ninterface FieldDetailPanelProps {\n selection: FieldSelection\n meta: { cubes: CubeMetaCube[] }\n onClose: () => void\n}\n\nfunction DetailRow({ label, children }: { label: string; children: ReactNode }) {\n return (\n <div className=\"dc:flex dc:items-start dc:gap-2 dc:text-xs\">\n <span className=\"text-dc-text-muted dc:w-20 dc:flex-shrink-0 dc:font-medium\">{label}</span>\n <span className=\"text-dc-text dc:flex-1 dc:min-w-0\">{children}</span>\n </div>\n )\n}\n\nfunction TypeBadge({ type, color }: { type: string; color: string }) {\n return (\n <span\n className=\"dc:inline-flex dc:items-center dc:px-1.5 dc:py-0.5 dc:rounded dc:text-[10px] dc:font-medium dc:uppercase\"\n style={{ backgroundColor: `color-mix(in srgb, ${color} 15%, var(--dc-surface))`, color }}\n >\n {type}\n </span>\n )\n}\n\nfunction MeasureDetail({ measure, cube }: { measure: CubeMetaMeasure; cube: CubeMetaCube }) {\n const typeColor = {\n count: '#f59e0b',\n countDistinct: '#f59e0b',\n countDistinctApprox: '#f59e0b',\n sum: '#10b981',\n avg: '#3b82f6',\n min: '#8b5cf6',\n max: '#ec4899',\n runningTotal: '#06b6d4',\n number: '#6b7280',\n }[measure.type] || '#6b7280'\n\n return (\n <div className=\"dc:flex dc:flex-col dc:gap-2\">\n <DetailRow label=\"Cube\">{cube.title || cube.name}</DetailRow>\n <DetailRow label=\"Type\"><TypeBadge type={measure.type} color={typeColor} /></DetailRow>\n {measure.title && measure.title !== measure.shortTitle && (\n <DetailRow label=\"Title\">{measure.title}</DetailRow>\n )}\n {measure.drillMembers && measure.drillMembers.length > 0 && (\n <DetailRow label=\"Drill into\">\n <div className=\"dc:flex dc:flex-wrap dc:gap-1\">\n {measure.drillMembers.map(dm => (\n <span key={dm} className=\"dc:font-mono dc:text-[10px] dc:px-1 dc:py-0.5 dc:rounded bg-dc-surface-secondary border-dc-border dc:border\">\n {dm.split('.')[1] || dm}\n </span>\n ))}\n </div>\n </DetailRow>\n )}\n </div>\n )\n}\n\nfunction DimensionDetail({ dimension, cube }: { dimension: CubeMetaDimension; cube: CubeMetaCube }) {\n const typeColor = dimension.type === 'time' ? '#3b82f6' : '#10b981'\n\n // Find hierarchies this dimension belongs to\n const hierarchies = cube.hierarchies?.filter(h =>\n h.levels.some(l => l === dimension.name || l === `${cube.name}.${dimension.name.split('.')[1]}`)\n ) || []\n\n return (\n <div className=\"dc:flex dc:flex-col dc:gap-2\">\n <DetailRow label=\"Cube\">{cube.title || cube.name}</DetailRow>\n <DetailRow label=\"Type\"><TypeBadge type={dimension.type} color={typeColor} /></DetailRow>\n {dimension.title && dimension.title !== dimension.shortTitle && (\n <DetailRow label=\"Title\">{dimension.title}</DetailRow>\n )}\n {dimension.type === 'time' && dimension.granularities && dimension.granularities.length > 0 && (\n <DetailRow label=\"Granularity\">\n <div className=\"dc:flex dc:flex-wrap dc:gap-1\">\n {dimension.granularities.map(g => (\n <span key={g} className=\"dc:font-mono dc:text-[10px] dc:px-1 dc:py-0.5 dc:rounded bg-dc-surface-secondary border-dc-border dc:border\">\n {g}\n </span>\n ))}\n </div>\n </DetailRow>\n )}\n {hierarchies.length > 0 && (\n <DetailRow label=\"Hierarchy\">\n {hierarchies.map(h => (\n <div key={h.name} className=\"dc:text-[10px]\">\n <span className=\"dc:font-medium\">{h.title}</span>\n <span className=\"text-dc-text-muted dc:ml-1\">\n ({h.levels.map(l => l.split('.')[1] || l).join(' > ')})\n </span>\n </div>\n ))}\n </DetailRow>\n )}\n </div>\n )\n}\n\nfunction CubeDetail({ cube }: { cube: CubeMetaCube }) {\n const relationshipColors: Record<string, string> = {\n belongsTo: '#10b981',\n hasOne: '#3b82f6',\n hasMany: '#f59e0b',\n belongsToMany: '#8b5cf6',\n }\n\n return (\n <div className=\"dc:flex dc:flex-col dc:gap-2\">\n {cube.description && (\n <DetailRow label=\"Description\">{cube.description}</DetailRow>\n )}\n <DetailRow label=\"Measures\">\n <span className=\"dc:font-mono\">{cube.measures.length}</span>\n {cube.measures.length > 0 && (\n <span className=\"text-dc-text-muted dc:ml-1\">\n ({[...new Set(cube.measures.map(m => m.type))].join(', ')})\n </span>\n )}\n </DetailRow>\n <DetailRow label=\"Dimensions\">\n <span className=\"dc:font-mono\">{cube.dimensions.length}</span>\n {cube.dimensions.some(d => d.type === 'time') && (\n <span className=\"text-dc-text-muted dc:ml-1\">\n ({cube.dimensions.filter(d => d.type === 'time').length} time)\n </span>\n )}\n </DetailRow>\n {cube.relationships && cube.relationships.length > 0 && (\n <DetailRow label=\"Joins\">\n <div className=\"dc:flex dc:flex-col dc:gap-1\">\n {cube.relationships.map((rel, i) => (\n <div key={i} className=\"dc:flex dc:items-center dc:gap-1.5 dc:text-[10px]\">\n <TypeBadge type={rel.relationship} color={relationshipColors[rel.relationship] || '#6b7280'} />\n <span className=\"dc:font-mono\">{rel.targetCube}</span>\n </div>\n ))}\n </div>\n </DetailRow>\n )}\n {cube.hierarchies && cube.hierarchies.length > 0 && (\n <DetailRow label=\"Hierarchies\">\n <div className=\"dc:flex dc:flex-col dc:gap-1\">\n {cube.hierarchies.map(h => (\n <div key={h.name} className=\"dc:text-[10px]\">\n <span className=\"dc:font-medium\">{h.title}</span>\n <span className=\"text-dc-text-muted dc:ml-1\">\n ({h.levels.map(l => l.split('.')[1] || l).join(' > ')})\n </span>\n </div>\n ))}\n </div>\n </DetailRow>\n )}\n {cube.meta?.eventStream && (\n <DetailRow label=\"Event Stream\">\n <div className=\"dc:text-[10px]\">\n <span className=\"text-dc-text-muted\">binding: </span>\n <span className=\"dc:font-mono\">{cube.meta.eventStream.bindingKey.split('.')[1] || cube.meta.eventStream.bindingKey}</span>\n <span className=\"text-dc-text-muted dc:ml-2\">time: </span>\n <span className=\"dc:font-mono\">{cube.meta.eventStream.timeDimension.split('.')[1] || cube.meta.eventStream.timeDimension}</span>\n </div>\n </DetailRow>\n )}\n </div>\n )\n}\n\nexport function FieldDetailPanel({ selection, meta, onClose }: FieldDetailPanelProps) {\n const CloseIcon = getIcon('close')\n\n const cube = meta.cubes.find(c => c.name === selection.cubeName)\n if (!cube) return null\n\n // Determine what to show\n let title: string\n let dotColor: string\n let sectionBgColor: string\n let content: ReactNode\n\n if (selection.fieldType === 'cube' || !selection.fieldName) {\n title = cube.title || cube.name\n dotColor = 'var(--dc-accent)'\n sectionBgColor = 'color-mix(in srgb, var(--dc-accent) 10%, var(--dc-surface))'\n content = <CubeDetail cube={cube} />\n } else if (selection.fieldType === 'measure') {\n const measure = cube.measures.find(m => {\n const mField = m.name.split('.')[1] || m.name\n return mField === selection.fieldName || m.name === selection.fieldName\n })\n if (!measure) return null\n title = measure.shortTitle || measure.title || selection.fieldName\n dotColor = 'var(--dc-warning)'\n sectionBgColor = 'color-mix(in srgb, var(--dc-warning) 10%, var(--dc-surface))'\n content = <MeasureDetail measure={measure} cube={cube} />\n } else {\n const dimension = cube.dimensions.find(d => {\n const dField = d.name.split('.')[1] || d.name\n return dField === selection.fieldName || d.name === selection.fieldName\n })\n if (!dimension) return null\n title = dimension.shortTitle || dimension.title || selection.fieldName\n dotColor = dimension.type === 'time' ? 'var(--dc-accent)' : 'var(--dc-success)'\n sectionBgColor = dimension.type === 'time'\n ? 'color-mix(in srgb, var(--dc-accent) 10%, var(--dc-surface))'\n : 'color-mix(in srgb, var(--dc-success) 10%, var(--dc-surface))'\n content = <DimensionDetail dimension={dimension} cube={cube} />\n }\n\n return (\n <div\n className=\"dc:border-2 dc:rounded-lg dc:shadow-lg dc:min-w-[260px] dc:max-w-[320px] dc:overflow-hidden dc:transition-all border-dc-border\"\n style={{ backgroundColor: 'var(--dc-surface)' }}\n >\n {/* Header - same style as CubeNode section headers */}\n <div\n className=\"dc:px-4 dc:py-2.5 dc:border-b border-dc-border dc:flex dc:items-center dc:justify-between\"\n style={{ backgroundColor: sectionBgColor }}\n >\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:min-w-0\">\n <span\n className=\"dc:w-2 dc:h-2 dc:rounded-full dc:flex-shrink-0\"\n style={{ backgroundColor: dotColor }}\n />\n <h4 className=\"dc:text-sm dc:font-semibold text-dc-text dc:truncate\">{title}</h4>\n </div>\n <button\n onClick={onClose}\n className=\"dc:ml-2 dc:flex-shrink-0 text-dc-text-muted dc:hover:text-dc-text dc:transition-colors\"\n >\n <CloseIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"dc:px-4 dc:py-3\">\n {content}\n </div>\n </div>\n )\n}\n","import { useState, useEffect, useRef, useMemo } from 'react'\nimport type { Node, Edge, Position } from '@xyflow/react'\n\nexport interface LayoutOptions {\n direction: 'TB' | 'LR'\n nodeWidth: number\n nodeSep: number\n rankSep: number\n}\n\nexport const defaultLayoutOptions: LayoutOptions = {\n direction: 'LR',\n nodeWidth: 340,\n nodeSep: 150,\n rankSep: 350,\n}\n\n/**\n * Layout state machine phases:\n * waiting → ELK not loaded yet, nothing to show\n * computing → ELK loaded, running layout algorithm\n * ready → layout complete, node positions available\n */\nexport type LayoutPhase = 'waiting' | 'computing' | 'ready'\n\nfunction getTargetPosition(direction: string): Position {\n switch (direction) {\n case 'TB': return 'top' as Position\n case 'LR': return 'left' as Position\n default: return 'top' as Position\n }\n}\n\nfunction getSourcePosition(direction: string): Position {\n switch (direction) {\n case 'TB': return 'bottom' as Position\n case 'LR': return 'right' as Position\n default: return 'bottom' as Position\n }\n}\n\n/**\n * Estimate the rendered pixel height of a CubeNode.\n */\nfunction estimateNodeHeight(node: Node): number {\n const cube = node.data?.cube as {\n measures?: unknown[]\n dimensions?: { type?: string }[]\n description?: string\n } | undefined\n if (!cube) return 300\n\n const HEADER = cube.description ? 80 : 56\n const SECTION_HEADER = 36\n const ROW = 34\n const MAX_BODY = 256\n const PADDING = 30\n\n let h = HEADER\n const measures = cube.measures?.length ?? 0\n const timeDims = cube.dimensions?.filter((d) => d.type === 'time').length ?? 0\n const regularDims = cube.dimensions?.filter((d) => d.type !== 'time').length ?? 0\n\n if (measures > 0) h += SECTION_HEADER + Math.min(measures * ROW, MAX_BODY)\n if (timeDims > 0) h += SECTION_HEADER + Math.min(timeDims * ROW, MAX_BODY)\n if (regularDims > 0) h += SECTION_HEADER + Math.min(regularDims * ROW, MAX_BODY)\n\n return h + PADDING\n}\n\n// --- ELK singleton (module-level, loaded once) ---\n\ninterface ELKInstance {\n layout(graph: unknown): Promise<unknown>\n}\n\nlet elkInstance: ELKInstance | null = null\nlet elkLoadPromise: Promise<ELKInstance | null> | null = null\n\nfunction loadElk(): Promise<ELKInstance | null> {\n if (elkLoadPromise) return elkLoadPromise\n // Use dynamic specifier so no bundler (Rollup, webpack) can statically resolve this.\n // elkjs is an optional peer dependency - if not installed, the catch() handles it.\n const specifier = ['elkjs', 'lib', 'elk.bundled.js'].join('/')\n elkLoadPromise = import(/* @vite-ignore */ specifier)\n .then((mod) => {\n const ELK = (mod as unknown as { default?: new () => ELKInstance }).default || mod\n elkInstance = new (ELK as unknown as new () => ELKInstance)()\n return elkInstance\n })\n .catch(() => null)\n return elkLoadPromise\n}\n\n// Force-export to prevent tree shaking of the side-effectful import\nexport { loadElk as _elkLoadPromise }\n\n// --- ELK layout computation (pure async function, no hooks) ---\n\ninterface ElkPort {\n id: string\n layoutOptions: Record<string, string>\n}\n\ninterface ElkNode {\n id: string\n width: number\n height: number\n layoutOptions?: Record<string, string>\n ports?: ElkPort[]\n}\n\ninterface ElkEdge {\n id: string\n sources: string[]\n targets: string[]\n}\n\ninterface ElkResultNode { id: string; x: number; y: number }\n\ninterface ElkResult {\n children?: ElkResultNode[]\n}\n\nexport interface ElkLayoutResult {\n nodes: Node[]\n edges: Edge[]\n}\n\nasync function computeElkLayout(\n nodes: Node[],\n edges: Edge[],\n opts: LayoutOptions,\n): Promise<ElkLayoutResult> {\n // Ensure ELK is loaded (lazy, webpack-safe)\n await loadElk()\n if (!elkInstance) {\n // ELK failed to load — return grid fallback\n return { nodes: layoutWithGrid(nodes, opts), edges }\n }\n\n const sourceSide = opts.direction === 'LR' ? 'EAST' : 'SOUTH'\n const targetSide = opts.direction === 'LR' ? 'WEST' : 'NORTH'\n\n // Per-edge ports so ELK spreads connections along the node side\n const nodeSourceEdges = new Map<string, string[]>()\n const nodeTargetEdges = new Map<string, string[]>()\n edges.forEach(edge => {\n if (!nodeSourceEdges.has(edge.source)) nodeSourceEdges.set(edge.source, [])\n nodeSourceEdges.get(edge.source)!.push(edge.id)\n if (!nodeTargetEdges.has(edge.target)) nodeTargetEdges.set(edge.target, [])\n nodeTargetEdges.get(edge.target)!.push(edge.id)\n })\n\n const elkNodes: ElkNode[] = nodes.map((node) => {\n const w = opts.nodeWidth\n const h = estimateNodeHeight(node)\n\n const srcEdges = nodeSourceEdges.get(node.id) || []\n const tgtEdges = nodeTargetEdges.get(node.id) || []\n const ports: ElkPort[] = [\n ...srcEdges.map(edgeId => ({\n id: `${node.id}__src__${edgeId}`,\n layoutOptions: { 'elk.port.side': sourceSide },\n })),\n ...tgtEdges.map(edgeId => ({\n id: `${node.id}__tgt__${edgeId}`,\n layoutOptions: { 'elk.port.side': targetSide },\n })),\n ]\n\n return {\n id: node.id,\n width: w,\n height: h,\n layoutOptions: { 'elk.portConstraints': 'FIXED_SIDE' },\n ports,\n }\n })\n\n const elkEdges: ElkEdge[] = edges.map((edge) => ({\n id: edge.id,\n sources: [`${edge.source}__src__${edge.id}`],\n targets: [`${edge.target}__tgt__${edge.id}`],\n }))\n\n const elkDirection = opts.direction === 'LR' ? 'RIGHT' : 'DOWN'\n\n const graph = {\n id: 'root',\n layoutOptions: {\n 'elk.algorithm': 'layered',\n 'elk.direction': elkDirection,\n 'elk.edgeRouting': 'SPLINES',\n 'elk.layered.edgeRouting.splines.mode': 'CONSERVATIVE',\n 'elk.spacing.nodeNode': String(opts.nodeSep),\n 'elk.layered.spacing.nodeNodeBetweenLayers': String(opts.rankSep),\n 'elk.spacing.edgeNode': '60',\n 'elk.layered.spacing.edgeNodeBetweenLayers': '60',\n 'elk.spacing.edgeEdge': '25',\n 'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX',\n 'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',\n },\n children: elkNodes,\n edges: elkEdges,\n }\n\n const result = await elkInstance.layout(graph) as ElkResult\n\n // Extract node positions (ELK returns top-left, same as React Flow)\n const positionMap = new Map<string, { x: number; y: number }>()\n if (result.children) {\n for (const child of result.children) {\n positionMap.set(child.id, { x: child.x, y: child.y })\n }\n }\n\n const layoutedNodes = nodes.map((node) => ({\n ...node,\n position: positionMap.get(node.id) || node.position,\n targetPosition: getTargetPosition(opts.direction),\n sourcePosition: getSourcePosition(opts.direction),\n }))\n\n // Return positioned nodes + original edges.\n // Edge rendering is handled by React Flow's getBezierPath() which knows\n // the actual handle positions — much more accurate than ELK's spline routes.\n return { nodes: layoutedNodes, edges }\n}\n\nfunction layoutWithGrid(nodes: Node[], opts: LayoutOptions): Node[] {\n const COL_HEIGHT_LIMIT = 1200\n let col = 0\n let colY = 0\n\n return nodes.map((node) => {\n const h = estimateNodeHeight(node)\n if (colY + h > COL_HEIGHT_LIMIT && colY > 0) {\n col++\n colY = 0\n }\n const position = { x: col * (opts.nodeWidth + opts.nodeSep), y: colY }\n colY += h + 40\n\n return {\n ...node,\n position,\n sourcePosition: getSourcePosition(opts.direction),\n targetPosition: getTargetPosition(opts.direction),\n }\n })\n}\n\n// --- Hook: single computation per structure key, no races ---\n\n/**\n * Layout hook with explicit state machine.\n *\n * - Waits for ELK to load (phase: 'waiting')\n * - Runs layout once per unique node/edge structure (phase: 'computing')\n * - Returns stable result (phase: 'ready')\n *\n * The effect only depends on a string structure key, NOT object references.\n * This prevents re-renders from cancelling in-flight computations.\n */\nexport function useERDLayout(\n nodes: Node[],\n edges: Edge[],\n options: Partial<LayoutOptions> = {}\n): { nodes: Node[]; edges: Edge[]; phase: LayoutPhase } {\n const opts = useMemo(\n () => ({ ...defaultLayoutOptions, ...options }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [options.direction, options.nodeWidth, options.nodeSep, options.rankSep]\n )\n\n // Stable structure key — only changes when node/edge IDs change\n const structureKey = useMemo(() => {\n if (nodes.length === 0) return ''\n return nodes.map(n => n.id).sort().join(',') + '|' + edges.map(e => e.id).sort().join(',')\n }, [nodes, edges])\n\n // Refs hold latest values so the effect closure always reads current data\n // without needing them as dependencies\n const nodesRef = useRef(nodes)\n const edgesRef = useRef(edges)\n const optsRef = useRef(opts)\n nodesRef.current = nodes\n edgesRef.current = edges\n optsRef.current = opts\n\n const [phase, setPhase] = useState<LayoutPhase>('waiting')\n const [result, setResult] = useState<ElkLayoutResult | null>(null)\n const [resultKey, setResultKey] = useState('')\n\n // Single computation per structure key\n const computeIdRef = useRef(0)\n\n useEffect(() => {\n if (!structureKey) {\n setPhase('ready')\n setResult(null)\n setResultKey('')\n return\n }\n\n const id = ++computeIdRef.current\n setPhase('computing')\n\n computeElkLayout(nodesRef.current, edgesRef.current, optsRef.current)\n .then(layoutResult => {\n // Only apply if this is still the latest computation\n if (id !== computeIdRef.current) return\n setResult(layoutResult)\n setResultKey(structureKey)\n setPhase('ready')\n })\n .catch(() => {\n if (id !== computeIdRef.current) return\n // On error, use grid fallback\n setResult({\n nodes: layoutWithGrid(nodesRef.current, optsRef.current),\n edges: edgesRef.current,\n })\n setResultKey(structureKey)\n setPhase('ready')\n })\n }, [structureKey]) // ONLY depends on structure key — no object refs\n\n // Output: only return layout when it matches current structure\n if (phase === 'ready' && result && resultKey === structureKey) {\n return { nodes: result.nodes, edges: result.edges, phase: 'ready' }\n }\n\n return { nodes: [], edges: [], phase }\n}\n","import { useCallback, useMemo, useEffect, useState, useRef } from 'react'\nimport type { MouseEvent as ReactMouseEvent } from 'react'\nimport type { Node, Edge, NodeChange, EdgeChange } from '@xyflow/react'\nimport '@xyflow/react/dist/style.css'\n\nimport { useCubeContext } from '../../providers/CubeProvider'\nimport { CubeNode } from './CubeNode'\nimport { RelationshipEdge } from './RelationshipEdge'\nimport { FieldDetailPanel } from './FieldDetailPanel'\nimport type { FieldSelection } from './FieldDetailPanel'\nimport { useERDLayout } from './useERDLayout'\nimport { getIcon } from '../../icons'\nimport { useXyflow } from './xyflowContext'\n\nconst nodeTypes = { cubeNode: CubeNode }\nconst edgeTypes = { relationshipEdge: RelationshipEdge }\n\n/**\n * Inner component that uses useNodesInitialized() (must be inside <ReactFlow>).\n * Calls fitView exactly once per fitViewToken change, after all nodes are measured.\n */\nfunction FitViewOnReady({ token }: { token: number }) {\n const { useNodesInitialized, useReactFlow } = useXyflow()\n const nodesInitialized = useNodesInitialized()\n const { fitView } = useReactFlow()\n const appliedTokenRef = useRef(0)\n\n useEffect(() => {\n if (token === 0 || token === appliedTokenRef.current) return\n if (!nodesInitialized) return\n appliedTokenRef.current = token\n fitView({ padding: 0.1 })\n }, [token, nodesInitialized, fitView])\n\n return null\n}\n\n// Stable empty arrays (avoid new [] on every render)\nconst EMPTY_STRINGS: string[] = []\n\nexport interface SchemaVisualizationProps {\n className?: string\n onFieldClick?: (cubeName: string, fieldName: string, fieldType: 'measure' | 'dimension') => void\n highlightedCubes?: string[]\n highlightedFields?: string[]\n searchTerm?: string\n height?: string | number\n}\n\nfunction getRelationshipColor(relationship: string): string {\n switch (relationship) {\n case 'belongsTo': return '#10b981'\n case 'hasOne': return '#3b82f6'\n case 'hasMany': return '#f59e0b'\n case 'belongsToMany': return '#8b5cf6'\n default: return '#6b7280'\n }\n}\n\nexport function SchemaVisualization({\n className = '',\n onFieldClick,\n highlightedCubes,\n highlightedFields,\n searchTerm,\n height = '100%',\n}: SchemaVisualizationProps) {\n const {\n ReactFlow: ReactFlowComponent,\n Controls,\n MiniMap,\n Background,\n applyNodeChanges,\n } = useXyflow()\n const { meta, metaLoading, metaError } = useCubeContext()\n\n // Stabilize array props to avoid re-creating references every render\n const stableHighlightedCubes = highlightedCubes || EMPTY_STRINGS\n const stableHighlightedFields = highlightedFields || EMPTY_STRINGS\n\n const [contextMenu, setContextMenu] = useState<{ x: number; y: number } | null>(null)\n const [searchInput, setSearchInput] = useState(searchTerm || '')\n const [autoLayoutCounter, setAutoLayoutCounter] = useState(0)\n const [selectedField, setSelectedField] = useState<FieldSelection | null>(null)\n const [detailPosition, setDetailPosition] = useState<{ x: number; y: number } | null>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n\n // In standalone mode (no onFieldClick), clicking fields shows the detail panel\n const isStandaloneMode = !onFieldClick\n const [savedPositions, setSavedPositions] = useState<Record<string, { x: number; y: number }>>({})\n const [positionsLoaded, setPositionsLoaded] = useState(false)\n\n const SearchIcon = getIcon('search')\n const CloseIcon = getIcon('close')\n\n const effectiveSearchTerm = searchTerm !== undefined ? searchTerm : searchInput\n\n // Load saved positions from localStorage (once)\n useEffect(() => {\n try {\n const saved = localStorage.getItem('drizzle-cube-erd-node-positions')\n if (saved) setSavedPositions(JSON.parse(saved))\n } catch { /* ignore */ }\n setPositionsLoaded(true)\n }, [])\n\n // --- Structural data: only depends on meta (stable) ---\n // Separate structure (for layout) from presentation (highlights/search)\n\n const { structuralNodes, structuralEdges } = useMemo(() => {\n if (!meta) return { structuralNodes: [] as Node[], structuralEdges: [] as Edge[] }\n\n const nodes: Node[] = meta.cubes.map((cube, index) => ({\n id: cube.name,\n type: 'cubeNode',\n // Placeholder position — ELK will override for auto-layout\n position: { x: (index % 3) * 400, y: Math.floor(index / 3) * 300 },\n data: { cube },\n }))\n\n const edges: Edge[] = []\n meta.cubes.forEach(cube => {\n if (cube.relationships) {\n cube.relationships.forEach((rel, i) => {\n if (rel.relationship === 'belongsTo') return\n edges.push({\n id: `${cube.name}-${rel.targetCube}-${i}`,\n source: cube.name,\n target: rel.targetCube,\n type: 'relationshipEdge',\n data: { relationship: rel, joinFields: rel.joinFields || [] },\n animated: false,\n style: { stroke: getRelationshipColor(rel.relationship), strokeWidth: 2 },\n })\n })\n }\n })\n\n return { structuralNodes: nodes, structuralEdges: edges }\n }, [meta]) // Only meta — no highlights/search/callbacks\n\n // --- Layout decision ---\n const needsAutoLayout = autoLayoutCounter > 0 || (positionsLoaded && Object.keys(savedPositions).length === 0)\n\n // Run ELK layout (only when auto-layout needed, otherwise pass empty to skip)\n const { nodes: elkNodes, edges: elkEdges, phase: layoutPhase } = useERDLayout(\n needsAutoLayout ? structuralNodes : [],\n needsAutoLayout ? structuralEdges : [],\n { direction: 'LR', nodeWidth: 340, nodeSep: 150, rankSep: 350 }\n )\n\n // --- Build display data: merge layout positions + presentation data ---\n\n // Convert client coords to container-relative, clamped to keep panel in view\n const toContainerPos = useCallback((clientX: number, clientY: number) => {\n const rect = containerRef.current?.getBoundingClientRect()\n if (!rect) return { x: clientX, y: clientY }\n const panelW = 300, panelH = 200 // approximate panel size for clamping\n let x = clientX - rect.left + 12 // offset slightly right of cursor\n let y = clientY - rect.top + 12 // offset slightly below cursor\n // Clamp so panel doesn't overflow right/bottom\n if (x + panelW > rect.width) x = clientX - rect.left - panelW - 12\n if (y + panelH > rect.height) y = rect.height - panelH - 8\n if (x < 0) x = 8\n if (y < 0) y = 8\n return { x, y }\n }, [])\n\n // Internal field click handler for standalone mode\n const handleInternalFieldClick = useCallback((cubeName: string, fieldName: string, fieldType: 'measure' | 'dimension', pos?: { x: number; y: number }) => {\n setSelectedField(prev => {\n if (prev && prev.cubeName === cubeName && prev.fieldName === fieldName) {\n setDetailPosition(null)\n return null\n }\n if (pos) setDetailPosition(toContainerPos(pos.x, pos.y))\n return { cubeName, fieldName, fieldType }\n })\n }, [toContainerPos])\n\n const handleInternalCubeClick = useCallback((cubeName: string, pos?: { x: number; y: number }) => {\n setSelectedField(prev => {\n if (prev && prev.cubeName === cubeName && prev.fieldName === null) {\n setDetailPosition(null)\n return null\n }\n if (pos) setDetailPosition(toContainerPos(pos.x, pos.y))\n return { cubeName, fieldName: null, fieldType: 'cube' }\n })\n }, [toContainerPos])\n\n const effectiveFieldClick = isStandaloneMode ? handleInternalFieldClick : onFieldClick\n const effectiveCubeClick = isStandaloneMode ? handleInternalCubeClick : undefined\n\n const buildNodeData = useCallback((cube: unknown) => ({\n cube,\n onFieldClick: effectiveFieldClick,\n onCubeClick: effectiveCubeClick,\n isHighlighted: stableHighlightedCubes.includes((cube as { name: string }).name),\n highlightedFields: stableHighlightedFields,\n searchTerm: effectiveSearchTerm,\n selectedField: isStandaloneMode ? selectedField : null,\n }), [effectiveFieldClick, effectiveCubeClick, stableHighlightedCubes, stableHighlightedFields, effectiveSearchTerm, isStandaloneMode, selectedField])\n\n // Determine if layout is resolved (ready to render ReactFlow)\n const layoutReady = !needsAutoLayout || layoutPhase === 'ready'\n\n const displayNodes = useMemo(() => {\n if (!meta || !layoutReady || !positionsLoaded) return []\n\n if (needsAutoLayout && elkNodes.length > 0) {\n // Use ELK-positioned nodes, inject presentation data\n return elkNodes.map(node => ({\n ...node,\n data: buildNodeData(node.data?.cube || meta.cubes.find(c => c.name === node.id)),\n }))\n }\n\n // Use saved positions\n return structuralNodes.map(node => ({\n ...node,\n position: savedPositions[node.id] || node.position,\n data: buildNodeData(node.data?.cube),\n }))\n }, [meta, layoutReady, positionsLoaded, needsAutoLayout, elkNodes, structuralNodes, savedPositions, buildNodeData])\n\n const displayEdges = useMemo(() => {\n if (!meta || !layoutReady) return []\n if (needsAutoLayout && elkEdges.length > 0) return elkEdges\n return structuralEdges\n }, [meta, layoutReady, needsAutoLayout, elkEdges, structuralEdges])\n\n // --- Controlled ReactFlow state ---\n const [rfNodes, setRfNodes] = useState<Node[]>([])\n const [rfEdges, setRfEdges] = useState<Edge[]>([])\n const appliedLayoutKeyRef = useRef('')\n const [fitViewToken, setFitViewToken] = useState(0) // increment to request fitView\n\n // Push layout to ReactFlow — only when positions change (not on data-only changes)\n useEffect(() => {\n if (displayNodes.length === 0) return\n\n const key = displayNodes.map(n => `${n.id}:${Math.round(n.position.x)},${Math.round(n.position.y)}`).join('|')\n if (key === appliedLayoutKeyRef.current) return\n\n const isFirstLayout = appliedLayoutKeyRef.current === ''\n appliedLayoutKeyRef.current = key\n setRfNodes(displayNodes)\n setRfEdges(displayEdges)\n\n // Request fitView on first layout\n if (isFirstLayout) {\n setFitViewToken(t => t + 1)\n }\n }, [displayNodes, displayEdges])\n\n // Update presentation data (highlights, search) without resetting positions\n const prevPresentationRef = useRef('')\n useEffect(() => {\n const selectedKey = selectedField ? `${selectedField.cubeName}.${selectedField.fieldName}` : ''\n const presentationKey = `${stableHighlightedCubes.join(',')}|${stableHighlightedFields.join(',')}|${effectiveSearchTerm}|${String(onFieldClick)}|${selectedKey}`\n if (presentationKey === prevPresentationRef.current) return\n prevPresentationRef.current = presentationKey\n\n if (rfNodes.length === 0 || !meta) return\n setRfNodes(prev => prev.map(node => {\n const cube = meta.cubes.find(c => c.name === node.id)\n if (!cube) return node\n return { ...node, data: buildNodeData(cube) }\n }))\n }, [stableHighlightedCubes, stableHighlightedFields, effectiveSearchTerm, onFieldClick, selectedField, rfNodes.length, meta, buildNodeData])\n\n // Handle node changes (dragging)\n const handleNodesChange = useCallback((changes: NodeChange[]) => {\n setRfNodes(nds => applyNodeChanges(changes, nds))\n\n // Save positions on drag end\n const dragEnds = changes.filter(\n (c) => c.type === 'position' && 'dragging' in c && c.dragging === false\n )\n if (dragEnds.length > 0) {\n setRfNodes(currentNodes => {\n const positions: Record<string, { x: number; y: number }> = {}\n currentNodes.forEach(n => { if (n.position) positions[n.id] = n.position })\n try { localStorage.setItem('drizzle-cube-erd-node-positions', JSON.stringify(positions)) } catch { /* ignore */ }\n setSavedPositions(positions)\n return currentNodes\n })\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const handleEdgesChange = useCallback((_changes: EdgeChange[]) => {}, [])\n\n const handleContextMenu = useCallback((event: ReactMouseEvent | MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n setContextMenu({ x: event.clientX, y: event.clientY })\n }, [])\n\n const handleClick = useCallback(() => {\n if (contextMenu) setContextMenu(null)\n if (selectedField) { setSelectedField(null); setDetailPosition(null) }\n }, [contextMenu, selectedField])\n\n const handleAutoLayout = useCallback(() => {\n setSavedPositions({})\n appliedLayoutKeyRef.current = '' // force re-apply → layout effect will request fitView\n try { localStorage.removeItem('drizzle-cube-erd-node-positions') } catch { /* ignore */ }\n setAutoLayoutCounter(c => c + 1)\n setContextMenu(null)\n }, [])\n\n // --- Render ---\n\n if (metaLoading) {\n return (\n <div className={`dc:flex dc:items-center dc:justify-center dc:h-96 ${className}`}>\n <div className=\"dc:text-center\">\n <div className=\"dc:animate-spin dc:rounded-full dc:h-8 dc:w-8 dc:border-b-2 border-dc-accent dc:mx-auto dc:mb-2\" />\n <p className=\"text-dc-text-muted\">Loading cube schema...</p>\n </div>\n </div>\n )\n }\n\n if (metaError) {\n return (\n <div className={`dc:flex dc:items-center dc:justify-center dc:h-96 ${className}`}>\n <div className=\"dc:text-center text-dc-error\">\n <p className=\"dc:font-medium\">Failed to load cube schema</p>\n <p className=\"dc:text-sm dc:mt-1\">{metaError}</p>\n </div>\n </div>\n )\n }\n\n if (!meta || meta.cubes.length === 0) {\n return (\n <div className={`dc:flex dc:items-center dc:justify-center dc:h-96 ${className}`}>\n <div className=\"dc:text-center text-dc-text-muted\">\n <p className=\"dc:font-medium\">No cubes found</p>\n <p className=\"dc:text-sm dc:mt-1\">Register some cubes to see the relationship diagram</p>\n </div>\n </div>\n )\n }\n\n // Show loading while ELK computes layout (no grid flash)\n if (!layoutReady) {\n return (\n <div className={`dc:flex dc:items-center dc:justify-center dc:h-96 ${className}`}>\n <div className=\"dc:text-center\">\n <div className=\"dc:animate-spin dc:rounded-full dc:h-8 dc:w-8 dc:border-b-2 border-dc-accent dc:mx-auto dc:mb-2\" />\n <p className=\"text-dc-text-muted\">Computing layout...</p>\n </div>\n </div>\n )\n }\n\n return (\n <div className={`dc:flex dc:flex-col ${className}`} style={{ height, minHeight: 400 }}>\n {searchTerm === undefined && (\n <div className=\"dc:px-3 dc:py-2 dc:border-b border-dc-border bg-dc-surface dc:flex dc:items-center dc:gap-2 dc:flex-shrink-0\">\n <SearchIcon className=\"dc:w-4 dc:h-4 text-dc-text-muted\" />\n <input\n type=\"text\"\n value={searchInput}\n onChange={(e) => setSearchInput(e.target.value)}\n placeholder=\"Search cubes and fields...\"\n className=\"dc:flex-1 dc:text-sm dc:bg-transparent dc:outline-none text-dc-text dc:placeholder:text-dc-text-muted\"\n />\n {searchInput && (\n <button onClick={() => setSearchInput('')} className=\"text-dc-text-muted dc:hover:text-dc-text\">\n <CloseIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n )}\n </div>\n )}\n\n <div ref={containerRef} className=\"dc:relative dc:flex-1 dc:min-h-0\">\n <div style={{ position: 'absolute', inset: 0 }}>\n <ReactFlowComponent\n nodes={rfNodes}\n edges={rfEdges}\n onNodesChange={handleNodesChange}\n onEdgesChange={handleEdgesChange}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n connectionMode={'loose' as never}\n minZoom={0.1}\n maxZoom={2}\n proOptions={{ hideAttribution: true }}\n onPaneContextMenu={handleContextMenu}\n onPaneClick={handleClick}\n >\n <Controls />\n <MiniMap\n nodeColor={(node) => stableHighlightedCubes.includes(node.id) ? '#8b5cf6' : '#e5e7eb'}\n maskColor=\"rgb(240, 242, 246, 0.7)\"\n />\n <Background variant={'dots' as never} gap={12} size={1} />\n <FitViewOnReady token={fitViewToken} />\n </ReactFlowComponent>\n </div>\n\n {/* Field detail panel for standalone browse mode */}\n {isStandaloneMode && selectedField && detailPosition && meta && (\n <div\n className=\"dc:absolute dc:z-20\"\n style={{ left: detailPosition.x, top: detailPosition.y }}\n >\n <FieldDetailPanel\n selection={selectedField}\n meta={meta}\n onClose={() => { setSelectedField(null); setDetailPosition(null) }}\n />\n </div>\n )}\n </div>\n\n {contextMenu && (\n <div\n className=\"dc:fixed dc:z-50 bg-dc-surface dc:rounded-md dc:shadow-lg dc:border border-dc-border dc:py-1 dc:min-w-[120px]\"\n style={{ left: contextMenu.x, top: contextMenu.y }}\n >\n <button\n onClick={handleAutoLayout}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm text-dc-text-secondary dc:hover:bg-dc-surface-hover dc:text-left\"\n >\n Auto Layout\n </button>\n </div>\n )}\n </div>\n )\n}\n\nexport default SchemaVisualization\n"],"mappings":"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"}
1
+ {"version":3,"file":"schema-visualization-BJ8HrNqB.js","names":[],"sources":["../../../src/client/components/SchemaVisualization/xyflowContext.tsx","../../../src/client/components/SchemaVisualization/SchemaVisualizationLazy.tsx","../../../src/client/components/SchemaVisualization/CubeNode.tsx","../../../src/client/components/SchemaVisualization/RelationshipEdge.tsx","../../../src/client/components/SchemaVisualization/FieldDetailPanel.tsx","../../../src/client/components/SchemaVisualization/useERDLayout.ts","../../../src/client/components/SchemaVisualization/index.tsx"],"sourcesContent":["/**\n * React context for @xyflow/react modules.\n *\n * All xyflow imports are dynamic (via SchemaVisualizationLazy) so that\n * the built schema-visualization chunk has NO static imports from\n * @xyflow/react. This prevents consuming projects from failing at\n * build time when @xyflow/react is not installed.\n */\n\nimport { createContext, useContext } from 'react'\n\nexport type XyflowModule = typeof import('@xyflow/react')\n\nconst XyflowContext = createContext<XyflowModule | null>(null)\n\nexport const XyflowProvider = XyflowContext.Provider\n\nexport function useXyflow(): XyflowModule {\n const ctx = useContext(XyflowContext)\n if (!ctx) throw new Error('useXyflow must be used within XyflowProvider')\n return ctx\n}\n","/**\n * Lazy-loaded SchemaVisualization with graceful fallback\n * when @xyflow/react is not installed.\n *\n * All @xyflow/react imports are dynamic here so the built chunk\n * has NO static imports from @xyflow/react. This prevents consuming\n * projects from failing at build time when xyflow is not installed.\n */\n\nimport { lazy, Suspense, useState, useEffect } from 'react'\nimport type { SchemaVisualizationProps } from './index'\nimport { XyflowProvider } from './xyflowContext'\nimport type { XyflowModule } from './xyflowContext'\n\nlet loadFailed = false\n\nfunction MissingDependencyFallback(_props: SchemaVisualizationProps) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:h-full dc:p-8\">\n <div className=\"dc:text-center dc:max-w-md\">\n <div className=\"dc:text-4xl dc:mb-4\">&#128269;</div>\n <h3 className=\"dc:text-lg dc:font-semibold text-dc-text dc:mb-2\">\n Schema Visualization requires additional packages\n </h3>\n <p className=\"dc:text-sm text-dc-text-secondary dc:mb-4\">\n Install the required dependencies to enable the interactive schema diagram:\n </p>\n <code className=\"dc:block dc:px-4 dc:py-2 dc:rounded bg-dc-surface-secondary dc:text-sm dc:font-mono text-dc-text dc:border border-dc-border\">\n npm install @xyflow/react elkjs\n </code>\n </div>\n </div>\n )\n}\n\nfunction LoadingFallback() {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:h-full\">\n <div className=\"dc:text-center\">\n <div className=\"dc:animate-spin dc:rounded-full dc:h-8 dc:w-8 dc:border-b-2 border-dc-accent dc:mx-auto dc:mb-2\" />\n <p className=\"dc:text-sm text-dc-text-muted\">Loading schema visualization...</p>\n </div>\n </div>\n )\n}\n\nconst LazySchemaVisualization = lazy(async () => {\n const mod = await import('./index')\n return { default: mod.SchemaVisualization }\n})\n\nexport function SchemaVisualizationLazy(props: SchemaVisualizationProps) {\n const [xyflow, setXyflow] = useState<XyflowModule | null>(null)\n const [failed, setFailed] = useState(loadFailed)\n\n useEffect(() => {\n if (loadFailed) return\n let cancelled = false\n\n // Dynamic specifier so webpack/Next.js won't try to resolve this optional dependency at build time\n const specifier = ['@xyflow', 'react'].join('/')\n import(/* @vite-ignore */ specifier)\n .then((mod) => {\n if (!cancelled) setXyflow(mod as XyflowModule)\n })\n .catch(() => {\n loadFailed = true\n if (!cancelled) setFailed(true)\n })\n\n return () => { cancelled = true }\n }, [])\n\n if (failed) {\n return <MissingDependencyFallback {...props} />\n }\n\n if (!xyflow) {\n return <LoadingFallback />\n }\n\n return (\n <XyflowProvider value={xyflow}>\n <Suspense fallback={<LoadingFallback />}>\n <LazySchemaVisualization {...props} />\n </Suspense>\n </XyflowProvider>\n )\n}\n\nexport function isSchemaVisualizationAvailable(): boolean {\n return !loadFailed\n}\n","import type { MouseEvent } from 'react'\nimport type { CubeMetaCube } from '../../types'\nimport { getIcon } from '../../icons'\nimport { useXyflow } from './xyflowContext'\n\ninterface CubeNodeData {\n cube: CubeMetaCube\n onFieldClick?: (cubeName: string, fieldName: string, fieldType: 'measure' | 'dimension', pos?: { x: number; y: number }) => void\n onCubeClick?: (cubeName: string, pos?: { x: number; y: number }) => void\n isHighlighted: boolean\n highlightedFields: string[]\n searchTerm?: string\n selectedField?: { cubeName: string; fieldName: string | null } | null\n [key: string]: unknown\n}\n\ninterface CubeNodeProps {\n data: CubeNodeData\n}\n\nexport function CubeNode({ data }: CubeNodeProps) {\n const { Handle, Position } = useXyflow()\n const { cube, onFieldClick, onCubeClick, isHighlighted, highlightedFields, searchTerm, selectedField } = data\n\n const handleFieldClick = (e: MouseEvent, fieldName: string, fieldType: 'measure' | 'dimension') => {\n if (onFieldClick) {\n onFieldClick(cube.name, fieldName, fieldType, { x: e.clientX, y: e.clientY })\n }\n }\n\n const InfoIcon = getIcon('info')\n\n const handleCubeInfoClick = (e: MouseEvent) => {\n e.stopPropagation() // don't start drag\n if (onCubeClick) {\n onCubeClick(cube.name, { x: e.clientX, y: e.clientY })\n }\n }\n\n const isFieldSelected = (fieldName: string) => {\n if (!selectedField) return false\n return selectedField.cubeName === cube.name && selectedField.fieldName === fieldName\n }\n\n const isCubeSelected = selectedField?.cubeName === cube.name && selectedField?.fieldName === null\n\n const isFieldHighlighted = (fullFieldName: string) => {\n return highlightedFields.includes(fullFieldName)\n }\n\n const isFieldSearchMatch = (field: { name: string; title?: string }) => {\n if (!searchTerm?.trim()) return true\n const term = searchTerm.toLowerCase()\n return (\n field.name.toLowerCase().includes(term) ||\n (field.title && field.title.toLowerCase().includes(term))\n )\n }\n\n const cubeHasMatches = () => {\n if (!searchTerm?.trim()) return true\n return cube.measures.some(m => isFieldSearchMatch(m)) ||\n cube.dimensions.some(d => isFieldSearchMatch(d))\n }\n\n const hasCubeMatches = cubeHasMatches()\n\n const getFieldClasses = (\n field: { name: string; title?: string; type?: string },\n highlighted: boolean,\n _fieldType: 'measure' | 'dimension'\n ) => {\n const fieldName = field.name.split('.')[1] || field.name\n const selected = isFieldSelected(fieldName)\n const base = 'dc:px-4 dc:py-2 dc:text-xs dc:cursor-pointer dc:transition-all dc:border-b border-dc-border last:dc:border-b-0 nodrag nopan'\n\n if (selected) {\n return `${base} bg-dc-accent-bg text-dc-accent dc:font-semibold dc:ring-1 dc:ring-inset ring-dc-accent`\n }\n\n if (!hasCubeMatches && searchTerm?.trim()) {\n if (highlighted) return `${base} bg-dc-accent-bg text-dc-accent dc:font-semibold`\n return `${base} dc:hover:bg-dc-surface-hover text-dc-text-secondary`\n }\n\n if (searchTerm?.trim() && !isFieldSearchMatch(field)) {\n return `${base} dc:opacity-40 dc:hover:opacity-60 text-dc-text-muted`\n }\n\n if (searchTerm?.trim() && isFieldSearchMatch(field) && !highlighted) {\n return `${base} dc:font-bold dc:hover:bg-dc-accent-bg`\n }\n\n if (highlighted) {\n return `${base} bg-dc-accent-bg text-dc-accent dc:font-semibold`\n }\n\n return `${base} dc:hover:bg-dc-surface-hover text-dc-text-secondary`\n }\n\n const timeDimensions = cube.dimensions.filter(d => d.type === 'time')\n const regularDimensions = cube.dimensions.filter(d => d.type !== 'time')\n\n return (\n <div\n className={`\n dc:border-2 dc:rounded-lg dc:shadow-lg dc:min-w-[280px] dc:overflow-hidden dc:transition-all\n ${!hasCubeMatches && searchTerm?.trim() ? 'dc:opacity-30 dc:grayscale' : ''}\n ${isHighlighted ? 'border-dc-accent dc:ring-2 ring-dc-accent' : 'border-dc-border'}\n `}\n style={{ backgroundColor: 'var(--dc-surface)' }}\n >\n {/* Cube Header */}\n <div className={`dc:px-4 dc:py-3 dc:transition-colors ${\n isHighlighted ? 'bg-dc-accent-bg' : 'bg-dc-surface-secondary'\n }`}>\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <div>\n <h3 className=\"dc:font-semibold text-dc-text dc:text-sm\">\n {cube.title || cube.name}\n </h3>\n {cube.description && (\n <p className=\"dc:text-xs text-dc-text-muted dc:mt-1 dc:line-clamp-2\">{cube.description}</p>\n )}\n </div>\n {onCubeClick && (\n <button\n className={`dc:ml-2 dc:p-1 dc:rounded dc:transition-colors nodrag nopan ${\n isCubeSelected\n ? 'bg-dc-accent-bg text-dc-accent'\n : 'text-dc-text-muted dc:hover:text-dc-text dc:hover:bg-dc-surface-hover'\n }`}\n onClick={handleCubeInfoClick}\n title=\"Cube info\"\n >\n <InfoIcon className=\"dc:w-5 dc:h-5\" />\n </button>\n )}\n {!onCubeClick && (\n <div className=\"dc:text-xs text-dc-text-muted dc:ml-2\">\n <div>{cube.measures.length}M</div>\n <div>{cube.dimensions.length}D</div>\n </div>\n )}\n </div>\n </div>\n\n {/* Measures */}\n {cube.measures.length > 0 && (\n <div className=\"dc:border-t border-dc-border\">\n <div className=\"dc:px-4 dc:py-1.5 dc:border-b border-dc-border\" style={{ backgroundColor: 'color-mix(in srgb, var(--dc-warning) 10%, var(--dc-surface))' }}>\n <h4 className=\"dc:text-xs dc:font-medium text-dc-text-secondary dc:flex dc:items-center\">\n <span className=\"dc:w-2 dc:h-2 bg-dc-warning dc:rounded-full dc:mr-2\" />\n Measures ({cube.measures.length})\n </h4>\n </div>\n <div className=\"dc:max-h-64 dc:overflow-y-auto nowheel\">\n {cube.measures.map((measure) => {\n const fieldName = measure.name.split('.')[1] || measure.name\n const highlighted = isFieldHighlighted(measure.name)\n return (\n <div\n key={measure.name}\n className={getFieldClasses(measure, highlighted, 'measure')}\n onClick={(e) => handleFieldClick(e, fieldName, 'measure')}\n title={measure.title}\n >\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:font-mono dc:truncate\">\n {measure.shortTitle || measure.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted dc:ml-2 dc:text-[10px] dc:uppercase\">\n {measure.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Time Dimensions */}\n {timeDimensions.length > 0 && (\n <div className=\"dc:border-t border-dc-border\">\n <div className=\"dc:px-4 dc:py-1.5 dc:border-b border-dc-border\" style={{ backgroundColor: 'color-mix(in srgb, var(--dc-accent) 10%, var(--dc-surface))' }}>\n <h4 className=\"dc:text-xs dc:font-medium text-dc-text-secondary dc:flex dc:items-center\">\n <span className=\"dc:w-2 dc:h-2 bg-dc-accent dc:rounded-full dc:mr-2\" />\n Time Dimensions ({timeDimensions.length})\n </h4>\n </div>\n <div className=\"dc:max-h-64 dc:overflow-y-auto nowheel\">\n {timeDimensions.map((dimension) => {\n const fieldName = dimension.name.split('.')[1] || dimension.name\n const highlighted = isFieldHighlighted(dimension.name)\n return (\n <div\n key={dimension.name}\n className={getFieldClasses(dimension, highlighted, 'dimension')}\n onClick={(e) => handleFieldClick(e, fieldName, 'dimension')}\n title={dimension.title}\n >\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:font-mono dc:truncate\">\n {dimension.shortTitle || dimension.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted dc:ml-2 dc:text-[10px] dc:uppercase\">\n {dimension.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Regular Dimensions */}\n {regularDimensions.length > 0 && (\n <div className=\"dc:border-t border-dc-border\">\n <div className=\"dc:px-4 dc:py-1.5 dc:border-b border-dc-border\" style={{ backgroundColor: 'color-mix(in srgb, var(--dc-success) 10%, var(--dc-surface))' }}>\n <h4 className=\"dc:text-xs dc:font-medium text-dc-text-secondary dc:flex dc:items-center\">\n <span className=\"dc:w-2 dc:h-2 bg-dc-success dc:rounded-full dc:mr-2\" />\n Dimensions ({regularDimensions.length})\n </h4>\n </div>\n <div className=\"dc:max-h-64 dc:overflow-y-auto nowheel\">\n {regularDimensions.map((dimension) => {\n const fieldName = dimension.name.split('.')[1] || dimension.name\n const highlighted = isFieldHighlighted(dimension.name)\n return (\n <div\n key={dimension.name}\n className={getFieldClasses(dimension, highlighted, 'dimension')}\n onClick={(e) => handleFieldClick(e, fieldName, 'dimension')}\n title={dimension.title}\n >\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:font-mono dc:truncate\">\n {dimension.shortTitle || dimension.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted dc:ml-2 dc:text-[10px] dc:uppercase\">\n {dimension.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Connection handles - hidden */}\n <Handle type=\"source\" position={Position.Right} id=\"right\" className=\"dc:opacity-0\" isConnectable={false} />\n <Handle type=\"target\" position={Position.Left} id=\"left\" className=\"dc:opacity-0\" isConnectable={false} />\n <Handle type=\"source\" position={Position.Bottom} id=\"bottom\" className=\"dc:opacity-0\" isConnectable={false} />\n <Handle type=\"target\" position={Position.Top} id=\"top\" className=\"dc:opacity-0\" isConnectable={false} />\n </div>\n )\n}\n","import type { Edge, EdgeProps } from '@xyflow/react'\nimport type { CubeMetaRelationship } from '../../types'\nimport { useXyflow } from './xyflowContext'\n\ninterface RelationshipEdgeData {\n relationship: CubeMetaRelationship\n joinFields: Array<{\n sourceField: string\n targetField: string\n }>\n [key: string]: unknown\n}\n\nexport type RelationshipEdgeType = Edge<RelationshipEdgeData, 'relationshipEdge'>\n\nexport function RelationshipEdge({\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n style = {},\n data,\n markerEnd,\n}: EdgeProps<RelationshipEdgeType>) {\n const { getBezierPath, BaseEdge, EdgeLabelRenderer } = useXyflow()\n const [edgePath, labelX, labelY] = getBezierPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n })\n\n if (!data) return null\n\n const { relationship, joinFields } = data\n\n const getSymbol = (rel: string) => {\n switch (rel) {\n case 'belongsTo': return '\\u2208' // belongs to\n case 'hasOne': return '1:1'\n case 'hasMany': return '1:M'\n case 'belongsToMany': return 'M:M'\n default: return '?'\n }\n }\n\n const getColor = (rel: string) => {\n switch (rel) {\n case 'belongsTo': return '#10b981'\n case 'hasOne': return '#3b82f6'\n case 'hasMany': return '#f59e0b'\n case 'belongsToMany': return '#8b5cf6'\n default: return '#6b7280'\n }\n }\n\n const color = getColor(relationship.relationship)\n const symbol = getSymbol(relationship.relationship)\n\n return (\n <>\n <BaseEdge path={edgePath} markerEnd={markerEnd} style={{ ...style, stroke: color }} />\n <EdgeLabelRenderer>\n <div\n style={{\n position: 'absolute',\n transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n fontSize: 10,\n pointerEvents: 'all',\n }}\n className=\"nodrag nopan\"\n >\n <div\n className=\"dc:border-2 dc:rounded-md dc:px-2 dc:py-1 dc:shadow-xs\"\n style={{ backgroundColor: 'var(--dc-surface)', borderColor: color }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:font-bold dc:text-xs dc:mb-1\" style={{ color }}>\n {symbol}\n </div>\n <div className=\"dc:text-[9px] text-dc-text-muted dc:leading-tight\">\n {joinFields.map((field, index) => (\n <div key={index} className=\"dc:font-mono\">\n {field.sourceField} → {field.targetField}\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n </EdgeLabelRenderer>\n </>\n )\n}\n","import type { ReactNode } from 'react'\nimport type { CubeMetaCube, CubeMetaMeasure, CubeMetaDimension } from '../../types'\nimport { getIcon } from '../../icons'\n\nexport interface FieldSelection {\n cubeName: string\n fieldName: string | null // null = cube-level selection\n fieldType: 'measure' | 'dimension' | 'cube'\n}\n\ninterface FieldDetailPanelProps {\n selection: FieldSelection\n meta: { cubes: CubeMetaCube[] }\n onClose: () => void\n}\n\nfunction DetailRow({ label, children }: { label: string; children: ReactNode }) {\n return (\n <div className=\"dc:flex dc:items-start dc:gap-2 dc:text-xs\">\n <span className=\"text-dc-text-muted dc:w-20 dc:flex-shrink-0 dc:font-medium\">{label}</span>\n <span className=\"text-dc-text dc:flex-1 dc:min-w-0\">{children}</span>\n </div>\n )\n}\n\nfunction TypeBadge({ type, color }: { type: string; color: string }) {\n return (\n <span\n className=\"dc:inline-flex dc:items-center dc:px-1.5 dc:py-0.5 dc:rounded dc:text-[10px] dc:font-medium dc:uppercase\"\n style={{ backgroundColor: `color-mix(in srgb, ${color} 15%, var(--dc-surface))`, color }}\n >\n {type}\n </span>\n )\n}\n\nfunction MeasureDetail({ measure, cube }: { measure: CubeMetaMeasure; cube: CubeMetaCube }) {\n const typeColor = {\n count: '#f59e0b',\n countDistinct: '#f59e0b',\n countDistinctApprox: '#f59e0b',\n sum: '#10b981',\n avg: '#3b82f6',\n min: '#8b5cf6',\n max: '#ec4899',\n runningTotal: '#06b6d4',\n number: '#6b7280',\n }[measure.type] || '#6b7280'\n\n return (\n <div className=\"dc:flex dc:flex-col dc:gap-2\">\n <DetailRow label=\"Cube\">{cube.title || cube.name}</DetailRow>\n <DetailRow label=\"Type\"><TypeBadge type={measure.type} color={typeColor} /></DetailRow>\n {measure.title && measure.title !== measure.shortTitle && (\n <DetailRow label=\"Title\">{measure.title}</DetailRow>\n )}\n {measure.drillMembers && measure.drillMembers.length > 0 && (\n <DetailRow label=\"Drill into\">\n <div className=\"dc:flex dc:flex-wrap dc:gap-1\">\n {measure.drillMembers.map(dm => (\n <span key={dm} className=\"dc:font-mono dc:text-[10px] dc:px-1 dc:py-0.5 dc:rounded bg-dc-surface-secondary border-dc-border dc:border\">\n {dm.split('.')[1] || dm}\n </span>\n ))}\n </div>\n </DetailRow>\n )}\n </div>\n )\n}\n\nfunction DimensionDetail({ dimension, cube }: { dimension: CubeMetaDimension; cube: CubeMetaCube }) {\n const typeColor = dimension.type === 'time' ? '#3b82f6' : '#10b981'\n\n // Find hierarchies this dimension belongs to\n const hierarchies = cube.hierarchies?.filter(h =>\n h.levels.some(l => l === dimension.name || l === `${cube.name}.${dimension.name.split('.')[1]}`)\n ) || []\n\n return (\n <div className=\"dc:flex dc:flex-col dc:gap-2\">\n <DetailRow label=\"Cube\">{cube.title || cube.name}</DetailRow>\n <DetailRow label=\"Type\"><TypeBadge type={dimension.type} color={typeColor} /></DetailRow>\n {dimension.title && dimension.title !== dimension.shortTitle && (\n <DetailRow label=\"Title\">{dimension.title}</DetailRow>\n )}\n {dimension.type === 'time' && dimension.granularities && dimension.granularities.length > 0 && (\n <DetailRow label=\"Granularity\">\n <div className=\"dc:flex dc:flex-wrap dc:gap-1\">\n {dimension.granularities.map(g => (\n <span key={g} className=\"dc:font-mono dc:text-[10px] dc:px-1 dc:py-0.5 dc:rounded bg-dc-surface-secondary border-dc-border dc:border\">\n {g}\n </span>\n ))}\n </div>\n </DetailRow>\n )}\n {hierarchies.length > 0 && (\n <DetailRow label=\"Hierarchy\">\n {hierarchies.map(h => (\n <div key={h.name} className=\"dc:text-[10px]\">\n <span className=\"dc:font-medium\">{h.title}</span>\n <span className=\"text-dc-text-muted dc:ml-1\">\n ({h.levels.map(l => l.split('.')[1] || l).join(' > ')})\n </span>\n </div>\n ))}\n </DetailRow>\n )}\n </div>\n )\n}\n\nfunction CubeDetail({ cube }: { cube: CubeMetaCube }) {\n const relationshipColors: Record<string, string> = {\n belongsTo: '#10b981',\n hasOne: '#3b82f6',\n hasMany: '#f59e0b',\n belongsToMany: '#8b5cf6',\n }\n\n return (\n <div className=\"dc:flex dc:flex-col dc:gap-2\">\n {cube.description && (\n <DetailRow label=\"Description\">{cube.description}</DetailRow>\n )}\n <DetailRow label=\"Measures\">\n <span className=\"dc:font-mono\">{cube.measures.length}</span>\n {cube.measures.length > 0 && (\n <span className=\"text-dc-text-muted dc:ml-1\">\n ({[...new Set(cube.measures.map(m => m.type))].join(', ')})\n </span>\n )}\n </DetailRow>\n <DetailRow label=\"Dimensions\">\n <span className=\"dc:font-mono\">{cube.dimensions.length}</span>\n {cube.dimensions.some(d => d.type === 'time') && (\n <span className=\"text-dc-text-muted dc:ml-1\">\n ({cube.dimensions.filter(d => d.type === 'time').length} time)\n </span>\n )}\n </DetailRow>\n {cube.relationships && cube.relationships.length > 0 && (\n <DetailRow label=\"Joins\">\n <div className=\"dc:flex dc:flex-col dc:gap-1\">\n {cube.relationships.map((rel, i) => (\n <div key={i} className=\"dc:flex dc:items-center dc:gap-1.5 dc:text-[10px]\">\n <TypeBadge type={rel.relationship} color={relationshipColors[rel.relationship] || '#6b7280'} />\n <span className=\"dc:font-mono\">{rel.targetCube}</span>\n </div>\n ))}\n </div>\n </DetailRow>\n )}\n {cube.hierarchies && cube.hierarchies.length > 0 && (\n <DetailRow label=\"Hierarchies\">\n <div className=\"dc:flex dc:flex-col dc:gap-1\">\n {cube.hierarchies.map(h => (\n <div key={h.name} className=\"dc:text-[10px]\">\n <span className=\"dc:font-medium\">{h.title}</span>\n <span className=\"text-dc-text-muted dc:ml-1\">\n ({h.levels.map(l => l.split('.')[1] || l).join(' > ')})\n </span>\n </div>\n ))}\n </div>\n </DetailRow>\n )}\n {cube.meta?.eventStream && (\n <DetailRow label=\"Event Stream\">\n <div className=\"dc:text-[10px]\">\n <span className=\"text-dc-text-muted\">binding: </span>\n <span className=\"dc:font-mono\">{cube.meta.eventStream.bindingKey.split('.')[1] || cube.meta.eventStream.bindingKey}</span>\n <span className=\"text-dc-text-muted dc:ml-2\">time: </span>\n <span className=\"dc:font-mono\">{cube.meta.eventStream.timeDimension.split('.')[1] || cube.meta.eventStream.timeDimension}</span>\n </div>\n </DetailRow>\n )}\n </div>\n )\n}\n\nexport function FieldDetailPanel({ selection, meta, onClose }: FieldDetailPanelProps) {\n const CloseIcon = getIcon('close')\n\n const cube = meta.cubes.find(c => c.name === selection.cubeName)\n if (!cube) return null\n\n // Determine what to show\n let title: string\n let dotColor: string\n let sectionBgColor: string\n let content: ReactNode\n\n if (selection.fieldType === 'cube' || !selection.fieldName) {\n title = cube.title || cube.name\n dotColor = 'var(--dc-accent)'\n sectionBgColor = 'color-mix(in srgb, var(--dc-accent) 10%, var(--dc-surface))'\n content = <CubeDetail cube={cube} />\n } else if (selection.fieldType === 'measure') {\n const measure = cube.measures.find(m => {\n const mField = m.name.split('.')[1] || m.name\n return mField === selection.fieldName || m.name === selection.fieldName\n })\n if (!measure) return null\n title = measure.shortTitle || measure.title || selection.fieldName\n dotColor = 'var(--dc-warning)'\n sectionBgColor = 'color-mix(in srgb, var(--dc-warning) 10%, var(--dc-surface))'\n content = <MeasureDetail measure={measure} cube={cube} />\n } else {\n const dimension = cube.dimensions.find(d => {\n const dField = d.name.split('.')[1] || d.name\n return dField === selection.fieldName || d.name === selection.fieldName\n })\n if (!dimension) return null\n title = dimension.shortTitle || dimension.title || selection.fieldName\n dotColor = dimension.type === 'time' ? 'var(--dc-accent)' : 'var(--dc-success)'\n sectionBgColor = dimension.type === 'time'\n ? 'color-mix(in srgb, var(--dc-accent) 10%, var(--dc-surface))'\n : 'color-mix(in srgb, var(--dc-success) 10%, var(--dc-surface))'\n content = <DimensionDetail dimension={dimension} cube={cube} />\n }\n\n return (\n <div\n className=\"dc:border-2 dc:rounded-lg dc:shadow-lg dc:min-w-[260px] dc:max-w-[320px] dc:overflow-hidden dc:transition-all border-dc-border\"\n style={{ backgroundColor: 'var(--dc-surface)' }}\n >\n {/* Header - same style as CubeNode section headers */}\n <div\n className=\"dc:px-4 dc:py-2.5 dc:border-b border-dc-border dc:flex dc:items-center dc:justify-between\"\n style={{ backgroundColor: sectionBgColor }}\n >\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:min-w-0\">\n <span\n className=\"dc:w-2 dc:h-2 dc:rounded-full dc:flex-shrink-0\"\n style={{ backgroundColor: dotColor }}\n />\n <h4 className=\"dc:text-sm dc:font-semibold text-dc-text dc:truncate\">{title}</h4>\n </div>\n <button\n onClick={onClose}\n className=\"dc:ml-2 dc:flex-shrink-0 text-dc-text-muted dc:hover:text-dc-text dc:transition-colors\"\n >\n <CloseIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"dc:px-4 dc:py-3\">\n {content}\n </div>\n </div>\n )\n}\n","import { useState, useEffect, useRef, useMemo } from 'react'\nimport type { Node, Edge, Position } from '@xyflow/react'\n\nexport interface LayoutOptions {\n direction: 'TB' | 'LR'\n nodeWidth: number\n nodeSep: number\n rankSep: number\n}\n\nexport const defaultLayoutOptions: LayoutOptions = {\n direction: 'LR',\n nodeWidth: 340,\n nodeSep: 150,\n rankSep: 350,\n}\n\n/**\n * Layout state machine phases:\n * waiting → ELK not loaded yet, nothing to show\n * computing → ELK loaded, running layout algorithm\n * ready → layout complete, node positions available\n */\nexport type LayoutPhase = 'waiting' | 'computing' | 'ready'\n\nfunction getTargetPosition(direction: string): Position {\n switch (direction) {\n case 'TB': return 'top' as Position\n case 'LR': return 'left' as Position\n default: return 'top' as Position\n }\n}\n\nfunction getSourcePosition(direction: string): Position {\n switch (direction) {\n case 'TB': return 'bottom' as Position\n case 'LR': return 'right' as Position\n default: return 'bottom' as Position\n }\n}\n\n/**\n * Estimate the rendered pixel height of a CubeNode.\n */\nfunction estimateNodeHeight(node: Node): number {\n const cube = node.data?.cube as {\n measures?: unknown[]\n dimensions?: { type?: string }[]\n description?: string\n } | undefined\n if (!cube) return 300\n\n const HEADER = cube.description ? 80 : 56\n const SECTION_HEADER = 36\n const ROW = 34\n const MAX_BODY = 256\n const PADDING = 30\n\n let h = HEADER\n const measures = cube.measures?.length ?? 0\n const timeDims = cube.dimensions?.filter((d) => d.type === 'time').length ?? 0\n const regularDims = cube.dimensions?.filter((d) => d.type !== 'time').length ?? 0\n\n if (measures > 0) h += SECTION_HEADER + Math.min(measures * ROW, MAX_BODY)\n if (timeDims > 0) h += SECTION_HEADER + Math.min(timeDims * ROW, MAX_BODY)\n if (regularDims > 0) h += SECTION_HEADER + Math.min(regularDims * ROW, MAX_BODY)\n\n return h + PADDING\n}\n\n// --- ELK singleton (module-level, loaded once) ---\n\ninterface ELKInstance {\n layout(graph: unknown): Promise<unknown>\n}\n\nlet elkInstance: ELKInstance | null = null\nlet elkLoadPromise: Promise<ELKInstance | null> | null = null\n\nfunction loadElk(): Promise<ELKInstance | null> {\n if (elkLoadPromise) return elkLoadPromise\n // Use dynamic specifier so no bundler (Rollup, webpack) can statically resolve this.\n // elkjs is an optional peer dependency - if not installed, the catch() handles it.\n const specifier = ['elkjs', 'lib', 'elk.bundled.js'].join('/')\n elkLoadPromise = import(/* @vite-ignore */ specifier)\n .then((mod) => {\n const ELK = (mod as unknown as { default?: new () => ELKInstance }).default || mod\n elkInstance = new (ELK as unknown as new () => ELKInstance)()\n return elkInstance\n })\n .catch(() => null)\n return elkLoadPromise\n}\n\n// Force-export to prevent tree shaking of the side-effectful import\nexport { loadElk as _elkLoadPromise }\n\n// --- ELK layout computation (pure async function, no hooks) ---\n\ninterface ElkPort {\n id: string\n layoutOptions: Record<string, string>\n}\n\ninterface ElkNode {\n id: string\n width: number\n height: number\n layoutOptions?: Record<string, string>\n ports?: ElkPort[]\n}\n\ninterface ElkEdge {\n id: string\n sources: string[]\n targets: string[]\n}\n\ninterface ElkResultNode { id: string; x: number; y: number }\n\ninterface ElkResult {\n children?: ElkResultNode[]\n}\n\nexport interface ElkLayoutResult {\n nodes: Node[]\n edges: Edge[]\n}\n\nasync function computeElkLayout(\n nodes: Node[],\n edges: Edge[],\n opts: LayoutOptions,\n): Promise<ElkLayoutResult> {\n // Ensure ELK is loaded (lazy, webpack-safe)\n await loadElk()\n if (!elkInstance) {\n // ELK failed to load — return grid fallback\n return { nodes: layoutWithGrid(nodes, opts), edges }\n }\n\n const sourceSide = opts.direction === 'LR' ? 'EAST' : 'SOUTH'\n const targetSide = opts.direction === 'LR' ? 'WEST' : 'NORTH'\n\n // Per-edge ports so ELK spreads connections along the node side\n const nodeSourceEdges = new Map<string, string[]>()\n const nodeTargetEdges = new Map<string, string[]>()\n edges.forEach(edge => {\n if (!nodeSourceEdges.has(edge.source)) nodeSourceEdges.set(edge.source, [])\n nodeSourceEdges.get(edge.source)!.push(edge.id)\n if (!nodeTargetEdges.has(edge.target)) nodeTargetEdges.set(edge.target, [])\n nodeTargetEdges.get(edge.target)!.push(edge.id)\n })\n\n const elkNodes: ElkNode[] = nodes.map((node) => {\n const w = opts.nodeWidth\n const h = estimateNodeHeight(node)\n\n const srcEdges = nodeSourceEdges.get(node.id) || []\n const tgtEdges = nodeTargetEdges.get(node.id) || []\n const ports: ElkPort[] = [\n ...srcEdges.map(edgeId => ({\n id: `${node.id}__src__${edgeId}`,\n layoutOptions: { 'elk.port.side': sourceSide },\n })),\n ...tgtEdges.map(edgeId => ({\n id: `${node.id}__tgt__${edgeId}`,\n layoutOptions: { 'elk.port.side': targetSide },\n })),\n ]\n\n return {\n id: node.id,\n width: w,\n height: h,\n layoutOptions: { 'elk.portConstraints': 'FIXED_SIDE' },\n ports,\n }\n })\n\n const elkEdges: ElkEdge[] = edges.map((edge) => ({\n id: edge.id,\n sources: [`${edge.source}__src__${edge.id}`],\n targets: [`${edge.target}__tgt__${edge.id}`],\n }))\n\n const elkDirection = opts.direction === 'LR' ? 'RIGHT' : 'DOWN'\n\n const graph = {\n id: 'root',\n layoutOptions: {\n 'elk.algorithm': 'layered',\n 'elk.direction': elkDirection,\n 'elk.edgeRouting': 'SPLINES',\n 'elk.layered.edgeRouting.splines.mode': 'CONSERVATIVE',\n 'elk.spacing.nodeNode': String(opts.nodeSep),\n 'elk.layered.spacing.nodeNodeBetweenLayers': String(opts.rankSep),\n 'elk.spacing.edgeNode': '60',\n 'elk.layered.spacing.edgeNodeBetweenLayers': '60',\n 'elk.spacing.edgeEdge': '25',\n 'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX',\n 'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',\n },\n children: elkNodes,\n edges: elkEdges,\n }\n\n const result = await elkInstance.layout(graph) as ElkResult\n\n // Extract node positions (ELK returns top-left, same as React Flow)\n const positionMap = new Map<string, { x: number; y: number }>()\n if (result.children) {\n for (const child of result.children) {\n positionMap.set(child.id, { x: child.x, y: child.y })\n }\n }\n\n const layoutedNodes = nodes.map((node) => ({\n ...node,\n position: positionMap.get(node.id) || node.position,\n targetPosition: getTargetPosition(opts.direction),\n sourcePosition: getSourcePosition(opts.direction),\n }))\n\n // Return positioned nodes + original edges.\n // Edge rendering is handled by React Flow's getBezierPath() which knows\n // the actual handle positions — much more accurate than ELK's spline routes.\n return { nodes: layoutedNodes, edges }\n}\n\nfunction layoutWithGrid(nodes: Node[], opts: LayoutOptions): Node[] {\n const COL_HEIGHT_LIMIT = 1200\n let col = 0\n let colY = 0\n\n return nodes.map((node) => {\n const h = estimateNodeHeight(node)\n if (colY + h > COL_HEIGHT_LIMIT && colY > 0) {\n col++\n colY = 0\n }\n const position = { x: col * (opts.nodeWidth + opts.nodeSep), y: colY }\n colY += h + 40\n\n return {\n ...node,\n position,\n sourcePosition: getSourcePosition(opts.direction),\n targetPosition: getTargetPosition(opts.direction),\n }\n })\n}\n\n// --- Hook: single computation per structure key, no races ---\n\n/**\n * Layout hook with explicit state machine.\n *\n * - Waits for ELK to load (phase: 'waiting')\n * - Runs layout once per unique node/edge structure (phase: 'computing')\n * - Returns stable result (phase: 'ready')\n *\n * The effect only depends on a string structure key, NOT object references.\n * This prevents re-renders from cancelling in-flight computations.\n */\nexport function useERDLayout(\n nodes: Node[],\n edges: Edge[],\n options: Partial<LayoutOptions> = {}\n): { nodes: Node[]; edges: Edge[]; phase: LayoutPhase } {\n const opts = useMemo(\n () => ({ ...defaultLayoutOptions, ...options }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [options.direction, options.nodeWidth, options.nodeSep, options.rankSep]\n )\n\n // Stable structure key — only changes when node/edge IDs change\n const structureKey = useMemo(() => {\n if (nodes.length === 0) return ''\n return nodes.map(n => n.id).sort().join(',') + '|' + edges.map(e => e.id).sort().join(',')\n }, [nodes, edges])\n\n // Refs hold latest values so the effect closure always reads current data\n // without needing them as dependencies\n const nodesRef = useRef(nodes)\n const edgesRef = useRef(edges)\n const optsRef = useRef(opts)\n nodesRef.current = nodes\n edgesRef.current = edges\n optsRef.current = opts\n\n const [phase, setPhase] = useState<LayoutPhase>('waiting')\n const [result, setResult] = useState<ElkLayoutResult | null>(null)\n const [resultKey, setResultKey] = useState('')\n\n // Single computation per structure key\n const computeIdRef = useRef(0)\n\n useEffect(() => {\n if (!structureKey) {\n setPhase('ready')\n setResult(null)\n setResultKey('')\n return\n }\n\n const id = ++computeIdRef.current\n setPhase('computing')\n\n computeElkLayout(nodesRef.current, edgesRef.current, optsRef.current)\n .then(layoutResult => {\n // Only apply if this is still the latest computation\n if (id !== computeIdRef.current) return\n setResult(layoutResult)\n setResultKey(structureKey)\n setPhase('ready')\n })\n .catch(() => {\n if (id !== computeIdRef.current) return\n // On error, use grid fallback\n setResult({\n nodes: layoutWithGrid(nodesRef.current, optsRef.current),\n edges: edgesRef.current,\n })\n setResultKey(structureKey)\n setPhase('ready')\n })\n }, [structureKey]) // ONLY depends on structure key — no object refs\n\n // Output: only return layout when it matches current structure\n if (phase === 'ready' && result && resultKey === structureKey) {\n return { nodes: result.nodes, edges: result.edges, phase: 'ready' }\n }\n\n return { nodes: [], edges: [], phase }\n}\n","import { useCallback, useMemo, useEffect, useState, useRef } from 'react'\nimport type { MouseEvent as ReactMouseEvent } from 'react'\nimport type { Node, Edge, NodeChange, EdgeChange } from '@xyflow/react'\nimport '@xyflow/react/dist/style.css'\n\nimport { useCubeContext } from '../../providers/CubeProvider'\nimport { CubeNode } from './CubeNode'\nimport { RelationshipEdge } from './RelationshipEdge'\nimport { FieldDetailPanel } from './FieldDetailPanel'\nimport type { FieldSelection } from './FieldDetailPanel'\nimport { useERDLayout } from './useERDLayout'\nimport { getIcon } from '../../icons'\nimport { useXyflow } from './xyflowContext'\n\nconst nodeTypes = { cubeNode: CubeNode }\nconst edgeTypes = { relationshipEdge: RelationshipEdge }\n\n/**\n * Inner component that uses useNodesInitialized() (must be inside <ReactFlow>).\n * Calls fitView exactly once per fitViewToken change, after all nodes are measured.\n */\nfunction FitViewOnReady({ token }: { token: number }) {\n const { useNodesInitialized, useReactFlow } = useXyflow()\n const nodesInitialized = useNodesInitialized()\n const { fitView } = useReactFlow()\n const appliedTokenRef = useRef(0)\n\n useEffect(() => {\n if (token === 0 || token === appliedTokenRef.current) return\n if (!nodesInitialized) return\n appliedTokenRef.current = token\n fitView({ padding: 0.1 })\n }, [token, nodesInitialized, fitView])\n\n return null\n}\n\n// Stable empty arrays (avoid new [] on every render)\nconst EMPTY_STRINGS: string[] = []\n\nexport interface SchemaVisualizationProps {\n className?: string\n onFieldClick?: (cubeName: string, fieldName: string, fieldType: 'measure' | 'dimension') => void\n highlightedCubes?: string[]\n highlightedFields?: string[]\n searchTerm?: string\n height?: string | number\n}\n\nfunction getRelationshipColor(relationship: string): string {\n switch (relationship) {\n case 'belongsTo': return '#10b981'\n case 'hasOne': return '#3b82f6'\n case 'hasMany': return '#f59e0b'\n case 'belongsToMany': return '#8b5cf6'\n default: return '#6b7280'\n }\n}\n\nexport function SchemaVisualization({\n className = '',\n onFieldClick,\n highlightedCubes,\n highlightedFields,\n searchTerm,\n height = '100%',\n}: SchemaVisualizationProps) {\n const {\n ReactFlow: ReactFlowComponent,\n Controls,\n MiniMap,\n Background,\n applyNodeChanges,\n } = useXyflow()\n const { meta, metaLoading, metaError } = useCubeContext()\n\n // Stabilize array props to avoid re-creating references every render\n const stableHighlightedCubes = highlightedCubes || EMPTY_STRINGS\n const stableHighlightedFields = highlightedFields || EMPTY_STRINGS\n\n const [contextMenu, setContextMenu] = useState<{ x: number; y: number } | null>(null)\n const [searchInput, setSearchInput] = useState(searchTerm || '')\n const [autoLayoutCounter, setAutoLayoutCounter] = useState(0)\n const [selectedField, setSelectedField] = useState<FieldSelection | null>(null)\n const [detailPosition, setDetailPosition] = useState<{ x: number; y: number } | null>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n\n // In standalone mode (no onFieldClick), clicking fields shows the detail panel\n const isStandaloneMode = !onFieldClick\n const [savedPositions, setSavedPositions] = useState<Record<string, { x: number; y: number }>>({})\n const [positionsLoaded, setPositionsLoaded] = useState(false)\n\n const SearchIcon = getIcon('search')\n const CloseIcon = getIcon('close')\n\n const effectiveSearchTerm = searchTerm !== undefined ? searchTerm : searchInput\n\n // Load saved positions from localStorage (once)\n useEffect(() => {\n try {\n const saved = localStorage.getItem('drizzle-cube-erd-node-positions')\n if (saved) setSavedPositions(JSON.parse(saved))\n } catch { /* ignore */ }\n setPositionsLoaded(true)\n }, [])\n\n // --- Structural data: only depends on meta (stable) ---\n // Separate structure (for layout) from presentation (highlights/search)\n\n const { structuralNodes, structuralEdges } = useMemo(() => {\n if (!meta) return { structuralNodes: [] as Node[], structuralEdges: [] as Edge[] }\n\n const nodes: Node[] = meta.cubes.map((cube, index) => ({\n id: cube.name,\n type: 'cubeNode',\n // Placeholder position — ELK will override for auto-layout\n position: { x: (index % 3) * 400, y: Math.floor(index / 3) * 300 },\n data: { cube },\n }))\n\n const edges: Edge[] = []\n meta.cubes.forEach(cube => {\n if (cube.relationships) {\n cube.relationships.forEach((rel, i) => {\n if (rel.relationship === 'belongsTo') return\n edges.push({\n id: `${cube.name}-${rel.targetCube}-${i}`,\n source: cube.name,\n target: rel.targetCube,\n type: 'relationshipEdge',\n data: { relationship: rel, joinFields: rel.joinFields || [] },\n animated: false,\n style: { stroke: getRelationshipColor(rel.relationship), strokeWidth: 2 },\n })\n })\n }\n })\n\n return { structuralNodes: nodes, structuralEdges: edges }\n }, [meta]) // Only meta — no highlights/search/callbacks\n\n // --- Layout decision ---\n const needsAutoLayout = autoLayoutCounter > 0 || (positionsLoaded && Object.keys(savedPositions).length === 0)\n\n // Run ELK layout (only when auto-layout needed, otherwise pass empty to skip)\n const { nodes: elkNodes, edges: elkEdges, phase: layoutPhase } = useERDLayout(\n needsAutoLayout ? structuralNodes : [],\n needsAutoLayout ? structuralEdges : [],\n { direction: 'LR', nodeWidth: 340, nodeSep: 150, rankSep: 350 }\n )\n\n // --- Build display data: merge layout positions + presentation data ---\n\n // Convert client coords to container-relative, clamped to keep panel in view\n const toContainerPos = useCallback((clientX: number, clientY: number) => {\n const rect = containerRef.current?.getBoundingClientRect()\n if (!rect) return { x: clientX, y: clientY }\n const panelW = 300, panelH = 200 // approximate panel size for clamping\n let x = clientX - rect.left + 12 // offset slightly right of cursor\n let y = clientY - rect.top + 12 // offset slightly below cursor\n // Clamp so panel doesn't overflow right/bottom\n if (x + panelW > rect.width) x = clientX - rect.left - panelW - 12\n if (y + panelH > rect.height) y = rect.height - panelH - 8\n if (x < 0) x = 8\n if (y < 0) y = 8\n return { x, y }\n }, [])\n\n // Internal field click handler for standalone mode\n const handleInternalFieldClick = useCallback((cubeName: string, fieldName: string, fieldType: 'measure' | 'dimension', pos?: { x: number; y: number }) => {\n setSelectedField(prev => {\n if (prev && prev.cubeName === cubeName && prev.fieldName === fieldName) {\n setDetailPosition(null)\n return null\n }\n if (pos) setDetailPosition(toContainerPos(pos.x, pos.y))\n return { cubeName, fieldName, fieldType }\n })\n }, [toContainerPos])\n\n const handleInternalCubeClick = useCallback((cubeName: string, pos?: { x: number; y: number }) => {\n setSelectedField(prev => {\n if (prev && prev.cubeName === cubeName && prev.fieldName === null) {\n setDetailPosition(null)\n return null\n }\n if (pos) setDetailPosition(toContainerPos(pos.x, pos.y))\n return { cubeName, fieldName: null, fieldType: 'cube' }\n })\n }, [toContainerPos])\n\n const effectiveFieldClick = isStandaloneMode ? handleInternalFieldClick : onFieldClick\n const effectiveCubeClick = isStandaloneMode ? handleInternalCubeClick : undefined\n\n const buildNodeData = useCallback((cube: unknown) => ({\n cube,\n onFieldClick: effectiveFieldClick,\n onCubeClick: effectiveCubeClick,\n isHighlighted: stableHighlightedCubes.includes((cube as { name: string }).name),\n highlightedFields: stableHighlightedFields,\n searchTerm: effectiveSearchTerm,\n selectedField: isStandaloneMode ? selectedField : null,\n }), [effectiveFieldClick, effectiveCubeClick, stableHighlightedCubes, stableHighlightedFields, effectiveSearchTerm, isStandaloneMode, selectedField])\n\n // Determine if layout is resolved (ready to render ReactFlow)\n const layoutReady = !needsAutoLayout || layoutPhase === 'ready'\n\n const displayNodes = useMemo(() => {\n if (!meta || !layoutReady || !positionsLoaded) return []\n\n if (needsAutoLayout && elkNodes.length > 0) {\n // Use ELK-positioned nodes, inject presentation data\n return elkNodes.map(node => ({\n ...node,\n data: buildNodeData(node.data?.cube || meta.cubes.find(c => c.name === node.id)),\n }))\n }\n\n // Use saved positions\n return structuralNodes.map(node => ({\n ...node,\n position: savedPositions[node.id] || node.position,\n data: buildNodeData(node.data?.cube),\n }))\n }, [meta, layoutReady, positionsLoaded, needsAutoLayout, elkNodes, structuralNodes, savedPositions, buildNodeData])\n\n const displayEdges = useMemo(() => {\n if (!meta || !layoutReady) return []\n if (needsAutoLayout && elkEdges.length > 0) return elkEdges\n return structuralEdges\n }, [meta, layoutReady, needsAutoLayout, elkEdges, structuralEdges])\n\n // --- Controlled ReactFlow state ---\n const [rfNodes, setRfNodes] = useState<Node[]>([])\n const [rfEdges, setRfEdges] = useState<Edge[]>([])\n const appliedLayoutKeyRef = useRef('')\n const [fitViewToken, setFitViewToken] = useState(0) // increment to request fitView\n\n // Push layout to ReactFlow — only when positions change (not on data-only changes)\n useEffect(() => {\n if (displayNodes.length === 0) return\n\n const key = displayNodes.map(n => `${n.id}:${Math.round(n.position.x)},${Math.round(n.position.y)}`).join('|')\n if (key === appliedLayoutKeyRef.current) return\n\n const isFirstLayout = appliedLayoutKeyRef.current === ''\n appliedLayoutKeyRef.current = key\n setRfNodes(displayNodes)\n setRfEdges(displayEdges)\n\n // Request fitView on first layout\n if (isFirstLayout) {\n setFitViewToken(t => t + 1)\n }\n }, [displayNodes, displayEdges])\n\n // Update presentation data (highlights, search) without resetting positions\n const prevPresentationRef = useRef('')\n useEffect(() => {\n const selectedKey = selectedField ? `${selectedField.cubeName}.${selectedField.fieldName}` : ''\n const presentationKey = `${stableHighlightedCubes.join(',')}|${stableHighlightedFields.join(',')}|${effectiveSearchTerm}|${String(onFieldClick)}|${selectedKey}`\n if (presentationKey === prevPresentationRef.current) return\n prevPresentationRef.current = presentationKey\n\n if (rfNodes.length === 0 || !meta) return\n setRfNodes(prev => prev.map(node => {\n const cube = meta.cubes.find(c => c.name === node.id)\n if (!cube) return node\n return { ...node, data: buildNodeData(cube) }\n }))\n }, [stableHighlightedCubes, stableHighlightedFields, effectiveSearchTerm, onFieldClick, selectedField, rfNodes.length, meta, buildNodeData])\n\n // Handle node changes (dragging)\n const handleNodesChange = useCallback((changes: NodeChange[]) => {\n setRfNodes(nds => applyNodeChanges(changes, nds))\n\n // Save positions on drag end\n const dragEnds = changes.filter(\n (c) => c.type === 'position' && 'dragging' in c && c.dragging === false\n )\n if (dragEnds.length > 0) {\n setRfNodes(currentNodes => {\n const positions: Record<string, { x: number; y: number }> = {}\n currentNodes.forEach(n => { if (n.position) positions[n.id] = n.position })\n try { localStorage.setItem('drizzle-cube-erd-node-positions', JSON.stringify(positions)) } catch { /* ignore */ }\n setSavedPositions(positions)\n return currentNodes\n })\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const handleEdgesChange = useCallback((_changes: EdgeChange[]) => {}, [])\n\n const handleContextMenu = useCallback((event: ReactMouseEvent | MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n setContextMenu({ x: event.clientX, y: event.clientY })\n }, [])\n\n const handleClick = useCallback(() => {\n if (contextMenu) setContextMenu(null)\n if (selectedField) { setSelectedField(null); setDetailPosition(null) }\n }, [contextMenu, selectedField])\n\n const handleAutoLayout = useCallback(() => {\n setSavedPositions({})\n appliedLayoutKeyRef.current = '' // force re-apply → layout effect will request fitView\n try { localStorage.removeItem('drizzle-cube-erd-node-positions') } catch { /* ignore */ }\n setAutoLayoutCounter(c => c + 1)\n setContextMenu(null)\n }, [])\n\n // --- Render ---\n\n if (metaLoading) {\n return (\n <div className={`dc:flex dc:items-center dc:justify-center dc:h-96 ${className}`}>\n <div className=\"dc:text-center\">\n <div className=\"dc:animate-spin dc:rounded-full dc:h-8 dc:w-8 dc:border-b-2 border-dc-accent dc:mx-auto dc:mb-2\" />\n <p className=\"text-dc-text-muted\">Loading cube schema...</p>\n </div>\n </div>\n )\n }\n\n if (metaError) {\n return (\n <div className={`dc:flex dc:items-center dc:justify-center dc:h-96 ${className}`}>\n <div className=\"dc:text-center text-dc-error\">\n <p className=\"dc:font-medium\">Failed to load cube schema</p>\n <p className=\"dc:text-sm dc:mt-1\">{metaError}</p>\n </div>\n </div>\n )\n }\n\n if (!meta || meta.cubes.length === 0) {\n return (\n <div className={`dc:flex dc:items-center dc:justify-center dc:h-96 ${className}`}>\n <div className=\"dc:text-center text-dc-text-muted\">\n <p className=\"dc:font-medium\">No cubes found</p>\n <p className=\"dc:text-sm dc:mt-1\">Register some cubes to see the relationship diagram</p>\n </div>\n </div>\n )\n }\n\n // Show loading while ELK computes layout (no grid flash)\n if (!layoutReady) {\n return (\n <div className={`dc:flex dc:items-center dc:justify-center dc:h-96 ${className}`}>\n <div className=\"dc:text-center\">\n <div className=\"dc:animate-spin dc:rounded-full dc:h-8 dc:w-8 dc:border-b-2 border-dc-accent dc:mx-auto dc:mb-2\" />\n <p className=\"text-dc-text-muted\">Computing layout...</p>\n </div>\n </div>\n )\n }\n\n return (\n <div className={`dc:flex dc:flex-col ${className}`} style={{ height, minHeight: 400 }}>\n {searchTerm === undefined && (\n <div className=\"dc:px-3 dc:py-2 dc:border-b border-dc-border bg-dc-surface dc:flex dc:items-center dc:gap-2 dc:flex-shrink-0\">\n <SearchIcon className=\"dc:w-4 dc:h-4 text-dc-text-muted\" />\n <input\n type=\"text\"\n value={searchInput}\n onChange={(e) => setSearchInput(e.target.value)}\n placeholder=\"Search cubes and fields...\"\n className=\"dc:flex-1 dc:text-sm dc:bg-transparent dc:outline-none text-dc-text dc:placeholder:text-dc-text-muted\"\n />\n {searchInput && (\n <button onClick={() => setSearchInput('')} className=\"text-dc-text-muted dc:hover:text-dc-text\">\n <CloseIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n )}\n </div>\n )}\n\n <div ref={containerRef} className=\"dc:relative dc:flex-1 dc:min-h-0\">\n <div style={{ position: 'absolute', inset: 0 }}>\n <ReactFlowComponent\n nodes={rfNodes}\n edges={rfEdges}\n onNodesChange={handleNodesChange}\n onEdgesChange={handleEdgesChange}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n connectionMode={'loose' as never}\n minZoom={0.1}\n maxZoom={2}\n proOptions={{ hideAttribution: true }}\n onPaneContextMenu={handleContextMenu}\n onPaneClick={handleClick}\n >\n <Controls />\n <MiniMap\n nodeColor={(node) => stableHighlightedCubes.includes(node.id) ? '#8b5cf6' : '#e5e7eb'}\n maskColor=\"rgb(240, 242, 246, 0.7)\"\n />\n <Background variant={'dots' as never} gap={12} size={1} />\n <FitViewOnReady token={fitViewToken} />\n </ReactFlowComponent>\n </div>\n\n {/* Field detail panel for standalone browse mode */}\n {isStandaloneMode && selectedField && detailPosition && meta && (\n <div\n className=\"dc:absolute dc:z-20\"\n style={{ left: detailPosition.x, top: detailPosition.y }}\n >\n <FieldDetailPanel\n selection={selectedField}\n meta={meta}\n onClose={() => { setSelectedField(null); setDetailPosition(null) }}\n />\n </div>\n )}\n </div>\n\n {contextMenu && (\n <div\n className=\"dc:fixed dc:z-50 bg-dc-surface dc:rounded-md dc:shadow-lg dc:border border-dc-border dc:py-1 dc:min-w-[120px]\"\n style={{ left: contextMenu.x, top: contextMenu.y }}\n >\n <button\n onClick={handleAutoLayout}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm text-dc-text-secondary dc:hover:bg-dc-surface-hover dc:text-left\"\n >\n Auto Layout\n </button>\n </div>\n )}\n </div>\n )\n}\n\nexport default SchemaVisualization\n"],"mappings":"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"}
@@ -117,4 +117,4 @@ function e(e) {
117
117
  //#endregion
118
118
  export { e as default };
119
119
 
120
- //# sourceMappingURL=sql-B0chxcEK.js.map
120
+ //# sourceMappingURL=sql-D2qikO5q.js.map