@memberjunction/ng-artifacts 5.27.1 → 5.29.0

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 (65) hide show
  1. package/dist/lib/__tests__/artifact-snapshots.test.d.ts +2 -0
  2. package/dist/lib/__tests__/artifact-snapshots.test.d.ts.map +1 -0
  3. package/dist/lib/__tests__/artifact-snapshots.test.js +155 -0
  4. package/dist/lib/__tests__/artifact-snapshots.test.js.map +1 -0
  5. package/dist/lib/components/artifact-viewer-panel.component.d.ts +16 -2
  6. package/dist/lib/components/artifact-viewer-panel.component.d.ts.map +1 -1
  7. package/dist/lib/components/artifact-viewer-panel.component.js +128 -98
  8. package/dist/lib/components/artifact-viewer-panel.component.js.map +1 -1
  9. package/dist/lib/components/base-artifact-viewer.component.d.ts +24 -0
  10. package/dist/lib/components/base-artifact-viewer.component.d.ts.map +1 -1
  11. package/dist/lib/components/base-artifact-viewer.component.js +15 -1
  12. package/dist/lib/components/base-artifact-viewer.component.js.map +1 -1
  13. package/dist/lib/components/plugins/code-artifact-viewer.component.d.ts +2 -0
  14. package/dist/lib/components/plugins/code-artifact-viewer.component.d.ts.map +1 -1
  15. package/dist/lib/components/plugins/code-artifact-viewer.component.js +5 -1
  16. package/dist/lib/components/plugins/code-artifact-viewer.component.js.map +1 -1
  17. package/dist/lib/components/plugins/component-artifact-viewer.component.d.ts +2 -1
  18. package/dist/lib/components/plugins/component-artifact-viewer.component.d.ts.map +1 -1
  19. package/dist/lib/components/plugins/component-artifact-viewer.component.js +29 -0
  20. package/dist/lib/components/plugins/component-artifact-viewer.component.js.map +1 -1
  21. package/dist/lib/components/plugins/data-artifact-viewer.component.d.ts +50 -43
  22. package/dist/lib/components/plugins/data-artifact-viewer.component.d.ts.map +1 -1
  23. package/dist/lib/components/plugins/data-artifact-viewer.component.js +336 -137
  24. package/dist/lib/components/plugins/data-artifact-viewer.component.js.map +1 -1
  25. package/dist/lib/components/plugins/docx-artifact-viewer.component.d.ts +2 -0
  26. package/dist/lib/components/plugins/docx-artifact-viewer.component.d.ts.map +1 -1
  27. package/dist/lib/components/plugins/docx-artifact-viewer.component.js +21 -6
  28. package/dist/lib/components/plugins/docx-artifact-viewer.component.js.map +1 -1
  29. package/dist/lib/components/plugins/html-artifact-viewer.component.d.ts +2 -0
  30. package/dist/lib/components/plugins/html-artifact-viewer.component.d.ts.map +1 -1
  31. package/dist/lib/components/plugins/html-artifact-viewer.component.js +11 -1
  32. package/dist/lib/components/plugins/html-artifact-viewer.component.js.map +1 -1
  33. package/dist/lib/components/plugins/json-artifact-viewer.component.d.ts +2 -0
  34. package/dist/lib/components/plugins/json-artifact-viewer.component.d.ts.map +1 -1
  35. package/dist/lib/components/plugins/json-artifact-viewer.component.js +5 -1
  36. package/dist/lib/components/plugins/json-artifact-viewer.component.js.map +1 -1
  37. package/dist/lib/components/plugins/markdown-artifact-viewer.component.d.ts +2 -0
  38. package/dist/lib/components/plugins/markdown-artifact-viewer.component.d.ts.map +1 -1
  39. package/dist/lib/components/plugins/markdown-artifact-viewer.component.js +5 -1
  40. package/dist/lib/components/plugins/markdown-artifact-viewer.component.js.map +1 -1
  41. package/dist/lib/components/plugins/pdf-artifact-viewer.component.d.ts +2 -0
  42. package/dist/lib/components/plugins/pdf-artifact-viewer.component.d.ts.map +1 -1
  43. package/dist/lib/components/plugins/pdf-artifact-viewer.component.js +29 -10
  44. package/dist/lib/components/plugins/pdf-artifact-viewer.component.js.map +1 -1
  45. package/dist/lib/components/plugins/svg-artifact-viewer.component.d.ts +2 -0
  46. package/dist/lib/components/plugins/svg-artifact-viewer.component.d.ts.map +1 -1
  47. package/dist/lib/components/plugins/svg-artifact-viewer.component.js +11 -1
  48. package/dist/lib/components/plugins/svg-artifact-viewer.component.js.map +1 -1
  49. package/dist/lib/components/plugins/xlsx-artifact-viewer.component.d.ts +2 -0
  50. package/dist/lib/components/plugins/xlsx-artifact-viewer.component.d.ts.map +1 -1
  51. package/dist/lib/components/plugins/xlsx-artifact-viewer.component.js +39 -13
  52. package/dist/lib/components/plugins/xlsx-artifact-viewer.component.js.map +1 -1
  53. package/dist/lib/services/analyze-artifact.service.d.ts +77 -0
  54. package/dist/lib/services/analyze-artifact.service.d.ts.map +1 -0
  55. package/dist/lib/services/analyze-artifact.service.js +120 -0
  56. package/dist/lib/services/analyze-artifact.service.js.map +1 -0
  57. package/dist/lib/snapshot-helpers.d.ts +48 -0
  58. package/dist/lib/snapshot-helpers.d.ts.map +1 -0
  59. package/dist/lib/snapshot-helpers.js +79 -0
  60. package/dist/lib/snapshot-helpers.js.map +1 -0
  61. package/dist/public-api.d.ts +2 -0
  62. package/dist/public-api.d.ts.map +1 -1
  63. package/dist/public-api.js +3 -0
  64. package/dist/public-api.js.map +1 -1
  65. package/package.json +16 -16
@@ -6,16 +6,18 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  import { Component, Output, EventEmitter } from '@angular/core';
8
8
  import { RegisterClass } from '@memberjunction/global';
9
- import { Metadata, RunQuery, CompositeKey, KeyValuePair } from '@memberjunction/core';
9
+ import { DataSnapshot, NormalizeToTables, Metadata, RunQuery, CompositeKey, KeyValuePair } from '@memberjunction/core';
10
10
  import { QueryEngine, ArtifactMetadataEngine } from '@memberjunction/core-entities';
11
11
  import { resolveTargetEntity } from '@memberjunction/ng-query-viewer';
12
12
  import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
13
+ import { buildMultiTableSql } from '../../snapshot-helpers';
13
14
  import * as i0 from "@angular/core";
14
15
  import * as i1 from "@angular/common";
15
16
  import * as i2 from "@memberjunction/ng-markdown";
16
17
  import * as i3 from "@memberjunction/ng-query-viewer";
17
18
  import * as i4 from "@memberjunction/ng-shared-generic";
18
19
  import * as i5 from "./save-query-dialog.component";
20
+ const _forTrack0 = ($index, $item) => $item.name;
19
21
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_5_Template(rf, ctx) { if (rf & 1) {
20
22
  i0.ɵɵelementStart(0, "span", 7);
21
23
  i0.ɵɵtext(1, "Live");
@@ -41,9 +43,9 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_7_T
41
43
  } }
42
44
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_9_Template(rf, ctx) { if (rf & 1) {
43
45
  const _r2 = i0.ɵɵgetCurrentView();
44
- i0.ɵɵelementStart(0, "button", 17);
46
+ i0.ɵɵelementStart(0, "button", 19);
45
47
  i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_9_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnRefresh()); });
46
- i0.ɵɵelement(1, "i", 18);
48
+ i0.ɵɵelement(1, "i", 20);
47
49
  i0.ɵɵtext(2, " Refresh ");
48
50
  i0.ɵɵelementEnd();
49
51
  } if (rf & 2) {
@@ -54,21 +56,21 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_9_T
54
56
  } }
55
57
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_10_Template(rf, ctx) { if (rf & 1) {
56
58
  const _r3 = i0.ɵɵgetCurrentView();
57
- i0.ɵɵelementStart(0, "button", 19);
59
+ i0.ɵɵelementStart(0, "button", 21);
58
60
  i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_10_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnShowSaveDialog()); });
59
- i0.ɵɵelement(1, "i", 20);
60
- i0.ɵɵtext(2, " Save Query ");
61
+ i0.ɵɵelement(1, "i", 22);
62
+ i0.ɵɵtext(2, " Save Query");
61
63
  i0.ɵɵelementEnd();
62
64
  } }
63
65
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_11_Template(rf, ctx) { if (rf & 1) {
64
66
  const _r4 = i0.ɵɵgetCurrentView();
65
- i0.ɵɵelementStart(0, "span", 21);
66
- i0.ɵɵelement(1, "i", 22);
67
+ i0.ɵɵelementStart(0, "span", 23);
68
+ i0.ɵɵelement(1, "i", 24);
67
69
  i0.ɵɵtext(2);
68
70
  i0.ɵɵelementEnd();
69
- i0.ɵɵelementStart(3, "button", 23);
71
+ i0.ɵɵelementStart(3, "button", 25);
70
72
  i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_11_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnOpenSavedQuery()); });
71
- i0.ɵɵelement(4, "i", 24);
73
+ i0.ɵɵelement(4, "i", 26);
72
74
  i0.ɵɵtext(5, " Open Query ");
73
75
  i0.ɵɵelementEnd();
74
76
  } if (rf & 2) {
@@ -78,33 +80,33 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_11_Templat
78
80
  } }
79
81
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template(rf, ctx) { if (rf & 1) {
80
82
  const _r6 = i0.ɵɵgetCurrentView();
81
- i0.ɵɵelementStart(0, "div", 31);
83
+ i0.ɵɵelementStart(0, "div", 33);
82
84
  i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r6); return i0.ɵɵresetView($event.stopPropagation()); });
83
- i0.ɵɵelementStart(1, "div", 32);
85
+ i0.ɵɵelementStart(1, "div", 34);
84
86
  i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template_div_click_1_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnUpdateExistingQuery()); });
85
- i0.ɵɵelement(2, "i", 33);
86
- i0.ɵɵelementStart(3, "div")(4, "div", 34);
87
+ i0.ɵɵelement(2, "i", 35);
88
+ i0.ɵɵelementStart(3, "div")(4, "div", 36);
87
89
  i0.ɵɵtext(5);
88
90
  i0.ɵɵelementEnd();
89
- i0.ɵɵelementStart(6, "div", 35);
91
+ i0.ɵɵelementStart(6, "div", 37);
90
92
  i0.ɵɵtext(7, "Replace saved query SQL with this version's SQL");
91
93
  i0.ɵɵelementEnd()()();
92
- i0.ɵɵelementStart(8, "div", 32);
94
+ i0.ɵɵelementStart(8, "div", 34);
93
95
  i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template_div_click_8_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnSaveAsNewQuery()); });
