@jotx-labs/editor 2.4.193 → 2.4.204

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 (94) hide show
  1. package/dist/components/BlockMenu.d.ts.map +1 -1
  2. package/dist/components/BlockMenu.js +61 -7
  3. package/dist/components/BlockMenu.js.map +1 -1
  4. package/dist/components/ButtonNodeView.d.ts +1 -0
  5. package/dist/components/ButtonNodeView.d.ts.map +1 -1
  6. package/dist/components/ButtonNodeView.js +34 -13
  7. package/dist/components/ButtonNodeView.js.map +1 -1
  8. package/dist/components/ButtonStripNodeView.d.ts +13 -0
  9. package/dist/components/ButtonStripNodeView.d.ts.map +1 -0
  10. package/dist/components/ButtonStripNodeView.js +96 -0
  11. package/dist/components/ButtonStripNodeView.js.map +1 -0
  12. package/dist/components/ChartNodeView.d.ts.map +1 -1
  13. package/dist/components/ChartNodeView.js +504 -203
  14. package/dist/components/ChartNodeView.js.map +1 -1
  15. package/dist/components/ColumnNodeView.d.ts +6 -0
  16. package/dist/components/ColumnNodeView.d.ts.map +1 -0
  17. package/dist/components/ColumnNodeView.js +13 -0
  18. package/dist/components/ColumnNodeView.js.map +1 -0
  19. package/dist/components/ColumnsNodeView.d.ts +14 -0
  20. package/dist/components/ColumnsNodeView.d.ts.map +1 -0
  21. package/dist/components/ColumnsNodeView.js +122 -0
  22. package/dist/components/ColumnsNodeView.js.map +1 -0
  23. package/dist/components/DocumentHeader/DocumentHeader.d.ts.map +1 -1
  24. package/dist/components/DocumentHeader/DocumentHeader.js +1 -0
  25. package/dist/components/DocumentHeader/DocumentHeader.js.map +1 -1
  26. package/dist/components/DocumentHeader/ExportMenu.d.ts +2 -2
  27. package/dist/components/DocumentHeader/ExportMenu.d.ts.map +1 -1
  28. package/dist/components/DocumentHeader/ExportMenu.js +129 -9
  29. package/dist/components/DocumentHeader/ExportMenu.js.map +1 -1
  30. package/dist/components/JotxEditor.d.ts.map +1 -1
  31. package/dist/components/JotxEditor.js +52 -0
  32. package/dist/components/JotxEditor.js.map +1 -1
  33. package/dist/components/MathNodeView.d.ts.map +1 -1
  34. package/dist/components/MathNodeView.js +10 -1
  35. package/dist/components/MathNodeView.js.map +1 -1
  36. package/dist/components/PresentationToolbar.d.ts +13 -0
  37. package/dist/components/PresentationToolbar.d.ts.map +1 -0
  38. package/dist/components/PresentationToolbar.js +14 -0
  39. package/dist/components/PresentationToolbar.js.map +1 -0
  40. package/dist/components/PresentationView.d.ts +18 -0
  41. package/dist/components/PresentationView.d.ts.map +1 -0
  42. package/dist/components/PresentationView.js +77 -0
  43. package/dist/components/PresentationView.js.map +1 -0
  44. package/dist/components/SlashMenu.d.ts.map +1 -1
  45. package/dist/components/SlashMenu.js +16 -0
  46. package/dist/components/SlashMenu.js.map +1 -1
  47. package/dist/components/SlidePropertiesNodeView.d.ts +3 -0
  48. package/dist/components/SlidePropertiesNodeView.d.ts.map +1 -0
  49. package/dist/components/SlidePropertiesNodeView.js +87 -0
  50. package/dist/components/SlidePropertiesNodeView.js.map +1 -0
  51. package/dist/extensions/ButtonNode.d.ts.map +1 -1
  52. package/dist/extensions/ButtonNode.js +13 -9
  53. package/dist/extensions/ButtonNode.js.map +1 -1
  54. package/dist/extensions/ButtonStripNode.d.ts +8 -0
  55. package/dist/extensions/ButtonStripNode.d.ts.map +1 -0
  56. package/dist/extensions/ButtonStripNode.js +51 -0
  57. package/dist/extensions/ButtonStripNode.js.map +1 -0
  58. package/dist/extensions/ColumnNode.d.ts +11 -0
  59. package/dist/extensions/ColumnNode.d.ts.map +1 -0
  60. package/dist/extensions/ColumnNode.js +46 -0
  61. package/dist/extensions/ColumnNode.js.map +1 -0
  62. package/dist/extensions/ColumnsNode.d.ts +7 -0
  63. package/dist/extensions/ColumnsNode.d.ts.map +1 -0
  64. package/dist/extensions/ColumnsNode.js +56 -0
  65. package/dist/extensions/ColumnsNode.js.map +1 -0
  66. package/dist/extensions/SlideMarkerPlugin.d.ts +9 -0
  67. package/dist/extensions/SlideMarkerPlugin.d.ts.map +1 -0
  68. package/dist/extensions/SlideMarkerPlugin.js +78 -0
  69. package/dist/extensions/SlideMarkerPlugin.js.map +1 -0
  70. package/dist/extensions/SlidePropertiesNode.d.ts +8 -0
  71. package/dist/extensions/SlidePropertiesNode.d.ts.map +1 -0
  72. package/dist/extensions/SlidePropertiesNode.js +74 -0
  73. package/dist/extensions/SlidePropertiesNode.js.map +1 -0
  74. package/dist/index.d.ts +6 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +17 -1
  77. package/dist/index.js.map +1 -1
  78. package/dist/styles/ButtonStripNodeView.css +134 -0
  79. package/dist/styles/ChartNodeView.css +9 -1
  80. package/dist/styles/ColumnsNodeView.css +89 -0
  81. package/dist/styles/PresentationToolbar.css +65 -0
  82. package/dist/styles/ReadonlyBlockRenderer.css +5 -5
  83. package/dist/styles/SlidePropertiesNodeView.css +116 -0
  84. package/dist/utils/PresentationConverter.d.ts +41 -0
  85. package/dist/utils/PresentationConverter.d.ts.map +1 -0
  86. package/dist/utils/PresentationConverter.js +1506 -0
  87. package/dist/utils/PresentationConverter.js.map +1 -0
  88. package/package.json +65 -57
  89. package/src/styles/ButtonStripNodeView.css +134 -0
  90. package/src/styles/ChartNodeView.css +9 -1
  91. package/src/styles/ColumnsNodeView.css +89 -0
  92. package/src/styles/PresentationToolbar.css +65 -0
  93. package/src/styles/ReadonlyBlockRenderer.css +5 -5
  94. package/src/styles/SlidePropertiesNodeView.css +116 -0
