@creative-dswork/dscode 0.2.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.
@@ -0,0 +1,566 @@
1
+ (function() {
2
+ "use strict";
3
+
4
+ // ── Utility ──
5
+ function el(tag, className, text) {
6
+ var e = document.createElement(tag);
7
+ if (className) e.className = className;
8
+ if (text !== undefined) e.textContent = text;
9
+ return e;
10
+ }
11
+
12
+ function fmtCurrency(v) {
13
+ var a = Math.abs(v), s = v < 0 ? "-" : "";
14
+ if (a >= 1e6) return s + "$" + (a / 1e6).toFixed(2) + "M";
15
+ if (a >= 1e3) return s + "$" + (a / 1e3).toFixed(1) + "K";
16
+ return s + "$" + Math.round(a);
17
+ }
18
+
19
+ function fmtNumber(v) {
20
+ if (v >= 1e6) return (v / 1e6).toFixed(2) + "M";
21
+ if (v >= 1e3) return (v / 1e3).toFixed(1) + "K";
22
+ return String(Math.round(v));
23
+ }
24
+
25
+ function getCssVar(name, fallback) {
26
+ var v = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
27
+ return v || fallback;
28
+ }
29
+
30
+ // ── Data Binding ──
31
+ function resolveBindingPath(path, data) {
32
+ var parts = path.split(".");
33
+ var current = data;
34
+ for (var i = 0; i < parts.length; i++) {
35
+ if (current && typeof current === "object") {
36
+ current = current[parts[i]];
37
+ } else {
38
+ return undefined;
39
+ }
40
+ }
41
+ return current;
42
+ }
43
+
44
+ function setBindingPath(path, data, value) {
45
+ var parts = path.split(".");
46
+ var current = data;
47
+ for (var i = 0; i < parts.length - 1; i++) {
48
+ if (!current[parts[i]] || typeof current[parts[i]] !== "object") {
49
+ current[parts[i]] = {};
50
+ }
51
+ current = current[parts[i]];
52
+ }
53
+ current[parts[parts.length - 1]] = value;
54
+ }
55
+
56
+ function resolveBindings(value, data) {
57
+ if (value === null || value === undefined) return value;
58
+ if (typeof value === "object" && value !== null && "binding" in value) {
59
+ return resolveBindingPath(value.binding, data);
60
+ }
61
+ if (Array.isArray(value)) {
62
+ return value.map(function(v) { return resolveBindings(v, data); });
63
+ }
64
+ if (typeof value === "object") {
65
+ var resolved = {};
66
+ var keys = Object.keys(value);
67
+ for (var i = 0; i < keys.length; i++) {
68
+ resolved[keys[i]] = resolveBindings(value[keys[i]], data);
69
+ }
70
+ return resolved;
71
+ }
72
+ return value;
73
+ }
74
+
75
+ // ── MDX Parser ──
76
+ var ALL_COMPONENTS = ["Chart", "Metrics", "Table", "Slider", "Card", "Row"];
77
+
78
+ function findMatchingClose(source, start, tag) {
79
+ var openTag = "<" + tag;
80
+ var closeTag = "</" + tag + ">";
81
+ var depth = 1;
82
+ var i = start;
83
+
84
+ while (i < source.length) {
85
+ var nextOpen = source.indexOf(openTag, i);
86
+ var nextClose = source.indexOf(closeTag, i);
87
+ if (nextClose === -1) return -1;
88
+
89
+ if (nextOpen !== -1 && nextOpen < nextClose) {
90
+ var afterOpen = source[nextOpen + openTag.length];
91
+ if (afterOpen === " " || afterOpen === ">" || afterOpen === "/") depth++;
92
+ i = nextOpen + openTag.length;
93
+ } else {
94
+ depth--;
95
+ if (depth === 0) return nextClose;
96
+ i = nextClose + closeTag.length;
97
+ }
98
+ }
99
+ return -1;
100
+ }
101
+
102
+ function parseAttributes(attrStr) {
103
+ var props = {};
104
+ if (!attrStr) return props;
105
+
106
+ var i = 0;
107
+ while (i < attrStr.length) {
108
+ while (i < attrStr.length && /\s/.test(attrStr[i])) i++;
109
+ if (i >= attrStr.length) break;
110
+
111
+ var eqIdx = attrStr.indexOf("=", i);
112
+ if (eqIdx === -1) break;
113
+
114
+ var name = attrStr.slice(i, eqIdx).trim();
115
+ i = eqIdx + 1;
116
+ if (i >= attrStr.length) break;
117
+
118
+ var ch = attrStr[i];
119
+ if (ch === '"' || ch === "'") {
120
+ var quote = ch;
121
+ i++;
122
+ var val = "";
123
+ while (i < attrStr.length && attrStr[i] !== quote) {
124
+ if (attrStr[i] === "\\" && i + 1 < attrStr.length) { i++; val += attrStr[i]; }
125
+ else { val += attrStr[i]; }
126
+ i++;
127
+ }
128
+ if (i < attrStr.length) i++;
129
+ props[name] = val;
130
+ } else if (ch === "{") {
131
+ var braceCount = 1;
132
+ i++;
133
+ var val2 = "";
134
+ while (i < attrStr.length && braceCount > 0) {
135
+ if (attrStr[i] === "{") braceCount++;
136
+ else if (attrStr[i] === "}") braceCount--;
137
+ if (braceCount > 0) val2 += attrStr[i];
138
+ i++;
139
+ }
140
+ var trimmed = val2.trim();
141
+ if (trimmed.indexOf("[") === 0 && trimmed.lastIndexOf("]") === trimmed.length - 1) {
142
+ try { props[name] = JSON.parse(trimmed); }
143
+ catch(e) { props[name] = { binding: trimmed }; }
144
+ } else if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
145
+ props[name] = Number(trimmed);
146
+ } else {
147
+ props[name] = { binding: trimmed };
148
+ }
149
+ } else {
150
+ var val3 = "";
151
+ while (i < attrStr.length && attrStr[i] !== " " && attrStr[i] !== "/" && attrStr[i] !== ">") {
152
+ val3 += attrStr[i]; i++;
153
+ }
154
+ if (val3 === "true") props[name] = true;
155
+ else if (val3 === "false") props[name] = false;
156
+ else if (/^-?\d+(\.\d+)?$/.test(val3)) props[name] = Number(val3);
157
+ else props[name] = val3;
158
+ }
159
+ }
160
+ return props;
161
+ }
162
+
163
+ function parseMDX(source, knownComponents) {
164
+ var known = knownComponents || ALL_COMPONENTS;
165
+ var elements = [];
166
+ var i = 0;
167
+
168
+ while (i < source.length) {
169
+ if (source[i] === "<") {
170
+ var closeIdx = source.indexOf(">", i);
171
+ if (closeIdx === -1) { i++; continue; }
172
+ if (source[i + 1] === "/") { i = closeIdx + 1; continue; }
173
+
174
+ var tagBlock = source.slice(i, closeIdx + 1);
175
+ var selfClosing = tagBlock.slice(-2) === "/>";
176
+ var tagMatch = tagBlock.match(/^<(\w+)/);
177
+ if (!tagMatch) { i++; continue; }
178
+
179
+ var tag = tagMatch[1];
180
+ if (known.indexOf(tag) === -1) {
181
+ elements.push({ tag: "unknown", props: { componentName: tag }, children: [], textContent: "" });
182
+ i = closeIdx + 1;
183
+ continue;
184
+ }
185
+
186
+ var content = tagBlock.slice(tag.length + 1, selfClosing ? -2 : -1).trim();
187
+ var props = parseAttributes(content);
188
+
189
+ if (selfClosing) {
190
+ elements.push({ tag: tag, props: props, children: [], textContent: "" });
191
+ i = closeIdx + 1;
192
+ } else {
193
+ var closingIdx = findMatchingClose(source, closeIdx + 1, tag);
194
+ if (closingIdx === -1) {
195
+ elements.push({ tag: tag, props: props, children: [], textContent: "" });
196
+ i = closeIdx + 1;
197
+ } else {
198
+ var inner = source.slice(closeIdx + 1, closingIdx);
199
+ var children = parseMDX(inner, known);
200
+ elements.push({ tag: tag, props: props, children: children, textContent: inner.trim() });
201
+ i = closingIdx + ("</" + tag + ">").length;
202
+ }
203
+ }
204
+ } else if (source[i] === "\n" || source[i] === " " || source[i] === "\t" || source[i] === "\r") {
205
+ i++;
206
+ } else {
207
+ var nextTag = source.indexOf("<", i);
208
+ if (nextTag === -1) break;
209
+ var text = source.slice(i, nextTag).trim();
210
+ if (text) {
211
+ elements.push({ tag: "text", props: { content: text }, children: [], textContent: text });
212
+ }
213
+ i = nextTag;
214
+ }
215
+ }
216
+ return elements;
217
+ }
218
+
219
+ // ── Components ──
220
+ var BUILTIN_COMPONENTS = {};
221
+
222
+ BUILTIN_COMPONENTS.Chart = {
223
+ render: function(element, data, container, notifyChange) {
224
+ var props = resolveBindings(element.props, data);
225
+ var chartType = props.type || "line";
226
+ var chartData = props.data;
227
+ var xField = props.x;
228
+ var yFields = props.y;
229
+
230
+ var wrapper = el("div", "mdx-chart-wrap");
231
+
232
+ if (!chartData || !Array.isArray(chartData) || chartData.length === 0 || !xField) {
233
+ wrapper.textContent = "[Chart: no data]";
234
+ return wrapper;
235
+ }
236
+
237
+ var yFieldList = Array.isArray(yFields) ? yFields
238
+ : typeof yFields === "string" ? [yFields]
239
+ : Object.keys(chartData[0]).filter(function(k) { return typeof chartData[0][k] === "number" && k !== xField; });
240
+
241
+ var canvas = document.createElement("canvas");
242
+ canvas.className = "mdx-chart";
243
+ wrapper.appendChild(canvas);
244
+
245
+ var legend = el("div", "mdx-chart-legend");
246
+ for (var fi = 0; fi < yFieldList.length; fi++) {
247
+ var item = el("span", "mdx-legend-item");
248
+ var dot = el("span", "mdx-legend-dot");
249
+ item.appendChild(dot);
250
+ item.appendChild(document.createTextNode(yFieldList[fi]));
251
+ legend.appendChild(item);
252
+ }
253
+ wrapper.appendChild(legend);
254
+
255
+ // Defer canvas render to next frame so dimensions are available
256
+ requestAnimationFrame(function() {
257
+ renderChart(canvas, chartType, chartData, xField, yFieldList);
258
+ });
259
+
260
+ var ro = new ResizeObserver(function() {
261
+ renderChart(canvas, chartType, chartData, xField, yFieldList);
262
+ });
263
+ ro.observe(canvas);
264
+
265
+ return wrapper;
266
+ }
267
+ };
268
+
269
+ function renderChart(canvas, chartType, chartData, xField, yFieldList) {
270
+ var rect = canvas.getBoundingClientRect();
271
+ if (rect.width === 0 || rect.height === 0) return;
272
+ var dpr = window.devicePixelRatio || 1;
273
+ canvas.width = rect.width * dpr;
274
+ canvas.height = rect.height * dpr;
275
+ var ctx = canvas.getContext("2d");
276
+ ctx.scale(dpr, dpr);
277
+
278
+ var w = rect.width;
279
+ var h = rect.height;
280
+ var pad = { top: 10, right: 16, bottom: 28, left: 52 };
281
+ var pw = w - pad.left - pad.right;
282
+ var ph = h - pad.top - pad.bottom;
283
+
284
+ var xVals = [];
285
+ for (var i = 0; i < chartData.length; i++) xVals.push(chartData[i][xField]);
286
+
287
+ var allY = [];
288
+ for (var fi = 0; fi < yFieldList.length; fi++) {
289
+ for (var j = 0; j < chartData.length; j++) {
290
+ var v = chartData[j][yFieldList[fi]];
291
+ if (typeof v === "number") allY.push(v);
292
+ }
293
+ }
294
+ var yMin = 0;
295
+ var yMax = Math.max.apply(null, allY.concat([1]));
296
+ var yRange = yMax - yMin || 1;
297
+
298
+ function toX(idx) { return pad.left + (idx / Math.max(chartData.length - 1, 1)) * pw; }
299
+ function toY(val) { return pad.top + ph - ((val - yMin) / yRange) * ph; }
300
+
301
+ var colors = ["#3b82f6", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
302
+ var bg = getCssVar("--bg", "#ffffff");
303
+ var textColor = getCssVar("--text", "#171717");
304
+ var border = getCssVar("--border", "#e5e7eb");
305
+ var muted = getCssVar("--muted", "#6b7280");
306
+
307
+ ctx.clearRect(0, 0, w, h);
308
+
309
+ // Grid lines
310
+ ctx.strokeStyle = border;
311
+ ctx.lineWidth = 0.5;
312
+ for (var j = 0; j <= 4; j++) {
313
+ var y = pad.top + (j / 4) * ph;
314
+ ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(w - pad.right, y); ctx.stroke();
315
+ }
316
+
317
+ // Y labels
318
+ ctx.fillStyle = muted;
319
+ ctx.font = "10px system-ui, sans-serif";
320
+ ctx.textAlign = "right";
321
+ for (var j2 = 0; j2 <= 4; j2++) {
322
+ var v2 = yMin + ((4 - j2) / 4) * yRange;
323
+ ctx.fillText(fmtCurrency(v2), pad.left - 4, pad.top + (j2 / 4) * ph + 4);
324
+ }
325
+
326
+ // X labels
327
+ ctx.textAlign = "center";
328
+ var maxXLabels = Math.min(chartData.length, 6);
329
+ var xStep = Math.max(1, Math.floor(chartData.length / maxXLabels));
330
+ for (var i2 = 0; i2 < chartData.length; i2 += xStep) {
331
+ ctx.fillText(String(xVals[i2]), toX(i2), h - pad.bottom + 14);
332
+ }
333
+
334
+ // Data lines
335
+ for (var fi2 = 0; fi2 < yFieldList.length; fi2++) {
336
+ var yf = yFieldList[fi2];
337
+ ctx.strokeStyle = colors[fi2 % colors.length];
338
+ ctx.lineWidth = 2;
339
+ ctx.beginPath();
340
+ for (var i3 = 0; i3 < chartData.length; i3++) {
341
+ var v3 = chartData[i3][yf];
342
+ var x = toX(i3);
343
+ var y2 = toY(typeof v3 === "number" ? v3 : 0);
344
+ if (i3 === 0) ctx.moveTo(x, y2); else ctx.lineTo(x, y2);
345
+ }
346
+ ctx.stroke();
347
+
348
+ ctx.fillStyle = colors[fi2 % colors.length];
349
+ for (var i4 = 0; i4 < chartData.length; i4++) {
350
+ var v4 = chartData[i4][yf];
351
+ var x2 = toX(i4);
352
+ var y3 = toY(typeof v4 === "number" ? v4 : 0);
353
+ ctx.beginPath(); ctx.arc(x2, y3, 2.5, 0, Math.PI * 2); ctx.fill();
354
+ }
355
+ }
356
+ }
357
+
358
+ BUILTIN_COMPONENTS.Metrics = {
359
+ render: function(element, data, container, notifyChange) {
360
+ var props = resolveBindings(element.props, data);
361
+ var items = props.items;
362
+ var wrapper = el("div", "mdx-metrics");
363
+
364
+ if (!items || typeof items !== "object") {
365
+ wrapper.textContent = "[Metrics: no data]";
366
+ return wrapper;
367
+ }
368
+
369
+ var grid = el("div", "mdx-metrics-grid");
370
+ var keys = Object.keys(items);
371
+ for (var i = 0; i < keys.length; i++) {
372
+ var key = keys[i];
373
+ var value = items[key];
374
+ var card = el("div", "mdx-metric-card");
375
+ var valEl = el("div", "mdx-metric-value");
376
+ valEl.textContent = typeof value === "number" ? fmtCurrency(value) : String(value);
377
+ var labelEl = el("div", "mdx-metric-label");
378
+ labelEl.textContent = key.replace(/([A-Z])/g, " $1").replace(/^./, function(s) { return s.toUpperCase(); });
379
+ card.appendChild(valEl);
380
+ card.appendChild(labelEl);
381
+ grid.appendChild(card);
382
+ }
383
+ wrapper.appendChild(grid);
384
+ return wrapper;
385
+ }
386
+ };
387
+
388
+ BUILTIN_COMPONENTS.Table = {
389
+ render: function(element, data, container, notifyChange) {
390
+ var props = resolveBindings(element.props, data);
391
+ var rows = props.rows;
392
+ var wrapper = el("div", "mdx-table-wrap");
393
+
394
+ if (!rows || !Array.isArray(rows) || rows.length === 0) {
395
+ wrapper.textContent = "[Table: no data]";
396
+ return wrapper;
397
+ }
398
+
399
+ var columns = Object.keys(rows[0]);
400
+ var table = document.createElement("table");
401
+ table.className = "mdx-table";
402
+
403
+ var thead = document.createElement("thead");
404
+ var headerRow = document.createElement("tr");
405
+ for (var ci = 0; ci < columns.length; ci++) {
406
+ var th = document.createElement("th");
407
+ th.textContent = columns[ci].replace(/([A-Z])/g, " $1").replace(/^./, function(s) { return s.toUpperCase(); });
408
+ headerRow.appendChild(th);
409
+ }
410
+ thead.appendChild(headerRow);
411
+ table.appendChild(thead);
412
+
413
+ var tbody = document.createElement("tbody");
414
+ for (var ri = 0; ri < rows.length; ri++) {
415
+ var row = rows[ri];
416
+ var tr = document.createElement("tr");
417
+ for (var cj = 0; cj < columns.length; cj++) {
418
+ var td = document.createElement("td");
419
+ var v = row[columns[cj]];
420
+ td.textContent = typeof v === "number" ? fmtNumber(v) : String(v != null ? v : "");
421
+ tr.appendChild(td);
422
+ }
423
+ tbody.appendChild(tr);
424
+ }
425
+ table.appendChild(tbody);
426
+
427
+ wrapper.appendChild(table);
428
+ return wrapper;
429
+ }
430
+ };
431
+
432
+ BUILTIN_COMPONENTS.Slider = {
433
+ render: function(element, data, container, notifyChange) {
434
+ var props = resolveBindings(element.props, data);
435
+ var param = props.param;
436
+ var min = props.min != null ? props.min : 0;
437
+ var max = props.max != null ? props.max : 100;
438
+ var step = props.step != null ? props.step : 1;
439
+ var format = props.format || "number";
440
+ var bindPath = props.bind;
441
+
442
+ var wrapper = el("div", "mdx-slider-row");
443
+
444
+ if (!param) { wrapper.textContent = "[Slider: no param]"; return wrapper; }
445
+
446
+ var label = el("span", "mdx-slider-label");
447
+ label.textContent = param.replace(/([A-Z])/g, " $1").replace(/^./, function(s) { return s.toUpperCase(); });
448
+
449
+ var input = document.createElement("input");
450
+ input.type = "range";
451
+ input.className = "mdx-slider-input";
452
+ input.min = String(min);
453
+ input.max = String(max);
454
+ input.step = String(step);
455
+
456
+ var currentVal = bindPath ? resolveBindingPath(bindPath, data) : undefined;
457
+ var initialVal = typeof currentVal === "number" ? currentVal : min;
458
+ input.value = String(initialVal);
459
+
460
+ var valueDisplay = el("span", "mdx-slider-value");
461
+ valueDisplay.textContent = format === "currency" ? fmtCurrency(initialVal) : String(initialVal);
462
+
463
+ input.addEventListener("input", function() {
464
+ var v = Number(input.value);
465
+ valueDisplay.textContent = format === "currency" ? fmtCurrency(v) : String(v);
466
+ if (bindPath) setBindingPath(bindPath, data, v);
467
+ if (notifyChange) notifyChange();
468
+ });
469
+
470
+ wrapper.appendChild(label);
471
+ wrapper.appendChild(input);
472
+ wrapper.appendChild(valueDisplay);
473
+ return wrapper;
474
+ }
475
+ };
476
+
477
+ BUILTIN_COMPONENTS.Card = {
478
+ render: function(element, data, container, notifyChange) {
479
+ var props = resolveBindings(element.props, data);
480
+ var title = props.title;
481
+ var wrapper = el("div", "mdx-card");
482
+ if (title) {
483
+ var titleEl = el("div", "mdx-card-title", title);
484
+ wrapper.appendChild(titleEl);
485
+ }
486
+ wrapper.setAttribute("data-mdx-card", "true");
487
+ return wrapper;
488
+ }
489
+ };
490
+
491
+ BUILTIN_COMPONENTS.Row = {
492
+ render: function(element, data, container, notifyChange) {
493
+ var wrapper = el("div", "mdx-row");
494
+ wrapper.setAttribute("data-mdx-row", "true");
495
+ return wrapper;
496
+ }
497
+ };
498
+
499
+ // ── Renderer ──
500
+ function renderMDX(mdx, data, container) {
501
+ container.innerHTML = "";
502
+ container.className = "mdx-root";
503
+
504
+ try {
505
+ var elements = parseMDX(mdx);
506
+ renderElements(elements, data, container, mdx);
507
+ } catch(err) {
508
+ container.innerHTML = '<div class="mdx-error">Render error: ' + (err.message || String(err)) + '</div>';
509
+ }
510
+ }
511
+
512
+ function renderElements(elements, data, parent, mdxSource) {
513
+ for (var i = 0; i < elements.length; i++) {
514
+ var rendered = renderElement(elements[i], data, mdxSource);
515
+ if (rendered) parent.appendChild(rendered);
516
+ }
517
+ }
518
+
519
+ function renderElement(element, data, mdxSource) {
520
+ if (element.tag === "text") {
521
+ var span = document.createElement("span");
522
+ span.className = "mdx-text";
523
+ span.textContent = element.props.content;
524
+ return span;
525
+ }
526
+
527
+ if (element.tag === "unknown") {
528
+ var div = document.createElement("div");
529
+ div.className = "mdx-unknown";
530
+ div.textContent = "[Unknown component: " + element.props.componentName + "]";
531
+ return div;
532
+ }
533
+
534
+ var component = BUILTIN_COMPONENTS[element.tag];
535
+ if (!component) {
536
+ var div2 = document.createElement("div");
537
+ div2.className = "mdx-unknown";
538
+ div2.textContent = "[Unknown component: " + element.tag + "]";
539
+ return div2;
540
+ }
541
+
542
+ var notifyChange = function() {
543
+ var root = document.querySelector(".mdx-root");
544
+ if (root && mdxSource) {
545
+ renderMDX(mdxSource, data, root);
546
+ }
547
+ };
548
+
549
+ var rendered = component.render(element, data, document.body, notifyChange);
550
+
551
+ if (element.children.length > 0) {
552
+ var childTarget = rendered.querySelector("[data-mdx-card]") || rendered.querySelector("[data-mdx-row]") || rendered;
553
+ for (var ci = 0; ci < element.children.length; ci++) {
554
+ var childEl = renderElement(element.children[ci], data, mdxSource);
555
+ if (childEl) childTarget.appendChild(childEl);
556
+ }
557
+ }
558
+
559
+ return rendered;
560
+ }
561
+
562
+ // ── Expose public API ──
563
+ window.renderMDX = renderMDX;
564
+ window.parseMDX = parseMDX;
565
+ window.BUILTIN_COMPONENTS = BUILTIN_COMPONENTS;
566
+ })();