94
- i0.ɵɵelement(9, "i", 36);
95
- i0.ɵɵelementStart(10, "div")(11, "div", 34);
96
+ i0.ɵɵelement(9, "i", 38);
97
+ i0.ɵɵelementStart(10, "div")(11, "div", 36);
96
98
  i0.ɵɵtext(12, "Save as New Query");
97
99
  i0.ɵɵelementEnd();
98
- i0.ɵɵelementStart(13, "div", 35);
100
+ i0.ɵɵelementStart(13, "div", 37);
99
101
  i0.ɵɵtext(14, "Create a new query, keep the original unchanged");
100
102
  i0.ɵɵelementEnd()()();
101
- i0.ɵɵelementStart(15, "div", 32);
103
+ i0.ɵɵelementStart(15, "div", 34);
102
104
  i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template_div_click_15_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(4); ctx_r0.OnOpenSavedQuery(); return i0.ɵɵresetView(ctx_r0.OnCloseDropdown()); });
103
- i0.ɵɵelement(16, "i", 37);
104
- i0.ɵɵelementStart(17, "div")(18, "div", 34);
105
+ i0.ɵɵelement(16, "i", 39);
106
+ i0.ɵɵelementStart(17, "div")(18, "div", 36);
105
107
  i0.ɵɵtext(19, "Open Saved Query");
106
108
  i0.ɵɵelementEnd();
107
- i0.ɵɵelementStart(20, "div", 35);
109
+ i0.ɵɵelementStart(20, "div", 37);
108
110
  i0.ɵɵtext(21);
109
111
  i0.ɵɵelementEnd()()()();
110
112
  } if (rf & 2) {
@@ -116,17 +118,17 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditi
116
118
  } }
117
119
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Template(rf, ctx) { if (rf & 1) {
118
120
  const _r5 = i0.ɵɵgetCurrentView();
119
- i0.ɵɵelementStart(0, "span", 25);
120
- i0.ɵɵelement(1, "i", 26);
121
+ i0.ɵɵelementStart(0, "span", 27);
122
+ i0.ɵɵelement(1, "i", 28);
121
123
  i0.ɵɵtext(2);
122
124
  i0.ɵɵelementEnd();
123
- i0.ɵɵelementStart(3, "div", 27)(4, "button", 28);
125
+ i0.ɵɵelementStart(3, "div", 29)(4, "button", 30);
124
126
  i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnToggleUpdateDropdown()); });
125
- i0.ɵɵelement(5, "i", 20);
127
+ i0.ɵɵelement(5, "i", 22);
126
128
  i0.ɵɵtext(6, " Update Query ");
127
- i0.ɵɵelement(7, "i", 29);
129
+ i0.ɵɵelement(7, "i", 31);
128
130
  i0.ɵɵelementEnd();
129
- i0.ɵɵconditionalCreate(8, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template, 22, 3, "div", 30);
131
+ i0.ɵɵconditionalCreate(8, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Conditional_8_Template, 22, 3, "div", 32);
130
132
  i0.ɵɵelementEnd();
131
133
  } if (rf & 2) {
132
134
  const ctx_r0 = i0.ɵɵnextContext(3);
@@ -137,13 +139,13 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Templat
137
139
  } }
138
140
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_13_Template(rf, ctx) { if (rf & 1) {
139
141
  const _r7 = i0.ɵɵgetCurrentView();
140
- i0.ɵɵelementStart(0, "span", 38);
141
- i0.ɵɵelement(1, "i", 39);
142
+ i0.ɵɵelementStart(0, "span", 40);
143
+ i0.ɵɵelement(1, "i", 41);
142
144
  i0.ɵɵtext(2);
143
145
  i0.ɵɵelementEnd();
144
- i0.ɵɵelementStart(3, "button", 40);
146
+ i0.ɵɵelementStart(3, "button", 42);
145
147
  i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_13_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r7); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnOpenSavedQuery()); });
146
- i0.ɵɵelement(4, "i", 24);
148
+ i0.ɵɵelement(4, "i", 26);
147
149
  i0.ɵɵtext(5, " Open Query ");
148
150
  i0.ɵɵelementEnd();
149
151
  } if (rf & 2) {
@@ -153,13 +155,13 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_13_Templat
153
155
  } }
154
156
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_14_Template(rf, ctx) { if (rf & 1) {
155
157
  const _r8 = i0.ɵɵgetCurrentView();
156
- i0.ɵɵelementStart(0, "span", 38);
157
- i0.ɵɵelement(1, "i", 39);
158
+ i0.ɵɵelementStart(0, "span", 40);
159
+ i0.ɵɵelement(1, "i", 41);
158
160
  i0.ɵɵtext(2);
159
161
  i0.ɵɵelementEnd();
160
- i0.ɵɵelementStart(3, "button", 40);
162
+ i0.ɵɵelementStart(3, "button", 42);
161
163
  i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_14_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r8); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnOpenSavedQuery()); });
162
- i0.ɵɵelement(4, "i", 24);
164
+ i0.ɵɵelement(4, "i", 26);
163
165
  i0.ɵɵtext(5, " Open Query ");
164
166
  i0.ɵɵelementEnd();
165
167
  } if (rf & 2) {
@@ -168,30 +170,99 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_14_Templat
168
170
  i0.ɵɵtextInterpolate1(" Saved at v", ctx_r0.SavedAtVersion, " ");
169
171
  } }
170
172
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_15_Template(rf, ctx) { }
173
+ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_16_For_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
174
+ i0.ɵɵelementStart(0, "span", 46);
175
+ i0.ɵɵtext(1);
176
+ i0.ɵɵelementEnd();
177
+ } if (rf & 2) {
178
+ const table_r11 = i0.ɵɵnextContext().$implicit;
179
+ i0.ɵɵadvance();
180
+ i0.ɵɵtextInterpolate1(" (", (table_r11.metadata == null ? null : table_r11.metadata.rowCount) ?? table_r11.rows.length, " rows) ");
181
+ } }
182
+ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_16_For_2_Template(rf, ctx) { if (rf & 1) {
183
+ const _r9 = i0.ɵɵgetCurrentView();
184
+ i0.ɵɵelementStart(0, "button", 44);
185
+ i0.ɵɵlistener("click", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_16_For_2_Template_button_click_0_listener() { const ɵ$index_131_r10 = i0.ɵɵrestoreView(_r9).$index; const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnTableTabClick(ɵ$index_131_r10)); });
186
+ i0.ɵɵelementStart(1, "span", 45);
187
+ i0.ɵɵtext(2);
188
+ i0.ɵɵelementEnd();
189
+ i0.ɵɵconditionalCreate(3, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_16_For_2_Conditional_3_Template, 2, 1, "span", 46);
190
+ i0.ɵɵelementEnd();
191
+ } if (rf & 2) {
192
+ const table_r11 = ctx.$implicit;
193
+ const ɵ$index_131_r10 = ctx.$index;
194
+ const ctx_r0 = i0.ɵɵnextContext(4);
195
+ i0.ɵɵclassProp("active", ɵ$index_131_r10 === ctx_r0.ActiveTableIndex);
196
+ i0.ɵɵproperty("title", table_r11.description || table_r11.name);
197
+ i0.ɵɵadvance(2);
198
+ i0.ɵɵtextInterpolate(table_r11.name);
199
+ i0.ɵɵadvance();
200
+ i0.ɵɵconditional((table_r11.metadata == null ? null : table_r11.metadata.rowCount) != null || table_r11.rows.length ? 3 : -1);
201
+ } }
202
+ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_16_Template(rf, ctx) { if (rf & 1) {
203
+ i0.ɵɵelementStart(0, "div", 13);
204
+ i0.ɵɵrepeaterCreate(1, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_16_For_2_Template, 4, 5, "button", 43, _forTrack0);
205
+ i0.ɵɵelementEnd();
206
+ } if (rf & 2) {
207
+ const ctx_r0 = i0.ɵɵnextContext(3);
208
+ i0.ɵɵadvance();
209
+ i0.ɵɵrepeater(ctx_r0.ResolvedTables);
210
+ } }
211
+ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_17_For_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
212
+ i0.ɵɵelementStart(0, "div", 47)(1, "span", 48);
213
+ i0.ɵɵtext(2);
214
+ i0.ɵɵelementEnd();
215
+ i0.ɵɵelementStart(3, "span", 49);
216
+ i0.ɵɵtext(4);
217
+ i0.ɵɵelementEnd()();
218
+ } if (rf & 2) {
219
+ const comp_r12 = i0.ɵɵnextContext().$implicit;
220
+ i0.ɵɵproperty("title", comp_r12.description || comp_r12.name);
221
+ i0.ɵɵadvance(2);
222
+ i0.ɵɵtextInterpolate(comp_r12.name);
223
+ i0.ɵɵadvance(2);
224
+ i0.ɵɵtextInterpolate1(" ", comp_r12.formattedValue || comp_r12.value, " ");
225
+ } }
226
+ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_17_For_2_Template(rf, ctx) { if (rf & 1) {
227
+ i0.ɵɵconditionalCreate(0, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_17_For_2_Conditional_0_Template, 5, 3, "div", 47);
228
+ } if (rf & 2) {
229
+ const comp_r12 = ctx.$implicit;
230
+ const ctx_r0 = i0.ɵɵnextContext(4);
231
+ i0.ɵɵconditional(!ctx_r0.IsMultiTable || !comp_r12.table || comp_r12.table === (ctx_r0.ActiveTable == null ? null : ctx_r0.ActiveTable.name) ? 0 : -1);
232
+ } }
171
233
  function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_17_Template(rf, ctx) { if (rf & 1) {
172
234
  i0.ɵɵelementStart(0, "div", 14);
173
- i0.ɵɵelement(1, "mj-loading", 41);
235
+ i0.ɵɵrepeaterCreate(1, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_17_For_2_Template, 1, 1, null, null, _forTrack0);
236
+ i0.ɵɵelementEnd();
237
+ } if (rf & 2) {
238
+ const ctx_r0 = i0.ɵɵnextContext(3);
239
+ i0.ɵɵadvance();
240
+ i0.ɵɵrepeater(ctx_r0.spec.computations);
241
+ } }
242
+ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_Template(rf, ctx) { if (rf & 1) {
243
+ i0.ɵɵelementStart(0, "div", 16);
244
+ i0.ɵɵelement(1, "mj-loading", 50);
174
245
  i0.ɵɵelementEnd();
175
246
  } if (rf & 2) {
176
247
  const ctx_r0 = i0.ɵɵnextContext(3);
177
248
  i0.ɵɵadvance();
178
249
  i0.ɵɵproperty("text", ctx_r0.SavingMessage);
179
250
  } }