@@ -1,247 +1,548 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.ChartNodeView = ChartNodeView;
4
7
  const jsx_runtime_1 = require("react/jsx-runtime");
5
8
  const react_1 = require("react");
9
+ const react_dom_1 = __importDefault(require("react-dom"));
6
10
  const react_2 = require("@tiptap/react");
7
- const chart_js_1 = require("chart.js");
11
+ const bar_1 = require("@nivo/bar");
12
+ const line_1 = require("@nivo/line");
13
+ const pie_1 = require("@nivo/pie");
14
+ const radar_1 = require("@nivo/radar");
15
+ const heatmap_1 = require("@nivo/heatmap");
16
+ const treemap_1 = require("@nivo/treemap");
17
+ const scatterplot_1 = require("@nivo/scatterplot");
18
+ const sankey_1 = require("@nivo/sankey");
8
19
  const lucide_react_1 = require("lucide-react");
9
20
  require("../styles/ChartNodeView.css");
10
- chart_js_1.Chart.register(...chart_js_1.registerables);
21
+ // ─── Portal tooltip: bypasses all overflow:hidden ancestors ────────
22
+ // The webview's html > body > #root all have overflow:hidden, clipping
23
+ // Nivo's absolutely-positioned tooltip. This system:
24
+ // 1. Tracks mouse position via a module-level ref (no re-renders)
25
+ // 2. Renders tooltip via createPortal at document.body level
26
+ const mouseRef = { x: 0, y: 0 };
27
+ if (typeof window !== 'undefined') {
28
+ window.addEventListener('mousemove', (e) => {
29
+ mouseRef.x = e.clientX;
30
+ mouseRef.y = e.clientY;
31
+ }, { passive: true });
32
+ }
33
+ function PortalTooltip({ children, bg, color, border }) {
34
+ return react_dom_1.default.createPortal((0, jsx_runtime_1.jsx)("div", { style: {
35
+ position: 'fixed',
36
+ top: mouseRef.y + 14,
37
+ left: mouseRef.x + 14,
38
+ pointerEvents: 'none',
39
+ zIndex: 99999,
40
+ background: bg,
41
+ color: color,
42
+ fontSize: 12,
43
+ borderRadius: 6,
44
+ padding: '8px 12px',
45
+ boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
46
+ border: `1px solid ${border}`,
47
+ maxWidth: 300,
48
+ whiteSpace: 'nowrap',
49
+ }, children: children }), document.body);
50
+ }
51
+ // Helper to get CSS variable with robust fallback
11
52
  function getCssVar(name, fallback) {
53
+ if (typeof window === 'undefined')
54
+ return fallback;
12
55
  try {
13
- const cs = getComputedStyle(document.documentElement);
14
- return (cs.getPropertyValue(name)?.trim() || fallback).toString();
56
+ // Check both body and documentElement
57
+ const bodyVal = getComputedStyle(document.body).getPropertyValue(name).trim();
58
+ if (bodyVal)
59
+ return bodyVal;
60
+ const docVal = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
61
+ if (docVal)
62
+ return docVal;
63
+ return fallback;
15
64
  }
16
65
  catch {
17
66
  return fallback;
18
67
  }
19
68
  }