180
- function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_18_Template(rf, ctx) { if (rf & 1) {
181
- i0.ɵɵelement(0, "mj-loading", 15);
251
+ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_20_Template(rf, ctx) { if (rf & 1) {
252
+ i0.ɵɵelement(0, "mj-loading", 17);
182
253
  } }
183
- function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_Template(rf, ctx) { if (rf & 1) {
184
- const _r9 = i0.ɵɵgetCurrentView();
185
- i0.ɵɵelementStart(0, "div", 42);
186
- i0.ɵɵelement(1, "i", 43);
254
+ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template(rf, ctx) { if (rf & 1) {
255
+ const _r13 = i0.ɵɵgetCurrentView();
256
+ i0.ɵɵelementStart(0, "div", 51);
257
+ i0.ɵɵelement(1, "i", 52);
187
258
  i0.ɵɵelementStart(2, "span");
188
259
  i0.ɵɵtext(3);
189
260
  i0.ɵɵelementEnd();
190
- i0.ɵɵelementStart(4, "span", 44);
261
+ i0.ɵɵelementStart(4, "span", 53);
191
262
  i0.ɵɵtext(5, "(Showing cached data)");
192
263
  i0.ɵɵelementEnd()();
193
- i0.ɵɵelementStart(6, "mj-query-data-grid", 45);
194
- i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_Template_mj_query_data_grid_EntityLinkClick_6_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); })("PageChange", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_Template_mj_query_data_grid_PageChange_6_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnPageChange($event)); });
264
+ i0.ɵɵelementStart(6, "mj-query-data-grid", 54);
265
+ i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template_mj_query_data_grid_EntityLinkClick_6_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); })("PageChange", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template_mj_query_data_grid_PageChange_6_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnPageChange($event)); });
195
266
  i0.ɵɵelementEnd();
196
267
  } if (rf & 2) {
197
268
  const ctx_r0 = i0.ɵɵnextContext(3);
@@ -200,9 +271,9 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_
200
271
  i0.ɵɵadvance(3);
201
272
  i0.ɵɵproperty("ColumnConfigs", ctx_r0.GridColumnConfigs)("Data", ctx_r0.GridData)("TotalRowCount", ctx_r0.PagerTotalRowCount)("PageNumber", ctx_r0.PagerPageNumber)("PageSize", ctx_r0.PagerPageSize)("ShowToolbar", false)("ShowRefresh", false)("PersistState", false)("SelectionMode", "none");
202
273
  } }
203
- function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_20_Template(rf, ctx) { if (rf & 1) {
274
+ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_22_Template(rf, ctx) { if (rf & 1) {
204
275
  i0.ɵɵelementStart(0, "div", 1);
205
- i0.ɵɵelement(1, "i", 43);
276
+ i0.ɵɵelement(1, "i", 52);
206
277
  i0.ɵɵelementStart(2, "p");
207
278
  i0.ɵɵtext(3);
208
279
  i0.ɵɵelementEnd()();
@@ -211,10 +282,10 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_20_
211
282
  i0.ɵɵadvance(3);
212
283
  i0.ɵɵtextInterpolate(ctx_r0.ErrorMessage);
213
284
  } }
214
- function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template(rf, ctx) { if (rf & 1) {
215
- const _r10 = i0.ɵɵgetCurrentView();
216
- i0.ɵɵelementStart(0, "mj-query-data-grid", 45);
217
- i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template_mj_query_data_grid_EntityLinkClick_0_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); })("PageChange", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template_mj_query_data_grid_PageChange_0_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnPageChange($event)); });
285
+ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_23_Template(rf, ctx) { if (rf & 1) {
286
+ const _r14 = i0.ɵɵgetCurrentView();
287
+ i0.ɵɵelementStart(0, "mj-query-data-grid", 54);
288
+ i0.ɵɵlistener("EntityLinkClick", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_23_Template_mj_query_data_grid_EntityLinkClick_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnEntityLinkClick($event)); })("PageChange", function DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_23_Template_mj_query_data_grid_PageChange_0_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnPageChange($event)); });
218
289
  i0.ɵɵelementEnd();
219
290
  } if (rf & 2) {
220
291
  const ctx_r0 = i0.ɵɵnextContext(3);
@@ -234,9 +305,11 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Template(rf, ct
234
305
  i0.ɵɵconditionalCreate(9, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_9_Template, 3, 3, "button", 11);
235
306
  i0.ɵɵconditionalCreate(10, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_10_Template, 3, 0, "button", 12)(11, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_11_Template, 6, 1)(12, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_12_Template, 9, 2)(13, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_13_Template, 6, 1)(14, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_14_Template, 6, 1)(15, DataArtifactViewerComponent_Conditional_1_Conditional_0_Case_15_Template, 0, 0);
236
307
  i0.ɵɵelementEnd()();
237
- i0.ɵɵelementStart(16, "div", 13);
238
- i0.ɵɵconditionalCreate(17, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_17_Template, 2, 1, "div", 14);
239
- i0.ɵɵconditionalCreate(18, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_18_Template, 1, 0, "mj-loading", 15)(19, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_Template, 7, 10)(20, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_20_Template, 4, 1, "div", 1)(21, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template, 1, 9, "mj-query-data-grid", 16);
308
+ i0.ɵɵconditionalCreate(16, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_16_Template, 3, 0, "div", 13);
309
+ i0.ɵɵconditionalCreate(17, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_17_Template, 3, 0, "div", 14);
310
+ i0.ɵɵelementStart(18, "div", 15);
311
+ i0.ɵɵconditionalCreate(19, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_19_Template, 2, 1, "div", 16);
312
+ i0.ɵɵconditionalCreate(20, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_20_Template, 1, 0, "mj-loading", 17)(21, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_21_Template, 7, 10)(22, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_22_Template, 4, 1, "div", 1)(23, DataArtifactViewerComponent_Conditional_1_Conditional_0_Conditional_23_Template, 1, 9, "mj-query-data-grid", 18);
240
313
  i0.ɵɵelementEnd();
241
314
  } if (rf & 2) {
242
315
  let tmp_7_0;
@@ -253,19 +326,23 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_0_Template(rf, ct
253
326
  i0.ɵɵconditional(ctx_r0.IsLive ? 9 : -1);
254
327
  i0.ɵɵadvance();
255
328
  i0.ɵɵconditional((tmp_7_0 = ctx_r0.QuerySyncState) === "no-query-latest" ? 10 : tmp_7_0 === "synced" ? 11 : tmp_7_0 === "outdated-latest" ? 12 : tmp_7_0 === "query-ahead" ? 13 : tmp_7_0 === "query-behind" ? 14 : tmp_7_0 === "no-query-older" ? 15 : -1);
256
- i0.ɵɵadvance(7);
257
- i0.ɵɵconditional(ctx_r0.IsSaving ? 17 : -1);
329
+ i0.ɵɵadvance(6);
330
+ i0.ɵɵconditional(ctx_r0.IsMultiTable ? 16 : -1);
331
+ i0.ɵɵadvance();
332
+ i0.ɵɵconditional((ctx_r0.spec.computations == null ? null : ctx_r0.spec.computations.length) ? 17 : -1);
333
+ i0.ɵɵadvance(2);
334
+ i0.ɵɵconditional(ctx_r0.IsSaving ? 19 : -1);
258
335
  i0.ɵɵadvance();
259
- i0.ɵɵconditional(ctx_r0.IsLoading ? 18 : ctx_r0.HasError && ctx_r0.HasData ? 19 : ctx_r0.HasError ? 20 : 21);
336
+ i0.ɵɵconditional(ctx_r0.IsLoading ? 20 : ctx_r0.HasError && ctx_r0.HasData ? 21 : ctx_r0.HasError ? 22 : 23);
260
337
  } }
261
338
  function DataArtifactViewerComponent_Conditional_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
262
339
  i0.ɵɵelementStart(0, "div", 4)(1, "div", 5);
263
- i0.ɵɵelement(2, "i", 46);
340
+ i0.ɵɵelement(2, "i", 55);
264
341
  i0.ɵɵelementStart(3, "span");
265
342
  i0.ɵɵtext(4);
266
343
  i0.ɵɵelementEnd()()();
267
- i0.ɵɵelementStart(5, "div", 47);
268
- i0.ɵɵelement(6, "mj-markdown", 48);
344
+ i0.ɵɵelementStart(5, "div", 56);
345
+ i0.ɵɵelement(6, "mj-markdown", 57);
269
346
  i0.ɵɵelementEnd();
270
347
  } if (rf & 2) {
271
348
  const ctx_r0 = i0.ɵɵnextContext(2);
@@ -276,20 +353,20 @@ function DataArtifactViewerComponent_Conditional_1_Conditional_1_Template(rf, ct
276
353
  } }
277
354
  function DataArtifactViewerComponent_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
278
355
  i0.ɵɵelementStart(0, "div", 2);
279
- i0.ɵɵelement(1, "i", 49);
356
+ i0.ɵɵelement(1, "i", 58);
280
357
  i0.ɵɵelementStart(2, "p");
281
358
  i0.ɵɵtext(3, "No data to display");
282
359
  i0.ɵɵelementEnd()();
283
360
  } }
284
361
  function DataArtifactViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
285
- i0.ɵɵconditionalCreate(0, DataArtifactViewerComponent_Conditional_1_Conditional_0_Template, 22, 8)(1, DataArtifactViewerComponent_Conditional_1_Conditional_1_Template, 7, 6)(2, DataArtifactViewerComponent_Conditional_1_Conditional_2_Template, 4, 0, "div", 2);
362
+ i0.ɵɵconditionalCreate(0, DataArtifactViewerComponent_Conditional_1_Conditional_0_Template, 24, 10)(1, DataArtifactViewerComponent_Conditional_1_Conditional_1_Template, 7, 6)(2, DataArtifactViewerComponent_Conditional_1_Conditional_2_Template, 4, 0, "div", 2);
286
363
  } if (rf & 2) {
287
364
  const ctx_r0 = i0.ɵɵnextContext();
288
- i0.ɵɵconditional(ctx_r0.HasData || ctx_r0.IsLoading ? 0 : ctx_r0.spec.plan ? 1 : 2);
365
+ i0.ɵɵconditional(ctx_r0.HasData || ctx_r0.IsLoading || ctx_r0.ResolvedTables.length > 0 ? 0 : ctx_r0.spec.plan ? 1 : 2);
289
366
  } }
290
367
  function DataArtifactViewerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
291
368
  i0.ɵɵelementStart(0, "div", 1);
292
- i0.ɵɵelement(1, "i", 43);
369
+ i0.ɵɵelement(1, "i", 52);
293
370
  i0.ɵɵelementStart(2, "p");
294
371
  i0.ɵɵtext(3);
295
372
  i0.ɵɵelementEnd()();
@@ -300,15 +377,15 @@ function DataArtifactViewerComponent_Conditional_2_Template(rf, ctx) { if (rf &
300
377
  } }
301
378
  function DataArtifactViewerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
302
379
  i0.ɵɵelementStart(0, "div", 2);
303
- i0.ɵɵelement(1, "i", 49);
380
+ i0.ɵɵelement(1, "i", 58);
304
381
  i0.ɵɵelementStart(2, "p");
305
382
  i0.ɵɵtext(3, "No data to display");
306
383
  i0.ɵɵelementEnd()();
307
384
  } }
308
385
  function DataArtifactViewerComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
309
- const _r11 = i0.ɵɵgetCurrentView();
310
- i0.ɵɵelementStart(0, "mj-save-query-panel", 50);
311
- i0.ɵɵlistener("Saved", function DataArtifactViewerComponent_Conditional_4_Template_mj_save_query_panel_Saved_0_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnQuerySaved($event)); })("Cancelled", function DataArtifactViewerComponent_Conditional_4_Template_mj_save_query_panel_Cancelled_0_listener() { i0.ɵɵrestoreView(_r11); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.ShowSaveDialog = false); });
386
+ const _r15 = i0.ɵɵgetCurrentView();
387
+ i0.ɵɵelementStart(0, "mj-save-query-panel", 59);
388
+ i0.ɵɵlistener("Saved", function DataArtifactViewerComponent_Conditional_4_Template_mj_save_query_panel_Saved_0_listener($event) { i0.ɵɵrestoreView(_r15); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnQuerySaved($event)); })("Cancelled", function DataArtifactViewerComponent_Conditional_4_Template_mj_save_query_panel_Cancelled_0_listener() { i0.ɵɵrestoreView(_r15); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.ShowSaveDialog = false); });
312
389
  i0.ɵɵelementEnd();
313
390
  } if (rf & 2) {
314
391
  const ctx_r0 = i0.ɵɵnextContext();
@@ -344,6 +421,25 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
344
421
  PagerTotalRowCount = 0;
345
422
  PagerPageNumber = 1;
346
423
  PagerPageSize = 100;
424
+ // ─── Multi-Table State ───
425
+ /** All resolved tables (from multi-table or wrapped single-table) */
426
+ ResolvedTables = [];
427
+ /** Index of the currently active table tab */
428
+ ActiveTableIndex = 0;
429
+ /** Per-table paging state */
430
+ tablePageState = new Map();
431
+ /** Per-table live data cache (loaded lazily on tab switch) */
432
+ tableDataCache = new Map();
433
+ /** Per-table column configs cache */
434
+ tableColumnConfigsCache = new Map();
435
+ /** Whether we're in multi-table mode */
436
+ get IsMultiTable() {
437
+ return this.ResolvedTables.length > 1;
438
+ }
439
+ /** The currently active table */
440
+ get ActiveTable() {
441
+ return this.ResolvedTables[this.ActiveTableIndex] ?? null;
442
+ }
347
443
  /** Query sync state — drives the toolbar UI for saved query actions */
348
444
  QuerySyncState = 'no-query-latest';
349
445
  /** Latest version number for this artifact (from cache) */
@@ -358,7 +454,7 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
358
454
  this.cdr = cdr;
359
455
  }
360
456
  get hasDisplayContent() {
361
- return this.spec != null && (this.HasData || this.IsLoading);
457
+ return this.spec != null && (this.HasData || this.IsLoading || this.ResolvedTables.length > 0);
362
458
  }
363
459
  get parentShouldShowRawContent() {
364
460
  return true;
@@ -367,7 +463,9 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
367
463
  return this.GridData.length > 0;
368
464
  }
369
465
  get HasInlineData() {
370
- return !!(this.spec?.rows && this.spec.rows.length > 0);
466
+ // Check root-level rows (legacy single-table) or any resolved table with rows
467
+ return !!(this.spec?.rows && this.spec.rows.length > 0) ||
468
+ this.ResolvedTables.some(t => t.rows && t.rows.length > 0);
371
469
  }
372
470
  get DisplayRowCount() {
373
471
  return this.liveRowCount ?? this.spec?.metadata?.rowCount ?? null;
@@ -407,24 +505,28 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
407
505
  this.ErrorMessage = 'Failed to parse data artifact content';
408
506
  return;
409
507
  }
410
- // Build enriched column configs from agent metadata (if available)
411
- this.GridColumnConfigs = this.BuildColumnConfigs();
412
- // Set loading early (synchronously) so hasDisplayContent is true immediately.
413
- // This ensures the Display tab is available when onPluginLoaded fires,
414
- // showing a loading indicator instead of briefly flashing the Plan tab.
415
- if (this.spec.metadata?.sql || this.HasInlineData) {
508
+ // Resolve to tables array (handles both single and multi-table)
509
+ this.ResolvedTables = NormalizeToTables(this.spec, this.spec.title || 'Results');
510
+ if (this.ResolvedTables.length === 0 && !this.spec.plan) {
511
+ return;
512
+ }
513
+ // Build enriched column configs for the first table
514
+ if (this.ResolvedTables.length > 0) {
515
+ this.GridColumnConfigs = this.BuildColumnConfigsForTable(this.ResolvedTables[0]);
516
+ }
517
+ // Initialize paging state for all tables
518
+ for (let i = 0; i < this.ResolvedTables.length; i++) {
519
+ this.tablePageState.set(i, { pageNumber: 1, pageSize: 100, totalRowCount: 0 });
520
+ }
521
+ // Set loading early (synchronously) so hasDisplayContent is true immediately
522
+ if (this.ResolvedTables.some(t => t.metadata?.sql) || this.HasInlineData) {
416
523
  this.IsLoading = true;
417
524
  }
418
525
  // Load cached metadata and resolve query sync state
419
526
  await this.InitQuerySyncState();
420
- // If SQL is available, execute it live
421
- if (this.spec.metadata?.sql) {
422
- await this.LoadLiveData();
423
- }
424
- else if (this.HasInlineData) {
425
- // Fall back to embedded rows
426
- this.GridData = this.spec.rows;
427
- this.IsLoading = false;
527
+ // Load data for the first (active) table
528
+ if (this.ResolvedTables.length > 0) {
529
+ await this.LoadTableData(0);
428
530
  }
429
531
  // Signal parent that tabs/display content may have changed after async load
430
532
  this.tabsChanged.emit();
@@ -435,12 +537,11 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
435
537
  }
436
538
  }
437
539
  /**
438
- * Re-execute the live SQL query
540
+ * Re-execute the live SQL query for the active table
439
541
  */
440
542
  async OnRefresh() {
441
- if (this.spec?.metadata?.sql) {
442
- await this.LoadLiveData();
443
- }
543
+ this.tableDataCache.delete(this.ActiveTableIndex);
544
+ await this.LoadTableData(this.ActiveTableIndex);
444
545
  }
445
546
  /**
446
547
  * Handle entity link click from the grid and bubble up as openEntityRecord.
@@ -460,53 +561,98 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
460
561
  }
461
562
  /**
462
563
  * Handle page change from the data grid's embedded pager.
463
- * Re-executes the live SQL with the new page parameters.
564
+ * Re-executes the query for the active table with new page parameters.
464
565
  */
465
566
  async OnPageChange(event) {
466
- if (!this.spec?.metadata?.sql)
567
+ const pageState = this.tablePageState.get(this.ActiveTableIndex);
568
+ if (!pageState)
569
+ return;
570
+ pageState.pageNumber = event.PageNumber;
571
+ pageState.pageSize = event.PageSize;
572
+ // Clear cache so it reloads
573
+ this.tableDataCache.delete(this.ActiveTableIndex);
574
+ await this.LoadTableData(this.ActiveTableIndex);
575
+ }
576
+ /**
577
+ * Switch to a different table tab.
578
+ * Loads data lazily if not already cached.
579
+ */
580
+ async OnTableTabClick(index) {
581
+ if (index === this.ActiveTableIndex)
467
582
  return;
468
- this.PagerPageNumber = event.PageNumber;
469
- this.PagerPageSize = event.PageSize;
470
- await this.LoadLiveData();
583
+ this.ActiveTableIndex = index;
584
+ const table = this.ResolvedTables[index];
585
+ if (!table)
586
+ return;
587
+ // Use cached column configs or build new
588
+ if (!this.tableColumnConfigsCache.has(index)) {
589
+ const configs = this.BuildColumnConfigsForTable(table);
590
+ if (configs)
591
+ this.tableColumnConfigsCache.set(index, configs);
592
+ }
593
+ this.GridColumnConfigs = this.tableColumnConfigsCache.get(index) ?? null;
594
+ // Use cached data or load fresh
595
+ if (this.tableDataCache.has(index)) {
596
+ this.GridData = this.tableDataCache.get(index);
597
+ const pageState = this.tablePageState.get(index);
598
+ this.PagerPageNumber = pageState.pageNumber;
599
+ this.PagerPageSize = pageState.pageSize;
600
+ this.PagerTotalRowCount = pageState.totalRowCount;
601
+ }
602
+ else {
603
+ await this.LoadTableData(index);
604
+ }
605
+ this.cdr.detectChanges();
471
606
  }
472
607
  /**
473
- * Execute the SQL query via RunQuery and populate the grid.
474
- * Falls back to inline data on error.
608
+ * Load data for a specific table.
609
+ * Handles live SQL execution, inline rows, and caching.
475
610
  */