20
- function hexToRgba(hex, alpha) {
21
- const h = (hex || '').trim().replace('#', '');
22
- if (h.length !== 6)
23
- return `rgba(99, 102, 241, ${alpha})`;
24
- const r = parseInt(h.slice(0, 2), 16);
25
- const g = parseInt(h.slice(2, 4), 16);
26
- const b = parseInt(h.slice(4, 6), 16);
27
- return `rgba(${r}, ${g}, ${b}, ${alpha})`;
69
+ // Custom Teal-oriented palette
70
+ const TEAL_PALETTE = [
71
+ '#2dd4bf', // Teal 400
72
+ '#0d9488', // Teal 600
73
+ '#99f6e4', // Teal 200
74
+ '#115e59', // Teal 800
75
+ '#5eead4', // Teal 300
76
+ '#134e4a', // Teal 900
77
+ '#ccfbf1', // Teal 100
78
+ ];
79
+ // ------ Data Transformation Adapters ------
80
+ // 1. Chart.js -> Nivo Bar
81
+ // ChartJS: { labels: ['A', 'B'], datasets: [{label: 'S1', data: [10, 20]}, {label: 'S2', data: [5, 15]}] }
82
+ // Nivo Bar: [ { id: 'A', S1: 10, S2: 5 }, { id: 'B', S1: 20, S2: 15 } ]
83
+ function transformToBar(data) {
84
+ const labels = data?.labels || [];
85
+ const datasets = data?.datasets || [];
86
+ if (!labels.length)
87
+ return { data: [], keys: [] };
88
+ const result = [];
89
+ const keys = [];
90
+ // Collect keys
91
+ datasets.forEach((ds) => {
92
+ if (ds.label)
93
+ keys.push(ds.label);
94
+ });
95
+ // Build data points
96
+ labels.forEach((label, i) => {
97
+ const item = { id: label };
98
+ datasets.forEach((ds) => {
99
+ if (ds.label)
100
+ item[ds.label] = ds.data[i] || 0;
101
+ });
102
+ result.push(item);
103
+ });
104
+ return { data: result, keys };
28
105
  }
29
- function computeSuggestedMax(data) {
30
- try {
31
- const ds = Array.isArray(data?.datasets) ? data.datasets : [];
32
- const vals = [];
33
- for (const d of ds) {
34
- const arr = Array.isArray(d?.data) ? d.data : [];
35
- for (const v of arr) {
36
- const n = typeof v === 'number' ? v : parseFloat(String(v));
37
- if (Number.isFinite(n))
38
- vals.push(n);
39
- }
40
- }
41
- if (vals.length === 0)
42
- return undefined;
43
- const max = Math.max(...vals);
44
- if (!Number.isFinite(max))
45
- return undefined;
46
- const step = max <= 10 ? 2 : 10;
47
- return Math.ceil(max / step) * step;
48
- }
49
- catch {
50
- return undefined;
51
- }
106
+ // 2. Chart.js -> Nivo Line
107
+ // Nivo Line: [ { id: 'S1', data: [{x: 'A', y: 10}, {x: 'B', y: 20}] } ]
108
+ function transformToLine(data) {
109
+ const labels = data?.labels || [];
110
+ const datasets = data?.datasets || [];
111
+ const result = [];
112
+ datasets.forEach((ds) => {
113
+ const points = labels.map((label, i) => ({
114
+ x: label,
115
+ y: ds.data[i] || 0
116
+ }));
117
+ result.push({
118
+ id: ds.label || 'Series',
119
+ data: points
120
+ });
121
+ });
122
+ return result;
52
123
  }
53
- function applyDefaultStyles(chartType, data) {
54
- const primary = getCssVar('--jotx-primary', '#4f46e5');
55
- const text = getCssVar('--jotx-text', '#e5e7eb');
56
- const text2 = getCssVar('--jotx-textSecondary', '#cbd5e1');
57
- const border = getCssVar('--jotx-border', '#334155');
58
- const bg = getCssVar('--jotx-background', '#0b1220');
59
- const datasets = Array.isArray(data?.datasets) ? data.datasets : [];
60
- const labels = Array.isArray(data?.labels) ? data.labels : [];
61
- const defaultPiePalette = (count) => {
62
- // A readable palette in both dark/light themes.
63
- const base = [
64
- '#60a5fa', // blue
65
- '#34d399', // green
66
- '#fbbf24', // amber
67
- '#f87171', // red
68
- '#a78bfa', // purple
69
- '#22d3ee', // cyan
70
- '#fb7185', // pink
71
- '#4ade80' // lime
72
- ];
73
- const out = [];
74
- for (let i = 0; i < Math.max(1, count); i++) {
75
- out.push(base[i % base.length]);
76
- }
77
- return out;
78
- };
79
- // Gentle defaults for readability; only fill missing values.
80
- for (const ds of datasets) {
81
- if (chartType === 'radar') {
82
- ds.borderColor = ds.borderColor ?? primary;
83
- ds.backgroundColor = ds.backgroundColor ?? hexToRgba(primary, 0.18);
84
- ds.pointBackgroundColor = ds.pointBackgroundColor ?? primary;
85
- ds.pointBorderColor = ds.pointBorderColor ?? bg;
86
- ds.pointHoverBackgroundColor = ds.pointHoverBackgroundColor ?? primary;
87
- ds.pointHoverBorderColor = ds.pointHoverBorderColor ?? bg;
88
- ds.borderWidth = ds.borderWidth ?? 2;
89
- ds.pointRadius = ds.pointRadius ?? 3;
90
- ds.pointHoverRadius = ds.pointHoverRadius ?? 5;
91
- }
92
- else if (chartType === 'pie') {
93
- // Remove slice borders (and legend swatch borders) for a clean fill look.
94
- ds.borderWidth = 0;
95
- ds.borderColor = 'transparent';
96
- // If caller didn't provide slice colors, apply a default palette.
97
- // (Without this, Chart.js defaults can look black in our themed webview.)
98
- const hasBg = Array.isArray(ds.backgroundColor) ? ds.backgroundColor.length > 0 : typeof ds.backgroundColor === 'string' ? !!ds.backgroundColor : false;
99
- if (!hasBg) {
100
- const n = labels.length || (Array.isArray(ds.data) ? ds.data.length : 0) || 1;
101
- ds.backgroundColor = defaultPiePalette(n);
102
- ds.hoverBackgroundColor = ds.hoverBackgroundColor ?? ds.backgroundColor;
103
- }
104
- }
105
- else if (chartType === 'bar') {
106
- ds.backgroundColor = ds.backgroundColor ?? hexToRgba(primary, 0.28);
107
- ds.borderColor = ds.borderColor ?? hexToRgba(primary, 0.75);
108
- ds.borderWidth = ds.borderWidth ?? 1;
109
- }
124
+ // 3. Chart.js -> Nivo Pie
125
+ // Nivo Pie: [ { id: 'A', label: 'A', value: 10 }, { id: 'B', label: 'B', value: 20 } ]
126
+ // We only support the FIRST dataset for Pie charts (standard practice)
127
+ function transformToPie(data) {
128
+ const labels = data?.labels || [];
129
+ const ds = data?.datasets?.[0];
130
+ if (!ds)
131
+ return [];
132
+ return labels.map((label, i) => ({
133
+ id: label,
134
+ label,
135
+ value: ds.data[i] || 0
136
+ }));
137
+ }
138
+ // 4. Chart.js -> Nivo Radar
139
+ // Nivo Radar expects same format as Bar basically (indexBy keys)
140
+ // [ { id: 'Metric A', Series1: 100, Series2: 50 } ]
141
+ function transformToRadar(data) {
142
+ const labels = data?.labels || [];
143
+ const datasets = data?.datasets || [];
144
+ const result = [];
145
+ const keys = [];
146
+ datasets.forEach((ds) => {
147
+ if (ds.label)
148
+ keys.push(ds.label);
149
+ });
150
+ labels.forEach((label, i) => {
151
+ const item = { id: label };
152
+ datasets.forEach((ds) => {
153
+ if (ds.label)
154
+ item[ds.label] = ds.data[i] || 0;
155
+ });
156
+ result.push(item);
157
+ });
158
+ return { data: result, keys };
159
+ }
160
+ // 5. Chart.js -> Nivo Heatmap
161
+ // { labels: ['Mon', 'Tue'], datasets: [{ label: 'Morning', data: [10, 20] }, { label: 'Evening', data: [30, 50] }] }
162
+ // -> [ { id: 'Morning', data: [{x: 'Mon', y: 10}, {x: 'Tue', y: 20}] }, ... ]
163
+ function transformToHeatmap(data) {
164
+ const labels = data?.labels || [];
165
+ const datasets = data?.datasets || [];
166
+ return datasets.map((ds) => ({
167
+ id: ds.label,
168
+ data: labels.map((label, i) => ({
169
+ x: label,
170
+ y: ds.data[i] || 0
171
+ }))
172
+ }));
173
+ }
174
+ // 6. Chart.js -> Nivo Treemap
175
+ // Takes standard "datasets" and makes a simple hierarchy.
176
+ // { labels: ['A', 'B'], datasets: [{ label: 'Size', data: [100, 200] }] }
177
+ // -> { name: 'root', children: [ { name: 'A', loc: 100 }, { name: 'B', loc: 200 } ] }
178
+ function transformToTreemap(data) {
179
+ const labels = data?.labels || [];
180
+ const ds = data?.datasets?.[0];
181
+ if (!ds)
182
+ return { name: 'root', children: [] };
183
+ const children = labels.map((label, i) => ({
184
+ name: label,
185
+ loc: ds.data[i] || 0
186
+ }));
187
+ return { name: 'root', children };
188
+ }
189
+ // 7. Chart.js -> Nivo ScatterPlot
190
+ // { datasets: [{ label: 'Group A', data: [{x: 10, y: 14}, {x: 20, y: 30}] }] }
191
+ // -> [ { id: 'Group A', data: [{x: 10, y: 14}, ...] } ]
192
+ function transformToScatter(data) {
193
+ const datasets = data?.datasets || [];
194
+ return datasets.map((ds) => ({
195
+ id: ds.label,
196
+ data: ds.data?.map((pt) => ({
197
+ x: pt.x || 0,
198
+ y: pt.y || 0
199
+ })) || []
200
+ }));
201
+ }
202
+ // 8. Custom JSON -> Nivo Sankey
203
+ // Sankey doesn't map well to Chart.js structure. We expect direct Nivo structure key 'sankey'.
204
+ // Or we try to adapt:
205
+ // { labels: ['A', 'B', 'C'], datasets: [{ data: [ { source: 'A', target: 'B', value: 10 } ] }] }
206
+ function transformToSankey(data) {
207
+ // If user provides native Sankey structure in a generic way
208
+ if (data.nodes && data.links)
209
+ return data;
210
+ // Otherwise try to adapt from datasets if possible
211
+ const ds = data?.datasets?.[0];
212
+ if (ds && Array.isArray(ds.data)) {
213
+ // Collect all unique nodes from links
214
+ const links = ds.data;
215
+ const nodeIds = new Set();
216
+ links.forEach((l) => {
217
+ nodeIds.add(l.source);
218
+ nodeIds.add(l.target);
219
+ });
220
+ // Check if we have explicit nodes, else exact simple ones
221
+ const nodes = Array.from(nodeIds).map(id => ({ id }));
222
+ return { nodes, links };
110
223
  }
111
- return {
112
- colors: { primary, text, text2, border, bg },
113
- data: { ...data, datasets }
114
- };
224
+ return { nodes: [], links: [] };
115
225
  }
116
226
  function defaultChartJson(type) {
117
227
  if (type === 'pie') {
118
228
  return JSON.stringify({
119
- labels: ['A', 'B', 'C'],
120
- datasets: [{ label: 'Share', data: [30, 45, 25] }]
229
+ labels: ['Task A', 'Task B', 'Task C'],
230
+ datasets: [{ label: 'Hours', data: [30, 45, 25] }]
121
231
  }, null, 2);
122
232
  }
123
233
  if (type === 'radar') {
124
234
  return JSON.stringify({
125
- labels: ['Speed', 'Quality', 'Cost', 'DX', 'Reliability'],
126
- datasets: [{ label: 'Score', data: [70, 80, 60, 85, 75] }]
235
+ labels: ['Speed', 'Reliability', 'Cost', 'UX', 'Security'],
236
+ datasets: [{ label: 'Product A', data: [90, 80, 60, 85, 70] }]
237
+ }, null, 2);
238
+ }
239
+ if (type === 'heatmap') {
240
+ return JSON.stringify({
241
+ labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
242
+ datasets: [
243
+ { label: 'Morning', data: [10, 20, 15, 25, 30] },
244
+ { label: 'Afternoon', data: [35, 40, 30, 20, 45] },
245
+ { label: 'Evening', data: [15, 25, 40, 50, 60] }
246
+ ]
247
+ }, null, 2);
248
+ }
249
+ if (type === 'treemap') {
250
+ return JSON.stringify({
251
+ labels: ['React', 'Vue', 'Angular', 'Svelte', 'Solid'],
252
+ datasets: [{ label: 'Usage', data: [55, 30, 25, 15, 10] }]
253
+ }, null, 2);
254
+ }
255
+ if (type === 'scatterplot') {
256
+ return JSON.stringify({
257
+ datasets: [
258
+ {
259
+ label: 'Group A',
260
+ data: [{ x: 10, y: 14 }, { x: 20, y: 30 }, { x: 30, y: 45 }, { x: 40, y: 60 }]
261
+ },
262
+ {
263
+ label: 'Group B',
264
+ data: [{ x: 15, y: 24 }, { x: 25, y: 35 }, { x: 35, y: 20 }, { x: 45, y: 50 }]
265
+ }
266
+ ]
267
+ }, null, 2);
268
+ }
269
+ if (type === 'sankey') {
270
+ return JSON.stringify({
271
+ datasets: [{
272
+ data: [
273
+ { source: 'A', target: 'B', value: 50 },
274
+ { source: 'A', target: 'C', value: 30 },
275
+ { source: 'B', target: 'D', value: 40 },
276
+ { source: 'C', target: 'D', value: 20 }
277
+ ]
278
+ }]
127
279
  }, null, 2);
128
280
  }
129
- // bar default
281
+ // bar/line default
130
282
  return JSON.stringify({
131
- labels: ['Jan', 'Feb', 'Mar', 'Apr'],
132
- datasets: [{ label: 'Events', data: [12, 19, 8, 15] }]
283
+ labels: ['Q1', 'Q2', 'Q3', 'Q4'],
284
+ datasets: [{ label: 'Revenue', data: [1200, 1900, 800, 1500] }]
133
285
  }, null, 2);
134
286
  }
287
+ // --- Component ---
135
288
  function ChartNodeView({ node, updateAttributes, selected }) {
136
- const canvasRef = (0, react_1.useRef)(null);
137
- const chartRef = (0, react_1.useRef)(null);
138
- // Determine if source should be shown. Default to attribute, but if it's a new empty/default node, show it.
139
- // Read directly from node.attrs to make it reactive
140
289
  const showSource = node.attrs.showSource || false;
141
- (0, react_1.useEffect)(() => {
142
- const currentJson = (node.attrs.json || '').toString();
143
- const isNew = !currentJson || currentJson === defaultChartJson((node.attrs.chartType || 'bar').toString());
144
- if (isNew && !showSource) {
145
- updateAttributes({ showSource: true });
146
- }
147
- }, []);
148
- const setShowSource = (val) => {
149
- const current = node.attrs.showSource || false;
150
- const next = typeof val === 'function' ? val(current) : val;
151
- updateAttributes({ showSource: next });
152
- };
153
- const [error, setError] = (0, react_1.useState)(null);
154
290
  const chartType = (node.attrs.chartType || 'bar').toString();
155
291
  const json = (node.attrs.json || '').toString() || defaultChartJson(chartType);
156
- const id = (0, react_1.useMemo)(() => `chart_${Math.random().toString(36).slice(2)}`, []);
292
+ const [error, setError] = (0, react_1.useState)(null);
293
+ // Tooltip style state for portal tooltips
294
+ const [tooltipStyle, setTooltipStyle] = (0, react_1.useState)({ bg: '#252526', color: '#ffffff', border: '#444444' });
295
+ // Track dark mode for Sankey link colors
296
+ const [isDark, setIsDark] = (0, react_1.useState)(true);
297
+ // Theme state
298
+ const [theme, setTheme] = (0, react_1.useState)({
299
+ axis: {
300
+ ticks: { text: { fill: '#888' } },
301
+ legend: { text: { fill: '#888' } }
302
+ },
303
+ labels: { text: { fill: '#888' } },
304
+ legends: { text: { fill: '#888' } }
305
+ });
306
+ // Robust theme extraction
307
+ const updateTheme = () => {
308
+ // Try to detect if we are in dark mode to choose a good default
309
+ const isDarkMode = document.body.classList.contains('vscode-dark') ||
310
+ document.body.getAttribute('data-vscode-theme-kind') === 'vscode-dark';
311
+ setIsDark(isDarkMode);
312
+ // Core colors
313
+ const textPrimary = getCssVar('--jotx-text', isDarkMode ? '#e0e0e0' : '#333333');
314
+ const textSecondary = getCssVar('--vscode-descriptionForeground', isDarkMode ? '#aaaaaa' : '#666666');
315
+ const gridColor = getCssVar('--jotx-border', isDarkMode ? '#444444' : '#e0e0e0');
316
+ const tooltipBg = getCssVar('--jotx-background', isDarkMode ? '#252526' : '#ffffff');
317
+ const tooltipColor = getCssVar('--jotx-text', isDarkMode ? '#ffffff' : '#000000');
318
+ // Store tooltip style for portal tooltips
319
+ setTooltipStyle({ bg: tooltipBg, color: tooltipColor, border: gridColor });
320
+ setTheme({
321
+ background: 'transparent',
322
+ textColor: textPrimary,
323
+ axis: {
324
+ domain: {
325
+ line: {
326
+ stroke: gridColor,
327
+ strokeWidth: 1
328
+ }
329
+ },
330
+ ticks: {
331
+ line: {
332
+ stroke: gridColor,
333
+ strokeWidth: 1
334
+ },
335
+ text: {
336
+ fill: textSecondary, // Use slightly muted color for axis ticks
337
+ fontSize: 11
338
+ }
339
+ },
340
+ legend: {
341
+ text: {
342
+ fill: textPrimary, // Use primary color for titles
343
+ fontSize: 12,
344
+ fontWeight: 600
345
+ }
346
+ }
347
+ },
348
+ grid: {
349
+ line: {
350
+ stroke: gridColor,
351
+ strokeWidth: 1,
352
+ strokeDasharray: '4 4'
353
+ }
354
+ },
355
+ labels: {
356
+ text: {
357
+ fill: textPrimary,
358
+ fontSize: 12,
359
+ fontWeight: 600
360
+ }
361
+ },
362
+ tooltip: {
363
+ container: {
364
+ background: tooltipBg,
365
+ color: tooltipColor,
366
+ fontSize: 12,
367
+ borderRadius: 4,
368
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.25)',
369
+ border: `1px solid ${gridColor}`,
370
+ zIndex: 1000 // Ensure tooltip is above everything
371
+ }
372
+ },
373
+ legends: {
374
+ text: {
375
+ fill: textSecondary,
376
+ fontSize: 12
377
+ }
378
+ }
379
+ });
380
+ };
381
+ // Initial and dynamic theme update
157
382
  (0, react_1.useEffect)(() => {
158
- // hydrate default json once if empty
159
- if (!node.attrs.json) {
160
- updateAttributes({ json });
161
- }
162
- // eslint-disable-next-line react-hooks/exhaustive-deps
383
+ updateTheme();
384
+ // Observe body for class changes (theme switching in VS Code)
385
+ const observer = new MutationObserver((mutations) => {
386
+ updateTheme();
387
+ });
388
+ observer.observe(document.body, {
389
+ attributes: true,
390
+ attributeFilter: ['class', 'data-vscode-theme-kind']
391
+ });
392
+ return () => observer.disconnect();
163
393
  }, []);
164
- (0, react_1.useEffect)(() => {
165
- const el = canvasRef.current;
166
- if (!el)
167
- return;
168
- const ctx = el.getContext('2d');
169
- if (!ctx)
170
- return;
394
+ // Parse JSON
395
+ const chartData = (0, react_1.useMemo)(() => {
171
396
  try {
172
- const parsedRaw = JSON.parse(json);
173
- const themed = applyDefaultStyles(chartType, parsedRaw);
174
- const parsed = themed.data;
175
- const { text, text2, border } = themed.colors;
176
- const suggestedMax = chartType === 'radar' ? computeSuggestedMax(parsed) : undefined;
397
+ const parsed = JSON.parse(json);
177
398
  setError(null);
178
- chartRef.current?.destroy();
179
- chartRef.current = new chart_js_1.Chart(ctx, {
180
- type: chartType,
181
- data: parsed,
182
- options: {
183
- responsive: true,
184
- maintainAspectRatio: false,
185
- animation: { duration: 180 },
186
- layout: { padding: chartType === 'radar' ? 16 : 10 },
187
- plugins: {
188
- legend: {
189
- display: true,
190
- labels: {
191
- color: text2,
192
- boxWidth: 14,
193
- boxHeight: 8,
194
- // Pie legend should be solid color swatches (no borders). Point-style looks odd here.
195
- usePointStyle: chartType === 'pie' ? false : true
196
- }
197
- }
198
- },
199
- ...(chartType === 'radar'
200
- ? {
201
- scales: {
202
- r: {
203
- beginAtZero: true,
204
- suggestedMax,
205
- grid: {
206
- color: hexToRgba(border, 0.45),
207
- circular: true
208
- },
209
- angleLines: {
210
- color: hexToRgba(border, 0.55)
211
- },
212
- pointLabels: {
213
- color: text,
214
- font: { size: 12, weight: '600' },
215
- padding: 6
216
- },
217
- ticks: {
218
- display: false
219
- }
220
- }
221
- },
222
- elements: {
223
- line: { tension: 0.25 }
224
- }
225
- }
226
- : {})
227
- }
228
- });
399
+ return parsed;
229
400
  }
230
401
  catch (e) {
231
- chartRef.current?.destroy();
232
- chartRef.current = null;
233
- setError(e?.message || 'Invalid chart JSON');
402
+ setError(e.message);
403
+ return null;
234
404
  }
235
- return () => {
236
- // keep chart alive between renders; only destroy on unmount
237
- };
238
- }, [chartType, json, id]);
239
- (0, react_1.useEffect)(() => {
240
- return () => {
241
- chartRef.current?.destroy();
242
- chartRef.current = null;
243
- };
244
- }, []);
245
- return ((0, jsx_runtime_1.jsxs)(react_2.NodeViewWrapper, { className: "jotx-chart", "data-type": "chart", "data-chart-type": chartType, "data-selected": selected ? 'true' : 'false', children: [(0, jsx_runtime_1.jsxs)("div", { className: "jotx-chart-header", children: [(0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-title", children: "Chart" }), (0, jsx_runtime_1.jsx)("input", { type: "text", className: "jotx-block-title-input", placeholder: "Title (shown in graph)...", value: (node.attrs.title || '').toString(), onChange: (e) => updateAttributes({ title: e.target.value }) }), (0, jsx_runtime_1.jsxs)("div", { className: "jotx-chart-type", children: [(0, jsx_runtime_1.jsx)("button", { className: `jotx-chart-type-btn ${chartType === 'bar' ? 'active' : ''}`, onMouseDown: (e) => e.preventDefault(), onClick: () => updateAttributes({ chartType: 'bar', json: node.attrs.json || defaultChartJson('bar') }), title: "Bar", children: (0, jsx_runtime_1.jsx)(lucide_react_1.BarChart3, { size: 16 }) }), (0, jsx_runtime_1.jsx)("button", { className: `jotx-chart-type-btn ${chartType === 'pie' ? 'active' : ''}`, onMouseDown: (e) => e.preventDefault(), onClick: () => updateAttributes({ chartType: 'pie', json: node.attrs.json || defaultChartJson('pie') }), title: "Pie", children: (0, jsx_runtime_1.jsx)(lucide_react_1.PieChart, { size: 16 }) }), (0, jsx_runtime_1.jsx)("button", { className: `jotx-chart-type-btn ${chartType === 'radar' ? 'active' : ''}`, onMouseDown: (e) => e.preventDefault(), onClick: () => updateAttributes({ chartType: 'radar', json: node.attrs.json || defaultChartJson('radar') }), title: "Radar", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Radar, { size: 16 }) })] }), (0, jsx_runtime_1.jsx)("button", { className: "jotx-chart-settings", onMouseDown: (e) => e.preventDefault(), onClick: () => setShowSource((s) => !s), title: (node.attrs.showSource || false) ? "Hide Source" : "Show Source", children: (node.attrs.showSource || false) ? (0, jsx_runtime_1.jsx)(lucide_react_1.EyeOff, { size: 16 }) : (0, jsx_runtime_1.jsx)(lucide_react_1.Eye, { size: 16 }) })] }), error ? (0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-error", children: error }) : null, (0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-canvasWrap", children: (0, jsx_runtime_1.jsx)("canvas", { ref: canvasRef }) }), (node.attrs.showSource || false) ? ((0, jsx_runtime_1.jsxs)("div", { className: "jotx-chart-editor", children: [(0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-editorHint", children: "Edit JSON (labels + datasets)" }), (0, jsx_runtime_1.jsx)("textarea", { value: json, onChange: (e) => updateAttributes({ json: e.target.value }), spellCheck: false })] })) : null] }));
405
+ }, [json]);
406
+ // Render correct chart type (Memoized to prevent flickering/lag on hover)
407
+ // Note: Nivo charts are heavy/complex, avoiding re-renders helps responsiveness.
408
+ const chartComponent = (0, react_1.useMemo)(() => {
409
+ if (!chartData)
410
+ return null;
411
+ switch (chartType) {
412
+ case 'bar':
413
+ const barData = transformToBar(chartData);
414
+ return ((0, jsx_runtime_1.jsx)(bar_1.ResponsiveBar, { data: barData.data, keys: barData.keys, indexBy: "id", margin: { top: 20, right: 20, bottom: 50, left: 60 }, padding: 0.3, valueScale: { type: 'linear' }, colors: TEAL_PALETTE, theme: theme, animate: true, labelSkipWidth: 12, labelSkipHeight: 12, labelTextColor: { from: 'color', modifiers: [['darker', 2]] }, tooltip: ({ id, value, color }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color }, children: "\u25CF" }), " ", id, ": ", (0, jsx_runtime_1.jsx)("strong", { children: value })] })), axisBottom: {
415
+ tickSize: 5,
416
+ tickPadding: 5,
417
+ tickRotation: 0,
418
+ legend: 'Category',
419
+ legendPosition: 'middle',
420
+ legendOffset: 36
421
+ }, axisLeft: {
422
+ tickSize: 5,
423
+ tickPadding: 5,
424
+ tickRotation: 0,
425
+ legend: 'Value',
426
+ legendPosition: 'middle',
427
+ legendOffset: -45
428
+ } }));
429
+ case 'line':
430
+ const lineData = transformToLine(chartData);
431
+ return ((0, jsx_runtime_1.jsx)(line_1.ResponsiveLine, { data: lineData, margin: { top: 20, right: 30, bottom: 50, left: 60 }, xScale: { type: 'point' }, yScale: { type: 'linear', min: 'auto', max: 'auto', stacked: false, reverse: false }, axisTop: null, axisRight: null, axisBottom: {
432
+ tickSize: 5,
433
+ tickPadding: 5,
434
+ tickRotation: 0,
435
+ legend: 'Time',
436
+ legendOffset: 36,
437
+ legendPosition: 'middle'
438
+ }, axisLeft: {
439
+ tickSize: 5,
440
+ tickPadding: 5,
441
+ tickRotation: 0,
442
+ legend: 'Value',
443
+ legendOffset: -45,
444
+ legendPosition: 'middle'
445
+ }, pointSize: 10, pointColor: { theme: 'background' }, pointBorderWidth: 2, pointBorderColor: { from: 'serieColor' }, pointLabelYOffset: -12, useMesh: true, theme: theme, colors: TEAL_PALETTE, tooltip: ({ point }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: point.serieColor }, children: "\u25CF" }), " ", point.serieId, ": ", (0, jsx_runtime_1.jsx)("strong", { children: point.data.yFormatted })] })) }));
446
+ case 'pie':
447
+ case 'doughnut': // Adapt doughnut as Pie with innerRadius
448
+ const pieData = transformToPie(chartData);
449
+ return ((0, jsx_runtime_1.jsx)(pie_1.ResponsivePie, { data: pieData, margin: { top: 40, right: 80, bottom: 80, left: 80 }, innerRadius: chartType === 'doughnut' ? 0.5 : 0, padAngle: 0.7, cornerRadius: 3, activeOuterRadiusOffset: 8, borderWidth: 1, borderColor: { from: 'color', modifiers: [['darker', 0.2]] }, arcLinkLabelsSkipAngle: 10, arcLinkLabelsTextColor: { from: 'color', modifiers: [] }, arcLinkLabelsThickness: 2, arcLinkLabelsColor: { from: 'color' }, arcLabelsSkipAngle: 10, arcLabelsTextColor: { from: 'color', modifiers: [['darker', 2]] }, theme: theme, colors: TEAL_PALETTE, tooltip: ({ datum }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: datum.color }, children: "\u25CF" }), " ", datum.id, ": ", (0, jsx_runtime_1.jsx)("strong", { children: datum.value })] })) }));
450
+ case 'radar':
451
+ const radarData = transformToRadar(chartData);
452
+ return ((0, jsx_runtime_1.jsx)(radar_1.ResponsiveRadar, { data: radarData.data, keys: radarData.keys, indexBy: "id", margin: { top: 70, right: 80, bottom: 40, left: 80 }, borderColor: { from: 'color' }, gridLabelOffset: 36, dotSize: 10, dotColor: { theme: 'background' }, dotBorderWidth: 2, colors: TEAL_PALETTE, blendMode: "normal", motionConfig: "wobbly", theme: theme, sliceTooltip: ({ index, data }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("strong", { children: index }), data.map((d) => ((0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: 6, alignItems: 'center' }, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: d.color }, children: "\u25CF" }), " ", d.id, ": ", d.value] }, d.id)))] })) }));
453
+ case 'heatmap':
454
+ const heatData = transformToHeatmap(chartData);
455
+ return ((0, jsx_runtime_1.jsx)(heatmap_1.ResponsiveHeatMap, { data: heatData, margin: { top: 60, right: 90, bottom: 60, left: 90 }, valueFormat: ">-.2s", axisTop: {
456
+ tickSize: 5,
457
+ tickPadding: 5,
458
+ tickRotation: -90,
459
+ legend: '',
460
+ legendOffset: 46
461
+ }, axisRight: {
462
+ tickSize: 5,
463
+ tickPadding: 5,
464
+ tickRotation: 0,
465
+ legend: '',
466
+ legendPosition: 'middle',
467
+ legendOffset: 70
468
+ }, axisLeft: {
469
+ tickSize: 5,
470
+ tickPadding: 5,
471
+ tickRotation: 0,
472
+ legend: '',
473
+ legendPosition: 'middle',
474
+ legendOffset: -72
475
+ }, colors: {
476
+ type: 'sequential',
477
+ scheme: 'blues' // Matches our teal theme roughly
478
+ }, emptyColor: "#555555", theme: theme, tooltip: ({ cell }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [cell.serieId, " / ", cell.data.x, ": ", (0, jsx_runtime_1.jsx)("strong", { children: cell.data.y ?? cell.value })] })) }));
479
+ case 'treemap':
480
+ const treeData = transformToTreemap(chartData);
481
+ return ((0, jsx_runtime_1.jsx)(treemap_1.ResponsiveTreeMap, { data: treeData, identity: "name", value: "loc", valueFormat: ".02s", margin: { top: 10, right: 10, bottom: 10, left: 10 }, labelSkipSize: 12, labelTextColor: { from: 'color', modifiers: [['darker', 2]] }, parentLabelPosition: "left", parentLabelTextColor: { from: 'color', modifiers: [['darker', 2]] }, borderColor: { from: 'color', modifiers: [['darker', 0.1]] }, colors: TEAL_PALETTE, theme: theme, tooltip: ({ node }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: node.color }, children: "\u25CF" }), " ", node.id, ": ", (0, jsx_runtime_1.jsx)("strong", { children: node.formattedValue })] })) }));
482
+ case 'scatterplot':
483
+ const scatterData = transformToScatter(chartData);
484
+ return ((0, jsx_runtime_1.jsx)(scatterplot_1.ResponsiveScatterPlot, { data: scatterData, margin: { top: 60, right: 140, bottom: 70, left: 90 }, xScale: { type: 'linear', min: 0, max: 'auto' }, xFormat: ">-.2f", yScale: { type: 'linear', min: 0, max: 'auto' }, yFormat: ">-.2f", blendMode: "normal", colors: TEAL_PALETTE, theme: theme, tooltip: ({ node }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: node.color }, children: "\u25CF" }), " ", node.serieId, ": (", node.formattedX, ", ", node.formattedY, ")"] })), axisTop: null, axisRight: null, axisBottom: {
485
+ tickSize: 5,
486
+ tickPadding: 5,
487
+ tickRotation: 0,
488
+ legend: 'X',
489
+ legendPosition: 'middle',
490
+ legendOffset: 46
491
+ }, axisLeft: {
492
+ tickSize: 5,
493
+ tickPadding: 5,
494
+ tickRotation: 0,
495
+ legend: 'Y',
496
+ legendPosition: 'middle',
497
+ legendOffset: -60
498
+ } }));
499
+ case 'sankey':
500
+ const sankeyData = transformToSankey(chartData);
501
+ // Inject link colors based on theme - startColor/endColor on each link
502
+ // Using SOLID colors (no rgba) + linkOpacity=1 so there's no double-transparency
503
+ const flowColor = isDark ? '#A8C4DC' : '#666666';
504
+ const themedSankeyData = {
505
+ ...sankeyData,
506
+ links: sankeyData.links.map((l) => ({
507
+ ...l,
508
+ startColor: l.startColor || flowColor,
509
+ endColor: l.endColor || flowColor,
510
+ }))
511
+ };
512
+ return ((0, jsx_runtime_1.jsx)(sankey_1.ResponsiveSankey, { data: themedSankeyData, margin: { top: 40, right: 160, bottom: 40, left: 50 }, align: "justify", colors: TEAL_PALETTE, nodeOpacity: 1, nodeHoverOthersOpacity: 0.35, nodeThickness: 18, nodeSpacing: 24, nodeBorderWidth: 0, nodeBorderColor: { from: 'color', modifiers: [['darker', 0.8]] }, linkOpacity: 0.5, linkHoverOthersOpacity: 0.1, linkContract: 3, linkBlendMode: "normal", enableLinkGradient: true, labelPosition: "outside", labelOrientation: "vertical", labelPadding: 16, labelTextColor: { from: 'color', modifiers: [['darker', 1]] }, nodeTooltip: ({ node }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: node.color }, children: "\u25CF" }), " ", node.label, ": ", (0, jsx_runtime_1.jsx)("strong", { children: node.value })] })), linkTooltip: ({ link }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [link.source.label, " \u2192 ", link.target.label, ": ", (0, jsx_runtime_1.jsx)("strong", { children: link.value })] })), theme: {
513
+ ...theme,
514
+ labels: { text: { fill: theme.textColor } }
515
+ } }));
516
+ default:
517
+ return (0, jsx_runtime_1.jsxs)("div", { children: ["Unknown chart type: ", chartType] });
518
+ }
519
+ }, [chartData, chartType, theme, tooltipStyle, isDark]);
520
+ // Confirmation state for switching types
521
+ const [pendingType, setPendingType] = (0, react_1.useState)(null);
522
+ const handleTypeSwitch = (newType) => {
523
+ if (newType === chartType)
524
+ return; // Already this type
525
+ // Always show confirmation before switching
526
+ setPendingType(newType);
527
+ };
528
+ const confirmSwitch = () => {
529
+ if (pendingType) {
530
+ updateAttributes({ chartType: pendingType, json: defaultChartJson(pendingType) });
531
+ setPendingType(null);
532
+ }
533
+ };
534
+ const cancelSwitch = () => {
535
+ setPendingType(null);
536
+ };
537
+ return ((0, jsx_runtime_1.jsxs)(react_2.NodeViewWrapper, { className: "jotx-chart", "data-type": "chart", "data-chart-type": chartType, "data-selected": selected ? 'true' : 'false', children: [(0, jsx_runtime_1.jsxs)("div", { className: "jotx-chart-header", children: [(0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-title", children: "Chart" }), (0, jsx_runtime_1.jsx)("input", { type: "text", className: "jotx-block-title-input", placeholder: "Title (shown in graph)...", value: (node.attrs.title || '').toString(), onChange: (e) => updateAttributes({ title: e.target.value }) }), (0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-type", children: ['bar', 'line', 'pie', 'radar', 'heatmap', 'treemap', 'scatterplot', 'sankey'].map(type => ((0, jsx_runtime_1.jsxs)("button", { className: `jotx-chart-type-btn ${chartType === type ? 'active' : ''}`, onMouseDown: (e) => e.preventDefault(), onClick: () => handleTypeSwitch(type), title: type.charAt(0).toUpperCase() + type.slice(1), children: [type === 'bar' && (0, jsx_runtime_1.jsx)(lucide_react_1.BarChart3, { size: 16 }), type === 'line' && (0, jsx_runtime_1.jsx)(lucide_react_1.Activity, { size: 16 }), type === 'pie' && (0, jsx_runtime_1.jsx)(lucide_react_1.PieChart, { size: 16 }), type === 'radar' && (0, jsx_runtime_1.jsx)(lucide_react_1.Radar, { size: 16 }), type === 'heatmap' && (0, jsx_runtime_1.jsx)(lucide_react_1.Grid, { size: 16 }), type === 'treemap' && (0, jsx_runtime_1.jsx)(lucide_react_1.LayoutDashboard, { size: 16 }), type === 'scatterplot' && (0, jsx_runtime_1.jsx)(lucide_react_1.ScatterChart, { size: 16 }), type === 'sankey' && (0, jsx_runtime_1.jsx)(lucide_react_1.GitMerge, { size: 16 })] }, type))) }), (0, jsx_runtime_1.jsx)("button", { className: "jotx-chart-settings", onMouseDown: (e) => e.preventDefault(), onClick: () => {
538
+ const next = !showSource;
539
+ updateAttributes({ showSource: next });
540
+ }, title: showSource ? "Hide Source" : "Show Source", children: showSource ? (0, jsx_runtime_1.jsx)(lucide_react_1.EyeOff, { size: 16 }) : (0, jsx_runtime_1.jsx)(lucide_react_1.Eye, { size: 16 }) })] }), pendingType && ((0, jsx_runtime_1.jsxs)("div", { className: "jotx-chart-confirm-overlay", style: {
541
+ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
542
+ background: 'rgba(0,0,0,0.8)', zIndex: 50,
543
+ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
544
+ color: 'white', padding: '20px', textAlign: 'center',
545
+ borderRadius: '8px'
546
+ }, children: [(0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '16px', fontWeight: 600 }, children: ["Switching to ", pendingType, " will reset your data."] }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: '10px' }, children: [(0, jsx_runtime_1.jsx)("button", { onClick: confirmSwitch, style: { padding: '6px 12px', background: '#d93025', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }, children: "Reset & Switch" }), (0, jsx_runtime_1.jsx)("button", { onClick: cancelSwitch, style: { padding: '6px 12px', background: '#555', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }, children: "Cancel" })] })] })), error ? (0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-error", children: error }) : null, (0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-canvasWrap", style: { height: '320px', width: '100%', position: 'relative', zIndex: 1, overflow: 'visible' }, children: chartComponent }), showSource ? ((0, jsx_runtime_1.jsxs)("div", { className: "jotx-chart-editor", children: [(0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-editorHint", children: "Edit JSON (labels + datasets)" }), (0, jsx_runtime_1.jsx)("textarea", { value: json, onChange: (e) => updateAttributes({ json: e.target.value }), spellCheck: false })] })) : null] }));
246
547
  }
247
548
  //# sourceMappingURL=ChartNodeView.js.map