476
- async LoadLiveData() {
611
+ async LoadTableData(tableIndex) {
612
+ const table = this.ResolvedTables[tableIndex];
613
+ if (!table)
614
+ return;
477
615
  this.IsLoading = true;
478
616
  this.HasError = false;
479
617
  this.cdr.detectChanges();
480
618
  try {
481
- const rq = new RunQuery();
482
- const startRow = (this.PagerPageNumber - 1) * this.PagerPageSize;
483
- const result = await rq.RunQuery({
484
- SQL: this.spec.metadata.sql,
485
- StartRow: startRow,
486
- MaxRows: this.PagerPageSize
487
- });
488
- if (result.Success) {
489
- this.GridData = result.Results;
490
- this.liveRowCount = result.RowCount;
491
- this.liveExecutionTime = result.ExecutionTime;
492
- this.IsLive = true;
493
- // Update pager state from result
494
- if (result.TotalRowCount != null) {
495
- this.PagerTotalRowCount = result.TotalRowCount;
496
- }
497
- if (result.PageNumber != null) {
498
- this.PagerPageNumber = result.PageNumber;
619
+ if (table.metadata?.sql) {
620
+ // Live SQL execution
621
+ const pageState = this.tablePageState.get(tableIndex);
622
+ const rq = new RunQuery();
623
+ const startRow = (pageState.pageNumber - 1) * pageState.pageSize;
624
+ const result = await rq.RunQuery({
625
+ SQL: table.metadata.sql,
626
+ StartRow: startRow,
627
+ MaxRows: pageState.pageSize
628
+ });
629
+ if (result.Success) {
630
+ this.tableDataCache.set(tableIndex, result.Results);
631
+ this.GridData = result.Results;
632
+ this.IsLive = true;
633
+ pageState.totalRowCount = result.TotalRowCount ?? result.RowCount ?? 0;
634
+ this.PagerTotalRowCount = pageState.totalRowCount;
635
+ this.PagerPageNumber = pageState.pageNumber;
636
+ this.PagerPageSize = pageState.pageSize;
637
+ this.liveRowCount = result.RowCount;
638
+ this.liveExecutionTime = result.ExecutionTime;
499
639
  }
500
- if (result.PageSize != null) {
501
- this.PagerPageSize = result.PageSize;
640
+ else {
641
+ this.HandleTableError(tableIndex, result.ErrorMessage || 'Query failed');
502
642
  }
503
643
  }
644
+ else if (table.rows && table.rows.length > 0) {
645
+ // Inline data
646
+ this.tableDataCache.set(tableIndex, table.rows);
647
+ this.GridData = table.rows;
648
+ this.liveRowCount = table.rows.length;
649
+ }
504
650
  else {
505
- this.HandleQueryError(result.ErrorMessage || 'Query execution failed');
651
+ this.GridData = [];
506
652
  }
507
653
  }
508
654
  catch (error) {
509
- this.HandleQueryError(error instanceof Error ? error.message : 'Query execution failed');
655
+ this.HandleTableError(tableIndex, error instanceof Error ? error.message : 'Load failed');
510
656
  }
511
657
  finally {
512
658
  this.IsLoading = false;
@@ -514,18 +660,19 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
514
660
  }
515
661
  }
516
662
  /**
517
- * Build QueryGridColumnConfig[] from enriched artifact column metadata.
663
+ * Build QueryGridColumnConfig[] from a DataTable's column metadata.
518
664
  * Returns null if columns have no entity metadata (grid falls back to auto-inference).
519
665
  */
520
- BuildColumnConfigs() {
521
- if (!this.spec?.columns?.length)
666
+ BuildColumnConfigsForTable(table) {
667
+ const columns = table.columns;
668
+ if (!columns?.length)
522
669
  return null;
523
670
  // Only build if at least one column has entity metadata or type info
524
- const hasMetadata = this.spec.columns.some(c => c.sourceEntity || c.sqlBaseType);
671
+ const hasMetadata = columns.some(c => c.sourceEntity || c.sqlBaseType);
525
672
  if (!hasMetadata)
526
673
  return null;
527
674
  const md = new Metadata();
528
- return this.spec.columns.map((col, index) => {
675
+ return columns.map((col, index) => {
529
676
  const target = resolveTargetEntity(col.sourceEntity, col.sourceFieldName, md);
530
677
  const isEntityLink = !!(target.targetEntityName && (target.isPrimaryKey || target.isForeignKey));
531
678
  const baseType = (col.sqlBaseType || 'nvarchar').toLowerCase();
@@ -536,15 +683,18 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
536
683
  else if (baseType === 'bit') {
537
684
  align = 'center';
538
685
  }
686
+ // Support both displayName (new) and headerName (legacy) for backward compat
687
+ const colAny = col;
688
+ const title = col.displayName || colAny['headerName'] || col.field;
539
689
  return {
540
690
  field: col.field,
541
- title: col.headerName || col.field,
691
+ title,
542
692
  visible: true,
543
693
  sortable: true,
544
694
  resizable: true,
545
695
  reorderable: true,
546
696
  sqlBaseType: col.sqlBaseType || 'nvarchar',
547
- sqlFullType: col.sqlBaseType || 'nvarchar',
697
+ sqlFullType: col.sqlFullType || col.sqlBaseType || 'nvarchar',
548
698
  align,
549
699
  order: index,
550
700
  sourceEntityName: col.sourceEntity,
@@ -560,13 +710,14 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
560
710
  });
561
711
  }
562
712
  /**
563
- * Handle a query error by setting error state and falling back to inline data
713
+ * Handle a table load error by setting error state and falling back to inline data
564
714
  */
565
- HandleQueryError(message) {
715
+ HandleTableError(tableIndex, message) {
566
716
  this.HasError = true;
567
717
  this.ErrorMessage = message;
568
- if (this.HasInlineData) {
569
- this.GridData = this.spec.rows;
718
+ const table = this.ResolvedTables[tableIndex];
719
+ if (table?.rows && table.rows.length > 0) {
720
+ this.GridData = table.rows;
570
721
  }
571
722
  }
572
723
  // ─── Query Sync State ────────────────────────────────────────────
@@ -807,9 +958,35 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
807
958
  this.artifactVersion.Content = JSON.stringify(this.spec);
808
959
  await this.artifactVersion.Save();
809
960
  }
961
+ /**
962
+ * Returns a DataSnapshot with live grid data, current page state,
963
+ * and query sync state.
964
+ */
965
+ GetCurrentStateSnapshot() {
966
+ if (!this.spec)
967
+ return null;
968
+ const tables = NormalizeToTables(this.spec, this.spec.title || 'Results');
969
+ const activeTable = tables[this.ActiveTableIndex];
970
+ if (activeTable) {
971
+ // Override with live data for the active table
972
+ if (this.IsLive)
973
+ activeTable.rows = this.GridData;
974
+ activeTable.pageNumber = this.PagerPageNumber;
975
+ activeTable.metadata = {
976
+ ...activeTable.metadata,
977
+ ...(this.liveRowCount != null ? { rowCount: this.liveRowCount } : {}),
978
+ ...(this.liveExecutionTime != null
979
+ ? { executionTimeMs: this.liveExecutionTime } : {}),
980
+ ...(this.PagerTotalRowCount > 0
981
+ ? { totalAvailableRows: this.PagerTotalRowCount } : {}),
982
+ pageSize: this.PagerPageSize
983
+ };
984
+ }
985
+ return DataSnapshot.FromTables(tables, this.spec.title);
986
+ }
810
987
  // ─── Tabs ───────────────────────────────────────────────────────
811
988
  /**
812
- * Provide Plan tab (markdown) and SQL tab (code) when available
989
+ * Provide Plan tab (markdown), SQL tab (code), and Interpretation tab when available
813
990
  */
814
991
  GetAdditionalTabs() {
815
992
  const tabs = [];
@@ -821,7 +998,20 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
821
998
  content: this.spec.plan
822
999
  });
823
1000
  }
824
- if (this.spec?.metadata?.sql) {
1001
+ // SQL tab — multi-table shows all queries with headers
1002
+ if (this.IsMultiTable) {
1003
+ const multiSql = buildMultiTableSql(this.ResolvedTables);
1004
+ if (multiSql) {
1005
+ tabs.push({
1006
+ label: 'SQL',
1007
+ icon: 'fa-database',
1008
+ contentType: 'code',
1009
+ language: 'sql',
1010
+ content: multiSql
1011
+ });
1012
+ }
1013
+ }
1014
+ else if (this.spec?.metadata?.sql) {
825
1015
  tabs.push({
826
1016
  label: 'SQL',
827
1017
  icon: 'fa-database',
@@ -830,10 +1020,19 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
830
1020
  content: this.spec.metadata.sql
831
1021
  });
832
1022
  }
1023
+ // Interpretation tab
1024
+ if (this.spec?.interpretation) {
1025
+ tabs.push({
1026
+ label: 'Interpretation',
1027
+ icon: 'fa-lightbulb',
1028
+ contentType: 'markdown',
1029
+ content: this.spec.interpretation
1030
+ });
1031
+ }
833
1032
  return tabs;
834
1033
  }
835
1034
  static ɵfac = function DataArtifactViewerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || DataArtifactViewerComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
836
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DataArtifactViewerComponent, selectors: [["mj-data-artifact-viewer"]], outputs: { openEntityRecord: "openEntityRecord", navigationRequest: "navigationRequest" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 3, consts: [[1, "data-artifact-viewer", 3, "ngClass"], [1, "error-state"], [1, "empty-state"], [3, "QueryName", "QueryDescription", "SQL", "Plan"], [1, "data-toolbar"], [1, "data-title"], [1, "fas", "fa-table"], [1, "live-badge"], [1, "row-count"], [1, "exec-time"], [1, "data-actions"], ["title", "Refresh data", 1, "btn-icon", 3, "disabled"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save"], [1, "grid-container"], [1, "saving-overlay"], ["text", "Loading data..."], ["Height", "100%", 3, "ColumnConfigs", "Data", "TotalRowCount", "PageNumber", "PageSize", "ShowToolbar", "ShowRefresh", "PersistState", "SelectionMode"], ["title", "Refresh data", 1, "btn-icon", 3, "click", "disabled"], [1, "fas", "fa-sync-alt"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save", 3, "click"], [1, "fas", "fa-save"], [1, "query-badge", "query-badge-synced"], [1, "fas", "fa-check"], ["title", "Open saved query record", 1, "btn-icon", "btn-open", 3, "click"], [1, "fas", "fa-external-link-alt"], [1, "query-badge", "query-badge-outdated"], [1, "fas", "fa-circle-exclamation"], [1, "dropdown-wrapper"], ["title", "Update or save query", 1, "btn-icon", "btn-warning", 3, "click"], [1, "fas", "fa-caret-down"], [1, "query-dropdown"], [1, "query-dropdown", 3, "click"], [1, "query-dropdown-item", 3, "click"], [1, "fas", "fa-arrow-up-from-bracket", 2, "color", "#d97706"], [1, "dropdown-label"], [1, "dropdown-desc"], [1, "fas", "fa-plus", 2, "color", "#16a34a"], [1, "fas", "fa-external-link-alt", 2, "color", "var(--mj-text-muted)"], [1, "query-badge", "query-badge-muted"], [1, "fas", "fa-clock-rotate-left"], ["title", "Open saved query record", 1, "btn-icon", "btn-muted", 3, "click"], [3, "text"], [1, "error-banner"], [1, "fas", "fa-exclamation-triangle"], [1, "fallback-note"], ["Height", "100%", 3, "EntityLinkClick", "PageChange", "ColumnConfigs", "Data", "TotalRowCount", "PageNumber", "PageSize", "ShowToolbar", "ShowRefresh", "PersistState", "SelectionMode"], [1, "fas", "fa-diagram-project"], [1, "plan-content"], [3, "data", "enableMermaid", "enableHighlight", "enableCollapsibleHeadings", "enableSmartypants"], [1, "fas", "fa-inbox"], [3, "Saved", "Cancelled", "QueryName", "QueryDescription", "SQL", "Plan"]], template: function DataArtifactViewerComponent_Template(rf, ctx) { if (rf & 1) {
1035
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DataArtifactViewerComponent, selectors: [["mj-data-artifact-viewer"]], outputs: { openEntityRecord: "openEntityRecord", navigationRequest: "navigationRequest" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 3, consts: [[1, "data-artifact-viewer", 3, "ngClass"], [1, "error-state"], [1, "empty-state"], [3, "QueryName", "QueryDescription", "SQL", "Plan"], [1, "data-toolbar"], [1, "data-title"], [1, "fas", "fa-table"], [1, "live-badge"], [1, "row-count"], [1, "exec-time"], [1, "data-actions"], ["title", "Refresh data", 1, "btn-icon", 3, "disabled"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save"], [1, "table-tabs"], [1, "computations-bar"], [1, "grid-container"], [1, "saving-overlay"], ["text", "Loading data..."], ["Height", "100%", 3, "ColumnConfigs", "Data", "TotalRowCount", "PageNumber", "PageSize", "ShowToolbar", "ShowRefresh", "PersistState", "SelectionMode"], ["title", "Refresh data", 1, "btn-icon", 3, "click", "disabled"], [1, "fas", "fa-sync-alt"], ["title", "Save as reusable query", 1, "btn-icon", "btn-save", 3, "click"], [1, "fas", "fa-save"], [1, "query-badge", "query-badge-synced"], [1, "fas", "fa-check"], ["title", "Open saved query record", 1, "btn-icon", "btn-open", 3, "click"], [1, "fas", "fa-external-link-alt"], [1, "query-badge", "query-badge-outdated"], [1, "fas", "fa-circle-exclamation"], [1, "dropdown-wrapper"], ["title", "Update or save query", 1, "btn-icon", "btn-warning", 3, "click"], [1, "fas", "fa-caret-down"], [1, "query-dropdown"], [1, "query-dropdown", 3, "click"], [1, "query-dropdown-item", 3, "click"], [1, "fas", "fa-arrow-up-from-bracket", 2, "color", "#d97706"], [1, "dropdown-label"], [1, "dropdown-desc"], [1, "fas", "fa-plus", 2, "color", "#16a34a"], [1, "fas", "fa-external-link-alt", 2, "color", "var(--mj-text-muted)"], [1, "query-badge", "query-badge-muted"], [1, "fas", "fa-clock-rotate-left"], ["title", "Open saved query record", 1, "btn-icon", "btn-muted", 3, "click"], [1, "table-tab", 3, "active", "title"], [1, "table-tab", 3, "click", "title"], [1, "table-tab-name"], [1, "table-tab-count"], [1, "computation-chip", 3, "title"], [1, "computation-label"], [1, "computation-value"], [3, "text"], [1, "error-banner"], [1, "fas", "fa-exclamation-triangle"], [1, "fallback-note"], ["Height", "100%", 3, "EntityLinkClick", "PageChange", "ColumnConfigs", "Data", "TotalRowCount", "PageNumber", "PageSize", "ShowToolbar", "ShowRefresh", "PersistState", "SelectionMode"], [1, "fas", "fa-diagram-project"], [1, "plan-content"], [3, "data", "enableMermaid", "enableHighlight", "enableCollapsibleHeadings", "enableSmartypants"], [1, "fas", "fa-inbox"], [3, "Saved", "Cancelled", "QueryName", "QueryDescription", "SQL", "Plan"]], template: function DataArtifactViewerComponent_Template(rf, ctx) { if (rf & 1) {
837
1036
  i0.ɵɵelementStart(0, "div", 0);
838
1037
  i0.ɵɵconditionalCreate(1, DataArtifactViewerComponent_Conditional_1_Template, 3, 1)(2, DataArtifactViewerComponent_Conditional_2_Template, 4, 1, "div", 1)(3, DataArtifactViewerComponent_Conditional_3_Template, 4, 0, "div", 2);
839
1038
  i0.ɵɵconditionalCreate(4, DataArtifactViewerComponent_Conditional_4_Template, 1, 4, "mj-save-query-panel", 3);
@@ -844,7 +1043,7 @@ let DataArtifactViewerComponent = class DataArtifactViewerComponent extends Base
844
1043
  i0.ɵɵconditional(ctx.spec ? 1 : ctx.HasError ? 2 : 3);
845
1044
  i0.ɵɵadvance(3);
846
1045
  i0.ɵɵconditional(ctx.ShowSaveDialog ? 4 : -1);
847
- } }, dependencies: [i1.NgClass, i2.MarkdownComponent, i3.QueryDataGridComponent, i4.LoadingComponent, i5.SaveQueryPanelComponent], styles: [".data-artifact-viewer[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.data-toolbar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.data-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.row-count[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.exec-time[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-status-success);\n}\n\n.live-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.data-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n}\n\n.btn-icon[_ngcontent-%COMP%] {\n padding: 4px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-secondary);\n}\n\n.btn-icon[_ngcontent-%COMP%]:hover {\n background: var(--mj-border-default);\n border-color: var(--mj-text-disabled);\n}\n\n.btn-icon[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-icon.btn-save[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-save[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.btn-icon.btn-open[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-open[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.grid-container[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n position: relative;\n}\n\n\n\n.saving-overlay[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n}\n\n.error-banner[_ngcontent-%COMP%] {\n padding: 8px 12px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border-bottom: 1px solid var(--mj-status-warning);\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--mj-status-warning);\n}\n\n.fallback-note[_ngcontent-%COMP%] {\n font-style: italic;\n color: var(--mj-text-muted);\n}\n\n.empty-state[_ngcontent-%COMP%], .error-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: var(--mj-text-muted);\n text-align: center;\n gap: 12px;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%], .error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n}\n\n.error-state[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.plan-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n padding: 16px;\n}\n\n\n\n.query-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.query-badge-synced[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.query-badge-outdated[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.query-badge-muted[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n}\n\n\n\n.dropdown-wrapper[_ngcontent-%COMP%] {\n position: relative;\n}\n\n.btn-icon.btn-warning[_ngcontent-%COMP%] {\n color: var(--mj-status-warning);\n border-color: color-mix(in srgb, var(--mj-status-warning) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-warning[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.btn-icon.btn-muted[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n border-color: var(--mj-border-default);\n}\n\n\n\n.query-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n min-width: 280px;\n z-index: 100;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%] {\n padding: 10px 14px;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background 0.1s;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.query-dropdown-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.dropdown-label[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.dropdown-desc[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}"] });
1046
+ } }, dependencies: [i1.NgClass, i2.MarkdownComponent, i3.QueryDataGridComponent, i4.LoadingComponent, i5.SaveQueryPanelComponent], styles: [".data-artifact-viewer[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.data-toolbar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.data-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.row-count[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.exec-time[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-status-success);\n}\n\n.live-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.data-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n}\n\n.btn-icon[_ngcontent-%COMP%] {\n padding: 4px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-secondary);\n}\n\n.btn-icon[_ngcontent-%COMP%]:hover {\n background: var(--mj-border-default);\n border-color: var(--mj-text-disabled);\n}\n\n.btn-icon[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-icon.btn-save[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-save[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.btn-icon.btn-open[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-open[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.grid-container[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n position: relative;\n}\n\n\n\n.saving-overlay[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n}\n\n.error-banner[_ngcontent-%COMP%] {\n padding: 8px 12px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border-bottom: 1px solid var(--mj-status-warning);\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--mj-status-warning);\n}\n\n.fallback-note[_ngcontent-%COMP%] {\n font-style: italic;\n color: var(--mj-text-muted);\n}\n\n.empty-state[_ngcontent-%COMP%], .error-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: var(--mj-text-muted);\n text-align: center;\n gap: 12px;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%], .error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n}\n\n.error-state[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.plan-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n padding: 16px;\n}\n\n\n\n.query-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.query-badge-synced[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.query-badge-outdated[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.query-badge-muted[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n}\n\n\n\n.dropdown-wrapper[_ngcontent-%COMP%] {\n position: relative;\n}\n\n.btn-icon.btn-warning[_ngcontent-%COMP%] {\n color: var(--mj-status-warning);\n border-color: color-mix(in srgb, var(--mj-status-warning) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-warning[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.btn-icon.btn-muted[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n border-color: var(--mj-border-default);\n}\n\n\n\n.query-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n min-width: 280px;\n z-index: 100;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%] {\n padding: 10px 14px;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background 0.1s;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.query-dropdown-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.query-dropdown-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.dropdown-label[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.dropdown-desc[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n\n\n\n.table-tabs[_ngcontent-%COMP%] {\n display: flex;\n gap: 0;\n border-bottom: 2px solid var(--mj-border-default);\n padding: 0 var(--mj-spacing-md, 16px);\n background: var(--mj-bg-surface);\n overflow-x: auto;\n}\n\n.table-tab[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -2px;\n transition: color 150ms ease, border-color 150ms ease;\n white-space: nowrap;\n}\n\n.table-tab[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n.table-tab.active[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.table-tab-name[_ngcontent-%COMP%] {\n text-transform: capitalize;\n}\n\n.table-tab-count[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-weight: 400;\n font-size: 12px;\n}\n\n.table-tab.active[_ngcontent-%COMP%] .table-tab-count[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n opacity: 0.7;\n}\n\n\n\n\n.computations-bar[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n padding: 8px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-subtle);\n overflow-x: auto;\n flex-wrap: wrap;\n}\n\n.computation-chip[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.computation-label[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-weight: 400;\n}\n\n.computation-value[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 600;\n}"] });
848
1047
  };
849
1048
  DataArtifactViewerComponent = __decorate([
850
1049
  RegisterClass(BaseArtifactViewerPluginComponent, 'DataArtifactViewerPlugin')
@@ -852,11 +1051,11 @@ DataArtifactViewerComponent = __decorate([
852
1051
  export { DataArtifactViewerComponent };
853
1052
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DataArtifactViewerComponent, [{
854
1053
  type: Component,
855
- args: [{ standalone: false, selector: 'mj-data-artifact-viewer', template: "<div class=\"data-artifact-viewer\" [ngClass]=\"cssClass\">\n @if (spec) {\n @if (HasData || IsLoading) {\n <!-- Toolbar -->\n <div class=\"data-toolbar\">\n <div class=\"data-title\">\n <i class=\"fas fa-table\"></i>\n <span>{{ spec.title || 'Data Results' }}</span>\n @if (IsLive) {\n <span class=\"live-badge\">Live</span>\n }\n @if (DisplayRowCount != null) {\n <span class=\"row-count\">{{ DisplayRowCount }} rows</span>\n }\n @if (DisplayExecutionTime != null) {\n <span class=\"exec-time\">{{ DisplayExecutionTime }}ms</span>\n }\n </div>\n <div class=\"data-actions\">\n @if (IsLive) {\n <button class=\"btn-icon\" title=\"Refresh data\" (click)=\"OnRefresh()\" [disabled]=\"IsLoading\">\n <i class=\"fas fa-sync-alt\" [class.fa-spin]=\"IsLoading\"></i> Refresh\n </button>\n }\n\n @switch (QuerySyncState) {\n <!-- S1: No saved query, latest version \u2192 Save Query -->\n @case ('no-query-latest') {\n <button class=\"btn-icon btn-save\" title=\"Save as reusable query\"\n (click)=\"OnShowSaveDialog()\">\n <i class=\"fas fa-save\"></i> Save Query\n </button>\n }\n\n <!-- S2: SQL matches \u2192 green badge + Open -->\n @case ('synced') {\n <span class=\"query-badge query-badge-synced\">\n <i class=\"fas fa-check\"></i> Saved at v{{ SavedAtVersion }}\n </span>\n <button class=\"btn-icon btn-open\" title=\"Open saved query record\"\n (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S3: SQL differs, latest version \u2192 amber badge + dropdown -->\n @case ('outdated-latest') {\n <span class=\"query-badge query-badge-outdated\">\n <i class=\"fas fa-circle-exclamation\"></i> Saved at v{{ SavedAtVersion }}\n </span>\n <div class=\"dropdown-wrapper\">\n <button class=\"btn-icon btn-warning\" title=\"Update or save query\"\n (click)=\"OnToggleUpdateDropdown()\">\n <i class=\"fas fa-save\"></i> Update Query <i class=\"fas fa-caret-down\"></i>\n </button>\n @if (ShowUpdateDropdown) {\n <div class=\"query-dropdown\" (click)=\"$event.stopPropagation()\">\n <div class=\"query-dropdown-item\" (click)=\"OnUpdateExistingQuery()\">\n <i class=\"fas fa-arrow-up-from-bracket\" style=\"color:#d97706\"></i>\n <div>\n <div class=\"dropdown-label\">Update \"{{ SavedQueryDisplayName }}\"</div>\n <div class=\"dropdown-desc\">Replace saved query SQL with this version's SQL</div>\n </div>\n </div>\n <div class=\"query-dropdown-item\" (click)=\"OnSaveAsNewQuery()\">\n <i class=\"fas fa-plus\" style=\"color:#16a34a\"></i>\n <div>\n <div class=\"dropdown-label\">Save as New Query</div>\n <div class=\"dropdown-desc\">Create a new query, keep the original unchanged</div>\n </div>\n </div>\n <div class=\"query-dropdown-item\" (click)=\"OnOpenSavedQuery(); OnCloseDropdown()\">\n <i class=\"fas fa-external-link-alt\" style=\"color:var(--mj-text-muted)\"></i>\n <div>\n <div class=\"dropdown-label\">Open Saved Query</div>\n <div class=\"dropdown-desc\">View \"{{ SavedQueryDisplayName }}\" (saved at v{{ SavedAtVersion }})</div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- S4: Older version, query was updated ahead -->\n @case ('query-ahead') {\n <span class=\"query-badge query-badge-muted\">\n <i class=\"fas fa-clock-rotate-left\"></i> Query updated at v{{ SavedAtVersion }}\n </span>\n <button class=\"btn-icon btn-muted\" title=\"Open saved query record\"\n (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S5: Middle version, saved at older, not latest -->\n @case ('query-behind') {\n <span class=\"query-badge query-badge-muted\">\n <i class=\"fas fa-clock-rotate-left\"></i> Saved at v{{ SavedAtVersion }}\n </span>\n <button class=\"btn-icon btn-muted\" title=\"Open saved query record\"\n (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S6: No saved query, older version \u2192 no actions -->\n @case ('no-query-older') {\n <!-- No query actions available on older versions -->\n }\n }\n </div>\n </div>\n\n <!-- Grid -->\n <div class=\"grid-container\">\n <!-- Saving overlay -->\n @if (IsSaving) {\n <div class=\"saving-overlay\">\n <mj-loading [text]=\"SavingMessage\"></mj-loading>\n </div>\n }\n @if (IsLoading) {\n <mj-loading text=\"Loading data...\"></mj-loading>\n } @else if (HasError && HasData) {\n <div class=\"error-banner\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <span>{{ ErrorMessage }}</span>\n <span class=\"fallback-note\">(Showing cached data)</span>\n </div>\n <mj-query-data-grid\n [ColumnConfigs]=\"GridColumnConfigs\"\n [Data]=\"GridData\"\n [TotalRowCount]=\"PagerTotalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PagerPageSize\"\n [ShowToolbar]=\"false\"\n [ShowRefresh]=\"false\"\n [PersistState]=\"false\"\n [SelectionMode]=\"'none'\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n (PageChange)=\"OnPageChange($event)\"\n Height=\"100%\">\n </mj-query-data-grid>\n } @else if (HasError) {\n <div class=\"error-state\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <p>{{ ErrorMessage }}</p>\n </div>\n } @else {\n <mj-query-data-grid\n [ColumnConfigs]=\"GridColumnConfigs\"\n [Data]=\"GridData\"\n [TotalRowCount]=\"PagerTotalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PagerPageSize\"\n [ShowToolbar]=\"false\"\n [ShowRefresh]=\"false\"\n [PersistState]=\"false\"\n [SelectionMode]=\"'none'\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n (PageChange)=\"OnPageChange($event)\"\n Height=\"100%\">\n </mj-query-data-grid>\n }\n </div>\n } @else if (spec.plan) {\n <!-- Plan-only view (no results yet) -->\n <div class=\"data-toolbar\">\n <div class=\"data-title\">\n <i class=\"fas fa-diagram-project\"></i>\n <span>{{ spec.title || 'Query Plan' }}</span>\n </div>\n </div>\n <div class=\"plan-content\">\n <mj-markdown\n [data]=\"spec.plan\"\n [enableMermaid]=\"true\"\n [enableHighlight]=\"true\"\n [enableCollapsibleHeadings]=\"false\"\n [enableSmartypants]=\"true\">\n </mj-markdown>\n </div>\n } @else {\n <!-- No data and no plan -->\n <div class=\"empty-state\">\n <i class=\"fas fa-inbox\"></i>\n <p>No data to display</p>\n </div>\n }\n } @else if (HasError) {\n <div class=\"error-state\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <p>{{ ErrorMessage }}</p>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fas fa-inbox\"></i>\n <p>No data to display</p>\n </div>\n }\n\n <!-- Save Query Panel (slide-in) -->\n @if (ShowSaveDialog) {\n <mj-save-query-panel\n [QueryName]=\"spec?.title || 'Untitled Query'\"\n [QueryDescription]=\"''\"\n [SQL]=\"spec?.metadata?.sql || ''\"\n [Plan]=\"spec?.plan || ''\"\n (Saved)=\"OnQuerySaved($event)\"\n (Cancelled)=\"ShowSaveDialog = false\">\n </mj-save-query-panel>\n }\n</div>\n", styles: [".data-artifact-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.data-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.data-title i {\n color: var(--mj-text-muted);\n}\n\n.row-count {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.exec-time {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-status-success);\n}\n\n.live-badge {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.data-actions {\n display: flex;\n gap: 6px;\n}\n\n.btn-icon {\n padding: 4px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-secondary);\n}\n\n.btn-icon:hover {\n background: var(--mj-border-default);\n border-color: var(--mj-text-disabled);\n}\n\n.btn-icon:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-icon.btn-save {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-save:hover {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.btn-icon.btn-open {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-open:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.grid-container {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n position: relative;\n}\n\n/* Saving overlay \u2014 semi-transparent panel over the grid */\n.saving-overlay {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n}\n\n.error-banner {\n padding: 8px 12px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border-bottom: 1px solid var(--mj-status-warning);\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--mj-status-warning);\n}\n\n.fallback-note {\n font-style: italic;\n color: var(--mj-text-muted);\n}\n\n.empty-state, .error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: var(--mj-text-muted);\n text-align: center;\n gap: 12px;\n}\n\n.empty-state i, .error-state i {\n font-size: 32px;\n}\n\n.error-state {\n color: var(--mj-status-error);\n}\n\n.plan-content {\n flex: 1;\n overflow: auto;\n padding: 16px;\n}\n\n/* Query sync badges */\n.query-badge {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.query-badge-synced {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.query-badge-outdated {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.query-badge-muted {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n}\n\n/* Dropdown wrapper */\n.dropdown-wrapper {\n position: relative;\n}\n\n.btn-icon.btn-warning {\n color: var(--mj-status-warning);\n border-color: color-mix(in srgb, var(--mj-status-warning) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-warning:hover {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.btn-icon.btn-muted {\n color: var(--mj-text-disabled);\n border-color: var(--mj-border-default);\n}\n\n/* Query actions dropdown */\n.query-dropdown {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n min-width: 280px;\n z-index: 100;\n}\n\n.query-dropdown-item {\n padding: 10px 14px;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background 0.1s;\n}\n\n.query-dropdown-item:last-child {\n border-bottom: none;\n}\n\n.query-dropdown-item:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.query-dropdown-item i {\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.dropdown-label {\n font-weight: 500;\n}\n\n.dropdown-desc {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n"] }]
1054
+ args: [{ standalone: false, selector: 'mj-data-artifact-viewer', template: "<div class=\"data-artifact-viewer\" [ngClass]=\"cssClass\">\n @if (spec) {\n @if (HasData || IsLoading || ResolvedTables.length > 0) {\n <!-- Toolbar -->\n <div class=\"data-toolbar\">\n <div class=\"data-title\">\n <i class=\"fas fa-table\"></i>\n <span>{{ spec.title || \"Data Results\" }}</span>\n @if (IsLive) {\n <span class=\"live-badge\">Live</span>\n }\n @if (DisplayRowCount != null) {\n <span class=\"row-count\">{{ DisplayRowCount }} rows</span>\n }\n @if (DisplayExecutionTime != null) {\n <span class=\"exec-time\">{{ DisplayExecutionTime }}ms</span>\n }\n </div>\n <div class=\"data-actions\">\n @if (IsLive) {\n <button class=\"btn-icon\" title=\"Refresh data\" (click)=\"OnRefresh()\" [disabled]=\"IsLoading\">\n <i class=\"fas fa-sync-alt\" [class.fa-spin]=\"IsLoading\"></i> Refresh\n </button>\n }\n\n @switch (QuerySyncState) {\n <!-- S1: No saved query, latest version \u2192 Save Query -->\n @case (\"no-query-latest\") {\n <button class=\"btn-icon btn-save\" title=\"Save as reusable query\" (click)=\"OnShowSaveDialog()\"><i class=\"fas fa-save\"></i> Save Query</button>\n }\n\n <!-- S2: SQL matches \u2192 green badge + Open -->\n @case (\"synced\") {\n <span class=\"query-badge query-badge-synced\"> <i class=\"fas fa-check\"></i> Saved at v{{ SavedAtVersion }} </span>\n <button class=\"btn-icon btn-open\" title=\"Open saved query record\" (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S3: SQL differs, latest version \u2192 amber badge + dropdown -->\n @case (\"outdated-latest\") {\n <span class=\"query-badge query-badge-outdated\"> <i class=\"fas fa-circle-exclamation\"></i> Saved at v{{ SavedAtVersion }} </span>\n <div class=\"dropdown-wrapper\">\n <button class=\"btn-icon btn-warning\" title=\"Update or save query\" (click)=\"OnToggleUpdateDropdown()\">\n <i class=\"fas fa-save\"></i> Update Query <i class=\"fas fa-caret-down\"></i>\n </button>\n @if (ShowUpdateDropdown) {\n <div class=\"query-dropdown\" (click)=\"$event.stopPropagation()\">\n <div class=\"query-dropdown-item\" (click)=\"OnUpdateExistingQuery()\">\n <i class=\"fas fa-arrow-up-from-bracket\" style=\"color: #d97706\"></i>\n <div>\n <div class=\"dropdown-label\">Update \"{{ SavedQueryDisplayName }}\"</div>\n <div class=\"dropdown-desc\">Replace saved query SQL with this version's SQL</div>\n </div>\n </div>\n <div class=\"query-dropdown-item\" (click)=\"OnSaveAsNewQuery()\">\n <i class=\"fas fa-plus\" style=\"color: #16a34a\"></i>\n <div>\n <div class=\"dropdown-label\">Save as New Query</div>\n <div class=\"dropdown-desc\">Create a new query, keep the original unchanged</div>\n </div>\n </div>\n <div class=\"query-dropdown-item\" (click)=\"OnOpenSavedQuery(); OnCloseDropdown()\">\n <i class=\"fas fa-external-link-alt\" style=\"color: var(--mj-text-muted)\"></i>\n <div>\n <div class=\"dropdown-label\">Open Saved Query</div>\n <div class=\"dropdown-desc\">View \"{{ SavedQueryDisplayName }}\" (saved at v{{ SavedAtVersion }})</div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- S4: Older version, query was updated ahead -->\n @case (\"query-ahead\") {\n <span class=\"query-badge query-badge-muted\"> <i class=\"fas fa-clock-rotate-left\"></i> Query updated at v{{ SavedAtVersion }} </span>\n <button class=\"btn-icon btn-muted\" title=\"Open saved query record\" (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S5: Middle version, saved at older, not latest -->\n @case (\"query-behind\") {\n <span class=\"query-badge query-badge-muted\"> <i class=\"fas fa-clock-rotate-left\"></i> Saved at v{{ SavedAtVersion }} </span>\n <button class=\"btn-icon btn-muted\" title=\"Open saved query record\" (click)=\"OnOpenSavedQuery()\">\n <i class=\"fas fa-external-link-alt\"></i> Open Query\n </button>\n }\n\n <!-- S6: No saved query, older version \u2192 no actions -->\n @case (\"no-query-older\") {\n <!-- No query actions available on older versions -->\n }\n }\n </div>\n </div>\n\n <!-- Multi-table tab strip -->\n @if (IsMultiTable) {\n <div class=\"table-tabs\">\n @for (table of ResolvedTables; track table.name; let i = $index) {\n <button class=\"table-tab\" [class.active]=\"i === ActiveTableIndex\" [title]=\"table.description || table.name\" (click)=\"OnTableTabClick(i)\">\n <span class=\"table-tab-name\">{{ table.name }}</span>\n @if (table.metadata?.rowCount != null || table.rows.length) {\n <span class=\"table-tab-count\"> ({{ table.metadata?.rowCount ?? table.rows.length }} rows) </span>\n }\n </button>\n }\n </div>\n }\n\n <!-- Computations summary bar -->\n @if (spec.computations?.length) {\n <div class=\"computations-bar\">\n @for (comp of spec.computations; track comp.name) {\n @if (!IsMultiTable || !comp.table || comp.table === ActiveTable?.name) {\n <div class=\"computation-chip\" [title]=\"comp.description || comp.name\">\n <span class=\"computation-label\">{{ comp.name }}</span>\n <span class=\"computation-value\">\n {{ comp.formattedValue || comp.value }}\n </span>\n </div>\n }\n }\n </div>\n }\n\n <!-- Grid -->\n <div class=\"grid-container\">\n <!-- Saving overlay -->\n @if (IsSaving) {\n <div class=\"saving-overlay\">\n <mj-loading [text]=\"SavingMessage\"></mj-loading>\n </div>\n }\n @if (IsLoading) {\n <mj-loading text=\"Loading data...\"></mj-loading>\n } @else if (HasError && HasData) {\n <div class=\"error-banner\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <span>{{ ErrorMessage }}</span>\n <span class=\"fallback-note\">(Showing cached data)</span>\n </div>\n <mj-query-data-grid\n [ColumnConfigs]=\"GridColumnConfigs\"\n [Data]=\"GridData\"\n [TotalRowCount]=\"PagerTotalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PagerPageSize\"\n [ShowToolbar]=\"false\"\n [ShowRefresh]=\"false\"\n [PersistState]=\"false\"\n [SelectionMode]=\"'none'\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n (PageChange)=\"OnPageChange($event)\"\n Height=\"100%\"\n >\n </mj-query-data-grid>\n } @else if (HasError) {\n <div class=\"error-state\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <p>{{ ErrorMessage }}</p>\n </div>\n } @else {\n <mj-query-data-grid\n [ColumnConfigs]=\"GridColumnConfigs\"\n [Data]=\"GridData\"\n [TotalRowCount]=\"PagerTotalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PagerPageSize\"\n [ShowToolbar]=\"false\"\n [ShowRefresh]=\"false\"\n [PersistState]=\"false\"\n [SelectionMode]=\"'none'\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n (PageChange)=\"OnPageChange($event)\"\n Height=\"100%\"\n >\n </mj-query-data-grid>\n }\n </div>\n } @else if (spec.plan) {\n <!-- Plan-only view (no results yet) -->\n <div class=\"data-toolbar\">\n <div class=\"data-title\">\n <i class=\"fas fa-diagram-project\"></i>\n <span>{{ spec.title || \"Query Plan\" }}</span>\n </div>\n </div>\n <div class=\"plan-content\">\n <mj-markdown [data]=\"spec.plan\" [enableMermaid]=\"true\" [enableHighlight]=\"true\" [enableCollapsibleHeadings]=\"false\" [enableSmartypants]=\"true\">\n </mj-markdown>\n </div>\n } @else {\n <!-- No data and no plan -->\n <div class=\"empty-state\">\n <i class=\"fas fa-inbox\"></i>\n <p>No data to display</p>\n </div>\n }\n } @else if (HasError) {\n <div class=\"error-state\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <p>{{ ErrorMessage }}</p>\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fas fa-inbox\"></i>\n <p>No data to display</p>\n </div>\n }\n\n <!-- Save Query Panel (slide-in) -->\n @if (ShowSaveDialog) {\n <mj-save-query-panel\n [QueryName]=\"spec?.title || 'Untitled Query'\"\n [QueryDescription]=\"''\"\n [SQL]=\"spec?.metadata?.sql || ''\"\n [Plan]=\"spec?.plan || ''\"\n (Saved)=\"OnQuerySaved($event)\"\n (Cancelled)=\"ShowSaveDialog = false\"\n >\n </mj-save-query-panel>\n }\n</div>\n", styles: [".data-artifact-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.data-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.data-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.data-title i {\n color: var(--mj-text-muted);\n}\n\n.row-count {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.exec-time {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-status-success);\n}\n\n.live-badge {\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.data-actions {\n display: flex;\n gap: 6px;\n}\n\n.btn-icon {\n padding: 4px 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-secondary);\n}\n\n.btn-icon:hover {\n background: var(--mj-border-default);\n border-color: var(--mj-text-disabled);\n}\n\n.btn-icon:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-icon.btn-save {\n color: var(--mj-status-success);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-save:hover {\n background: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.btn-icon.btn-open {\n color: var(--mj-brand-primary);\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-open:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.grid-container {\n flex: 1;\n overflow: hidden;\n min-height: 200px;\n height: 500px;\n position: relative;\n}\n\n/* Saving overlay \u2014 semi-transparent panel over the grid */\n.saving-overlay {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n}\n\n.error-banner {\n padding: 8px 12px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border-bottom: 1px solid var(--mj-status-warning);\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--mj-status-warning);\n}\n\n.fallback-note {\n font-style: italic;\n color: var(--mj-text-muted);\n}\n\n.empty-state, .error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: var(--mj-text-muted);\n text-align: center;\n gap: 12px;\n}\n\n.empty-state i, .error-state i {\n font-size: 32px;\n}\n\n.error-state {\n color: var(--mj-status-error);\n}\n\n.plan-content {\n flex: 1;\n overflow: auto;\n padding: 16px;\n}\n\n/* Query sync badges */\n.query-badge {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n\n.query-badge-synced {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.query-badge-outdated {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.query-badge-muted {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n}\n\n/* Dropdown wrapper */\n.dropdown-wrapper {\n position: relative;\n}\n\n.btn-icon.btn-warning {\n color: var(--mj-status-warning);\n border-color: color-mix(in srgb, var(--mj-status-warning) 30%, var(--mj-bg-surface));\n}\n\n.btn-icon.btn-warning:hover {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.btn-icon.btn-muted {\n color: var(--mj-text-disabled);\n border-color: var(--mj-border-default);\n}\n\n/* Query actions dropdown */\n.query-dropdown {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n min-width: 280px;\n z-index: 100;\n}\n\n.query-dropdown-item {\n padding: 10px 14px;\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--mj-border-default);\n transition: background 0.1s;\n}\n\n.query-dropdown-item:last-child {\n border-bottom: none;\n}\n\n.query-dropdown-item:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.query-dropdown-item i {\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n}\n\n.dropdown-label {\n font-weight: 500;\n}\n\n.dropdown-desc {\n font-size: 11px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* \u2500\u2500\u2500 Table Tab Strip \u2500\u2500\u2500 */\n\n.table-tabs {\n display: flex;\n gap: 0;\n border-bottom: 2px solid var(--mj-border-default);\n padding: 0 var(--mj-spacing-md, 16px);\n background: var(--mj-bg-surface);\n overflow-x: auto;\n}\n\n.table-tab {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -2px;\n transition: color 150ms ease, border-color 150ms ease;\n white-space: nowrap;\n}\n\n.table-tab:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n\n.table-tab.active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.table-tab-name {\n text-transform: capitalize;\n}\n\n.table-tab-count {\n color: var(--mj-text-muted);\n font-weight: 400;\n font-size: 12px;\n}\n\n.table-tab.active .table-tab-count {\n color: var(--mj-brand-primary);\n opacity: 0.7;\n}\n\n/* \u2500\u2500\u2500 Computations Summary Bar \u2500\u2500\u2500 */\n\n.computations-bar {\n display: flex;\n gap: 12px;\n padding: 8px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-subtle);\n overflow-x: auto;\n flex-wrap: wrap;\n}\n\n.computation-chip {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.computation-label {\n color: var(--mj-text-secondary);\n font-weight: 400;\n}\n\n.computation-value {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n"] }]
856
1055
  }], () => [{ type: i0.ChangeDetectorRef }], { openEntityRecord: [{
857
1056
  type: Output
858
1057
  }], navigationRequest: [{
859
1058
  type: Output
860
1059
  }] }); })();
861
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DataArtifactViewerComponent, { className: "DataArtifactViewerComponent", filePath: "src/lib/components/plugins/data-artifact-viewer.component.ts", lineNumber: 109 }); })();
1060
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DataArtifactViewerComponent, { className: "DataArtifactViewerComponent", filePath: "src/lib/components/plugins/data-artifact-viewer.component.ts", lineNumber: 69 }); })();
862
1061
  //# sourceMappingURL=data-artifact-viewer.component.js.map