bitwrench 2.0.14 → 2.0.16
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.
- package/README.md +57 -21
- package/dist/bitwrench-bccl.cjs.js +3746 -0
- package/dist/bitwrench-bccl.cjs.min.js +40 -0
- package/dist/bitwrench-bccl.esm.js +3741 -0
- package/dist/bitwrench-bccl.esm.min.js +40 -0
- package/dist/bitwrench-bccl.umd.js +3752 -0
- package/dist/bitwrench-bccl.umd.min.js +40 -0
- package/dist/bitwrench-code-edit.cjs.js +99 -49
- package/dist/bitwrench-code-edit.cjs.min.js +23 -0
- package/dist/bitwrench-code-edit.es5.js +79 -16
- package/dist/bitwrench-code-edit.es5.min.js +9 -2
- package/dist/bitwrench-code-edit.esm.js +99 -49
- package/dist/bitwrench-code-edit.esm.min.js +9 -2
- package/dist/bitwrench-code-edit.umd.js +99 -49
- package/dist/bitwrench-code-edit.umd.min.js +9 -2
- package/dist/bitwrench-lean.cjs.js +4923 -3248
- package/dist/bitwrench-lean.cjs.min.js +35 -6
- package/dist/bitwrench-lean.es5.js +6325 -4580
- package/dist/bitwrench-lean.es5.min.js +32 -3
- package/dist/bitwrench-lean.esm.js +4923 -3248
- package/dist/bitwrench-lean.esm.min.js +35 -6
- package/dist/bitwrench-lean.umd.js +4923 -3248
- package/dist/bitwrench-lean.umd.min.js +35 -6
- package/dist/bitwrench.cjs.js +5082 -3667
- package/dist/bitwrench.cjs.min.js +38 -8
- package/dist/bitwrench.css +2289 -6034
- package/dist/bitwrench.es5.js +6862 -5346
- package/dist/bitwrench.es5.min.js +34 -5
- package/dist/bitwrench.esm.js +5082 -3667
- package/dist/bitwrench.esm.min.js +38 -8
- package/dist/bitwrench.min.css +1 -0
- package/dist/bitwrench.umd.js +5082 -3667
- package/dist/bitwrench.umd.min.js +38 -8
- package/dist/builds.json +184 -74
- package/dist/bwserve.cjs.js +646 -0
- package/dist/bwserve.esm.js +638 -0
- package/dist/sri.json +36 -26
- package/package.json +23 -6
- package/readme.html +71 -32
- package/src/bitwrench-bccl-entry.js +72 -0
- package/src/{bitwrench-components-v2.js → bitwrench-bccl.js} +396 -647
- package/src/bitwrench-code-edit.js +98 -48
- package/src/bitwrench-color-utils.js +24 -18
- package/src/bitwrench-components-stub.js +4 -1
- package/src/bitwrench-file-ops.js +180 -0
- package/src/bitwrench-lean.js +2 -2
- package/src/bitwrench-styles.js +1287 -4029
- package/src/bitwrench-utils.js +458 -0
- package/src/bitwrench.js +2070 -1292
- package/src/bwserve/client.js +182 -0
- package/src/bwserve/index.js +352 -0
- package/src/bwserve/shell.js +103 -0
- package/src/cli/index.js +36 -15
- package/src/cli/layout-default.js +18 -18
- package/src/cli/serve.js +325 -0
- package/src/generate-css.js +73 -53
- package/src/version.js +3 -3
- package/src/bitwrench-component-base.js +0 -736
- package/src/bitwrench-components-inline.js +0 -374
- package/src/bitwrench-components.js +0 -610
- /package/bin/{bitwrench.js → bwcli.js} +0 -0
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
* Provides bw.highlight() for tokenizing JS/CSS/HTML into TACO spans,
|
|
5
5
|
* and bw.codeEditor() for a live editable code block with syntax coloring.
|
|
6
6
|
*
|
|
7
|
+
* Theme integration: The editor chrome (background, text color, font) reads
|
|
8
|
+
* from CSS custom properties --bw_code_bg, --bw_code_text, --bw_font_mono,
|
|
9
|
+
* falling back to built-in dark values when no theme is active. Syntax
|
|
10
|
+
* highlighting colors are intentionally fixed (they are a code color scheme,
|
|
11
|
+
* not brand colors). The bw_ce_light class is still supported for manual
|
|
12
|
+
* light-mode override.
|
|
13
|
+
*
|
|
7
14
|
* Can be loaded standalone (browser script tag after bitwrench.umd.js),
|
|
8
15
|
* or imported as an ES module / CJS module.
|
|
9
16
|
*
|
|
@@ -14,53 +21,58 @@
|
|
|
14
21
|
// -- CSS (injected once) ----------------------------------------------
|
|
15
22
|
var _cssInjected = false;
|
|
16
23
|
var CSS_TEXT =
|
|
17
|
-
'.
|
|
18
|
-
'.
|
|
19
|
-
'.
|
|
20
|
-
'.
|
|
21
|
-
'.
|
|
22
|
-
'.
|
|
23
|
-
'.
|
|
24
|
-
'.
|
|
25
|
-
'.
|
|
26
|
-
'.
|
|
27
|
-
'.
|
|
28
|
-
'.
|
|
29
|
-
'.
|
|
30
|
-
'.
|
|
31
|
-
'.
|
|
32
|
-
'.
|
|
33
|
-
'.
|
|
34
|
-
'.
|
|
35
|
-
'.
|
|
36
|
-
'.
|
|
37
|
-
'.
|
|
24
|
+
'.bw_ce{background:var(--bw_code_bg,#1e293b);border-radius:6px;border:1px solid rgba(255,255,255,0.08);overflow:auto}' +
|
|
25
|
+
'.bw_ce pre{margin:0;padding:0}' +
|
|
26
|
+
'.bw_ce code{font-family:var(--bw_font_mono,"SF Mono",Monaco,"Cascadia Code",Consolas,monospace);font-size:0.875rem;line-height:1.6;color:var(--bw_code_text,#e2e8f0);outline:none;white-space:pre-wrap;display:block;padding:0.75rem 1rem}' +
|
|
27
|
+
'.bw_ce code:empty::before{content:"\\200b"}' +
|
|
28
|
+
'.bw_ce .bw_ce_keyword{color:#c792ea}' +
|
|
29
|
+
'.bw_ce .bw_ce_string{color:#c3e88d}' +
|
|
30
|
+
'.bw_ce .bw_ce_comment{color:#546e7a;font-style:italic}' +
|
|
31
|
+
'.bw_ce .bw_ce_number{color:#f78c6c}' +
|
|
32
|
+
'.bw_ce .bw_ce_operator{color:#89ddff}' +
|
|
33
|
+
'.bw_ce .bw_ce_punctuation{color:#89ddff}' +
|
|
34
|
+
'.bw_ce .bw_ce_property{color:#82aaff}' +
|
|
35
|
+
'.bw_ce .bw_ce_function{color:#82aaff}' +
|
|
36
|
+
'.bw_ce .bw_ce_tag{color:#f07178}' +
|
|
37
|
+
'.bw_ce .bw_ce_attr_name{color:#ffcb6b}' +
|
|
38
|
+
'.bw_ce .bw_ce_attr_value{color:#c3e88d}' +
|
|
39
|
+
'.bw_ce .bw_ce_selector{color:#c792ea}' +
|
|
40
|
+
'.bw_ce .bw_ce_css_prop{color:#82aaff}' +
|
|
41
|
+
'.bw_ce .bw_ce_css_value{color:#f78c6c}' +
|
|
42
|
+
'.bw_ce .bw_ce_at_rule{color:#c792ea;font-style:italic}' +
|
|
43
|
+
'.bw_ce .bw_ce_color{color:#f78c6c}' +
|
|
44
|
+
'.bw_ce .bw_ce_template_interp{color:#89ddff}' +
|
|
38
45
|
// Light theme
|
|
39
|
-
'.
|
|
40
|
-
'.
|
|
41
|
-
'.
|
|
42
|
-
'.
|
|
43
|
-
'.
|
|
44
|
-
'.
|
|
45
|
-
'.
|
|
46
|
-
'.
|
|
47
|
-
'.
|
|
48
|
-
'.
|
|
49
|
-
'.
|
|
50
|
-
'.
|
|
51
|
-
'.
|
|
52
|
-
'.
|
|
53
|
-
'.
|
|
54
|
-
'.
|
|
55
|
-
'.
|
|
56
|
-
'.
|
|
57
|
-
'.
|
|
46
|
+
'.bw_ce_light.bw_ce{background:#fafafa;border-color:#d8d8d8}' +
|
|
47
|
+
'.bw_ce_light.bw_ce code{color:#1a1a1a}' +
|
|
48
|
+
'.bw_ce_light.bw_ce .bw_ce_keyword{color:#7c3aed}' +
|
|
49
|
+
'.bw_ce_light.bw_ce .bw_ce_string{color:#16a34a}' +
|
|
50
|
+
'.bw_ce_light.bw_ce .bw_ce_comment{color:#9ca3af;font-style:italic}' +
|
|
51
|
+
'.bw_ce_light.bw_ce .bw_ce_number{color:#ea580c}' +
|
|
52
|
+
'.bw_ce_light.bw_ce .bw_ce_operator{color:#0891b2}' +
|
|
53
|
+
'.bw_ce_light.bw_ce .bw_ce_punctuation{color:#6b7280}' +
|
|
54
|
+
'.bw_ce_light.bw_ce .bw_ce_property{color:#2563eb}' +
|
|
55
|
+
'.bw_ce_light.bw_ce .bw_ce_function{color:#2563eb}' +
|
|
56
|
+
'.bw_ce_light.bw_ce .bw_ce_tag{color:#dc2626}' +
|
|
57
|
+
'.bw_ce_light.bw_ce .bw_ce_attr_name{color:#d97706}' +
|
|
58
|
+
'.bw_ce_light.bw_ce .bw_ce_attr_value{color:#16a34a}' +
|
|
59
|
+
'.bw_ce_light.bw_ce .bw_ce_selector{color:#7c3aed}' +
|
|
60
|
+
'.bw_ce_light.bw_ce .bw_ce_css_prop{color:#2563eb}' +
|
|
61
|
+
'.bw_ce_light.bw_ce .bw_ce_css_value{color:#ea580c}' +
|
|
62
|
+
'.bw_ce_light.bw_ce .bw_ce_at_rule{color:#7c3aed}' +
|
|
63
|
+
'.bw_ce_light.bw_ce .bw_ce_color{color:#ea580c}' +
|
|
64
|
+
'.bw_ce_light.bw_ce .bw_ce_template_interp{color:#0891b2}' +
|
|
65
|
+
// Line number gutter (opt-in via lineNumbers option)
|
|
66
|
+
'.bw_ce_wrap{display:flex;flex-direction:row}' +
|
|
67
|
+
'.bw_ce_gutter{flex:0 0 auto;padding:0.75rem 0;text-align:right;user-select:none;-webkit-user-select:none;color:#546e7a;font-family:var(--bw_font_mono,"SF Mono",Monaco,"Cascadia Code",Consolas,monospace);font-size:0.875rem;line-height:1.6;border-right:1px solid rgba(255,255,255,0.08);overflow:hidden}' +
|
|
68
|
+
'.bw_ce_gutter span{display:block;padding:0 0.5rem 0 0.75rem}' +
|
|
69
|
+
'.bw_ce_light .bw_ce_gutter{color:#9ca3af;border-right-color:#d8d8d8}';
|
|
58
70
|
|
|
59
71
|
function ensureCSS(bw) {
|
|
60
72
|
if (_cssInjected) return;
|
|
61
73
|
_cssInjected = true;
|
|
62
74
|
if (bw && bw.injectCSS) {
|
|
63
|
-
bw.injectCSS(CSS_TEXT, { id: '
|
|
75
|
+
bw.injectCSS(CSS_TEXT, { id: 'bw_code_edit_styles' });
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
78
|
|
|
@@ -483,7 +495,7 @@ function tokensToTACO(tokArr) {
|
|
|
483
495
|
if (tok.type === 'plain') {
|
|
484
496
|
result.push(tok.text);
|
|
485
497
|
} else {
|
|
486
|
-
result.push({ t: 'span', a: { class: '
|
|
498
|
+
result.push({ t: 'span', a: { class: 'bw_ce_' + tok.type }, c: tok.text });
|
|
487
499
|
}
|
|
488
500
|
}
|
|
489
501
|
return result;
|
|
@@ -537,46 +549,83 @@ function codeEditor(opts) {
|
|
|
537
549
|
var lang = opts.lang || 'js';
|
|
538
550
|
var height = opts.height || '180px';
|
|
539
551
|
var readOnly = !!opts.readOnly;
|
|
540
|
-
var
|
|
552
|
+
var showLineNumbers = !!opts.lineNumbers;
|
|
553
|
+
var className = 'bw_ce' + (opts.className ? ' ' + opts.className : '');
|
|
541
554
|
|
|
542
555
|
var highlighted = highlight(code, lang);
|
|
543
556
|
|
|
544
557
|
var codeAttrs = {
|
|
545
558
|
spellcheck: 'false',
|
|
546
|
-
class: '
|
|
559
|
+
class: 'bw_ce_code'
|
|
547
560
|
};
|
|
548
561
|
if (!readOnly) {
|
|
549
562
|
codeAttrs.contenteditable = 'true';
|
|
550
563
|
}
|
|
551
564
|
|
|
565
|
+
// Build line number gutter TACO if requested
|
|
566
|
+
var gutterTaco = null;
|
|
567
|
+
if (showLineNumbers) {
|
|
568
|
+
var lineCount = (code.match(/\n/g) || []).length + 1;
|
|
569
|
+
var gutterLines = [];
|
|
570
|
+
for (var li = 1; li <= lineCount; li++) {
|
|
571
|
+
gutterLines.push({ t: 'span', c: String(li) });
|
|
572
|
+
}
|
|
573
|
+
gutterTaco = { t: 'div', a: { class: 'bw_ce_gutter' }, c: gutterLines };
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
var preBlock = { t: 'pre', a: { style: 'flex:1;min-width:0;margin:0' }, c: { t: 'code', a: codeAttrs, c: highlighted } };
|
|
577
|
+
var innerContent = showLineNumbers
|
|
578
|
+
? { t: 'div', a: { class: 'bw_ce_wrap' }, c: [gutterTaco, preBlock] }
|
|
579
|
+
: preBlock;
|
|
580
|
+
|
|
552
581
|
return {
|
|
553
582
|
t: 'div',
|
|
554
583
|
a: { class: className, style: 'max-height:' + height + ';overflow:auto' },
|
|
555
|
-
c: [
|
|
556
|
-
{ t: 'pre', c: { t: 'code', a: codeAttrs, c: highlighted } }
|
|
557
|
-
],
|
|
584
|
+
c: [innerContent],
|
|
558
585
|
o: {
|
|
559
586
|
mounted: function(el) {
|
|
560
|
-
var codeEl = el.querySelector('.
|
|
587
|
+
var codeEl = el.querySelector('.bw_ce_code');
|
|
561
588
|
if (!codeEl) return;
|
|
562
589
|
|
|
563
590
|
var currentCode = code;
|
|
564
591
|
var debounceTimer = null;
|
|
592
|
+
var gutterEl = showLineNumbers ? el.querySelector('.bw_ce_gutter') : null;
|
|
565
593
|
|
|
566
594
|
// Resolve bw from global or import context
|
|
567
595
|
var bw = (typeof window !== 'undefined' && window.bw) || {};
|
|
568
596
|
|
|
569
597
|
function getValue() { return codeEl.textContent || ''; }
|
|
570
598
|
|
|
599
|
+
function updateGutter(text) {
|
|
600
|
+
if (!gutterEl) return;
|
|
601
|
+
var count = (text.match(/\n/g) || []).length + 1;
|
|
602
|
+
var html = '';
|
|
603
|
+
for (var i = 1; i <= count; i++) html += '<span>' + i + '</span>';
|
|
604
|
+
gutterEl.innerHTML = html;
|
|
605
|
+
}
|
|
606
|
+
|
|
571
607
|
function setValue(newCode) {
|
|
572
608
|
currentCode = newCode;
|
|
573
609
|
var tacos = highlight(newCode, lang);
|
|
574
610
|
if (bw.html) codeEl.innerHTML = bw.html({ t: 'span', c: tacos });
|
|
611
|
+
updateGutter(newCode);
|
|
575
612
|
}
|
|
576
613
|
|
|
577
614
|
// Expose API on the element
|
|
578
615
|
el._bwCodeEdit = { getValue: getValue, setValue: setValue };
|
|
579
616
|
|
|
617
|
+
// Scroll sync: keep gutter aligned with code
|
|
618
|
+
if (gutterEl) {
|
|
619
|
+
var scrollParent = codeEl.closest('.bw_ce') || el;
|
|
620
|
+
scrollParent.addEventListener('scroll', function() {
|
|
621
|
+
gutterEl.style.transform = 'translateY(' + (-scrollParent.scrollTop) + 'px)';
|
|
622
|
+
});
|
|
623
|
+
// If the outer .bw_ce has overflow, sync from there
|
|
624
|
+
el.addEventListener('scroll', function() {
|
|
625
|
+
gutterEl.style.transform = 'translateY(' + (-el.scrollTop) + 'px)';
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
580
629
|
if (readOnly) return;
|
|
581
630
|
|
|
582
631
|
function rehighlight() {
|
|
@@ -587,6 +636,7 @@ function codeEditor(opts) {
|
|
|
587
636
|
var tacos = highlight(newCode, lang);
|
|
588
637
|
if (bw.html) codeEl.innerHTML = bw.html({ t: 'span', c: tacos });
|
|
589
638
|
setCaretOffset(codeEl, offset);
|
|
639
|
+
updateGutter(newCode);
|
|
590
640
|
if (opts.onChange) opts.onChange(newCode);
|
|
591
641
|
}
|
|
592
642
|
|
|
@@ -6,9 +6,13 @@
|
|
|
6
6
|
* bw.colorParse, bw.colorRgbToHsl, etc.
|
|
7
7
|
*
|
|
8
8
|
* @module bitwrench-color-utils
|
|
9
|
-
* @license BSD-2-Clause
|
|
9
|
+
* @license BSD-2-Clause
|
|
10
|
+
* @copy Manu Chatterjee @deftio
|
|
10
11
|
*/
|
|
11
12
|
|
|
13
|
+
function _xs (x) {
|
|
14
|
+
return ('0' + x.toString(16)).slice(-2)
|
|
15
|
+
}
|
|
12
16
|
/**
|
|
13
17
|
* Clamp a value between min and max.
|
|
14
18
|
* @param {number} val
|
|
@@ -200,10 +204,7 @@ export function hexToHsl(hex) {
|
|
|
200
204
|
*/
|
|
201
205
|
export function hslToHex(hsl) {
|
|
202
206
|
var rgb = colorHslToRgb(hsl[0], hsl[1], hsl[2], 255, true);
|
|
203
|
-
return '#' +
|
|
204
|
-
('0' + rgb[0].toString(16)).slice(-2) +
|
|
205
|
-
('0' + rgb[1].toString(16)).slice(-2) +
|
|
206
|
-
('0' + rgb[2].toString(16)).slice(-2);
|
|
207
|
+
return '#' + _xs(rgb[0])+_xs(rgb[1])+_xs(rgb[2]);
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
/**
|
|
@@ -232,10 +233,7 @@ export function mixColor(hex1, hex2, ratio) {
|
|
|
232
233
|
var r = Math.round(c1[0] + (c2[0] - c1[0]) * ratio);
|
|
233
234
|
var g = Math.round(c1[1] + (c2[1] - c1[1]) * ratio);
|
|
234
235
|
var b = Math.round(c1[2] + (c2[2] - c1[2]) * ratio);
|
|
235
|
-
return '#' +
|
|
236
|
-
('0' + r.toString(16)).slice(-2) +
|
|
237
|
-
('0' + g.toString(16)).slice(-2) +
|
|
238
|
-
('0' + b.toString(16)).slice(-2);
|
|
236
|
+
return '#' + _xs(r) + _xs(g) + _xs(b);
|
|
239
237
|
}
|
|
240
238
|
|
|
241
239
|
/**
|
|
@@ -415,16 +413,24 @@ export function derivePalette(config) {
|
|
|
415
413
|
var lightBase = config.light || hslToHex([h, 8, 97]);
|
|
416
414
|
var darkBase = config.dark || hslToHex([h, 10, 13]);
|
|
417
415
|
|
|
416
|
+
// Background & surface tokens — tinted with primary hue for theme personality.
|
|
417
|
+
// Very subtle: bg at L=98/S=6, surface at L=96/S=8.
|
|
418
|
+
// User can override with config.background / config.surface.
|
|
419
|
+
var bgBase = config.background || hslToHex([h, 6, 98]);
|
|
420
|
+
var surfBase = config.surface || hslToHex([h, 8, 96]);
|
|
421
|
+
|
|
418
422
|
var palette = {
|
|
419
|
-
primary:
|
|
420
|
-
secondary:
|
|
421
|
-
tertiary:
|
|
422
|
-
success:
|
|
423
|
-
danger:
|
|
424
|
-
warning:
|
|
425
|
-
info:
|
|
426
|
-
light:
|
|
427
|
-
dark:
|
|
423
|
+
primary: deriveShades(config.primary),
|
|
424
|
+
secondary: deriveShades(config.secondary),
|
|
425
|
+
tertiary: deriveShades(config.tertiary),
|
|
426
|
+
success: deriveShades(successBase),
|
|
427
|
+
danger: deriveShades(dangerBase),
|
|
428
|
+
warning: deriveShades(warningBase),
|
|
429
|
+
info: deriveShades(infoBase),
|
|
430
|
+
light: deriveShades(lightBase),
|
|
431
|
+
dark: deriveShades(darkBase),
|
|
432
|
+
background: bgBase,
|
|
433
|
+
surface: surfBase
|
|
428
434
|
};
|
|
429
435
|
|
|
430
436
|
return palette;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Empty stub for bitwrench-
|
|
2
|
+
* Empty stub for bitwrench-bccl.js.
|
|
3
3
|
* Used by the lean build to exclude all BCCL component code.
|
|
4
4
|
*/
|
|
5
5
|
export const componentHandles = {};
|
|
6
|
+
export function variantClass() { return ''; }
|
|
7
|
+
export var BCCL = {};
|
|
8
|
+
export function make() { throw new Error('bw.make() requires the full bitwrench build (not lean)'); }
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitwrench v2 File I/O Functions
|
|
3
|
+
*
|
|
4
|
+
* Save/load files in both Node.js and browser environments.
|
|
5
|
+
* Node uses fs module, browser uses Blob/XHR/FileReader.
|
|
6
|
+
*
|
|
7
|
+
* Called via bindFileOps(bw) which attaches all functions to the bw namespace.
|
|
8
|
+
* This preserves the same public API (bw.saveClientFile, bw.loadClientFile, etc.)
|
|
9
|
+
* while keeping the implementation in a separate module.
|
|
10
|
+
*
|
|
11
|
+
* @module bitwrench-file-ops
|
|
12
|
+
* @license BSD-2-Clause
|
|
13
|
+
* @author M A Chatterjee <deftio [at] deftio [dot] com>
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Attach all file I/O functions to the bitwrench namespace.
|
|
18
|
+
*
|
|
19
|
+
* @param {Object} bw - Bitwrench namespace object
|
|
20
|
+
*/
|
|
21
|
+
export function bindFileOps(bw) {
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Save data to a file. Works in both Node.js (fs.writeFile) and browser (download link).
|
|
25
|
+
*
|
|
26
|
+
* @param {string} fname - Filename to save as
|
|
27
|
+
* @param {*} data - Data to save (string or buffer)
|
|
28
|
+
* @category File I/O
|
|
29
|
+
*/
|
|
30
|
+
bw.saveClientFile = function(fname, data) {
|
|
31
|
+
if (bw.isNodeJS()) {
|
|
32
|
+
bw._getFs().then(function(fs) {
|
|
33
|
+
if (!fs) { console.error('bw.saveClientFile: fs module not available'); return; }
|
|
34
|
+
fs.writeFile(fname, data, function(err) {
|
|
35
|
+
if (err) {
|
|
36
|
+
console.error("Error saving file:", err);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
} else {
|
|
41
|
+
var blob = new Blob([data], { type: "application/octet-stream" });
|
|
42
|
+
var url = window.URL.createObjectURL(blob);
|
|
43
|
+
var a = bw.createDOM({
|
|
44
|
+
t: 'a',
|
|
45
|
+
a: {
|
|
46
|
+
href: url,
|
|
47
|
+
download: fname,
|
|
48
|
+
style: 'display: none'
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
document.body.appendChild(a);
|
|
52
|
+
a.click();
|
|
53
|
+
window.URL.revokeObjectURL(url);
|
|
54
|
+
document.body.removeChild(a);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Save data as a JSON file with pretty formatting.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} fname - Filename to save as
|
|
62
|
+
* @param {*} data - Data to serialize as JSON
|
|
63
|
+
* @category File I/O
|
|
64
|
+
*/
|
|
65
|
+
bw.saveClientJSON = function(fname, data) {
|
|
66
|
+
bw.saveClientFile(fname, JSON.stringify(data, null, 2));
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Load a file by path (Node.js) or URL (browser via XHR).
|
|
71
|
+
*
|
|
72
|
+
* @param {string} fname - File path (Node) or URL (browser)
|
|
73
|
+
* @param {Function} callback - Called with (data, error). data is null on error.
|
|
74
|
+
* @param {Object} [options] - Options
|
|
75
|
+
* @param {string} [options.parser="raw"] - "raw" for string, "JSON" to auto-parse
|
|
76
|
+
* @returns {string} "BW_OK"
|
|
77
|
+
* @category File I/O
|
|
78
|
+
*/
|
|
79
|
+
bw.loadClientFile = function(fname, callback, options) {
|
|
80
|
+
var opts = { parser: 'raw' };
|
|
81
|
+
if (options && options.parser) { opts.parser = options.parser; }
|
|
82
|
+
var parse = (opts.parser === 'JSON') ? JSON.parse : function(s) { return s; };
|
|
83
|
+
|
|
84
|
+
if (bw.isNodeJS()) {
|
|
85
|
+
bw._getFs().then(function(fs) {
|
|
86
|
+
if (!fs) { callback(null, new Error('fs module not available')); return; }
|
|
87
|
+
fs.readFile(fname, 'utf8', function(err, data) {
|
|
88
|
+
if (err) { callback(null, err); }
|
|
89
|
+
else {
|
|
90
|
+
try { callback(parse(data), null); }
|
|
91
|
+
catch (e) { callback(null, e); }
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
} else {
|
|
96
|
+
var x = new XMLHttpRequest();
|
|
97
|
+
x.open('GET', fname, true);
|
|
98
|
+
x.onreadystatechange = function() {
|
|
99
|
+
if (x.readyState === 4) {
|
|
100
|
+
if (x.status >= 200 && x.status < 300) {
|
|
101
|
+
try { callback(parse(x.responseText), null); }
|
|
102
|
+
catch (e) { callback(null, e); }
|
|
103
|
+
} else {
|
|
104
|
+
callback(null, new Error('HTTP ' + x.status + ': ' + fname));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
x.send(null);
|
|
109
|
+
}
|
|
110
|
+
return 'BW_OK';
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Load a JSON file by path (Node.js) or URL (browser).
|
|
115
|
+
*
|
|
116
|
+
* @param {string} fname - File path (Node) or URL (browser)
|
|
117
|
+
* @param {Function} callback - Called with (parsedData, error)
|
|
118
|
+
* @returns {string} "BW_OK"
|
|
119
|
+
* @category File I/O
|
|
120
|
+
*/
|
|
121
|
+
bw.loadClientJSON = function(fname, callback) {
|
|
122
|
+
return bw.loadClientFile(fname, callback, { parser: 'JSON' });
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Prompt user to pick a local file via file dialog (browser only).
|
|
127
|
+
*
|
|
128
|
+
* @param {Function} callback - Called with (data, filename, error)
|
|
129
|
+
* @param {Object} [options] - Options
|
|
130
|
+
* @param {string} [options.accept] - File type filter (e.g. ".json,.txt")
|
|
131
|
+
* @param {string} [options.parser="raw"] - "raw" for string, "JSON" to auto-parse
|
|
132
|
+
* @category File I/O
|
|
133
|
+
*/
|
|
134
|
+
bw.loadLocalFile = function(callback, options) {
|
|
135
|
+
var opts = { parser: 'raw', accept: '' };
|
|
136
|
+
if (options) {
|
|
137
|
+
if (options.parser) { opts.parser = options.parser; }
|
|
138
|
+
if (options.accept) { opts.accept = options.accept; }
|
|
139
|
+
}
|
|
140
|
+
var parse = (opts.parser === 'JSON') ? JSON.parse : function(s) { return s; };
|
|
141
|
+
|
|
142
|
+
if (bw.isNodeJS()) {
|
|
143
|
+
callback(null, '', new Error('bw.loadLocalFile is browser-only. Use bw.loadClientFile() in Node.'));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
var input = bw.createDOM({
|
|
148
|
+
t: 'input',
|
|
149
|
+
a: {
|
|
150
|
+
type: 'file',
|
|
151
|
+
accept: opts.accept,
|
|
152
|
+
style: 'display: none'
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
input.addEventListener('change', function() {
|
|
156
|
+
var file = input.files[0];
|
|
157
|
+
if (!file) { callback(null, '', new Error('No file selected')); return; }
|
|
158
|
+
var reader = new FileReader();
|
|
159
|
+
reader.onload = function(e) {
|
|
160
|
+
try { callback(parse(e.target.result), file.name, null); }
|
|
161
|
+
catch (err) { callback(null, file.name, err); }
|
|
162
|
+
};
|
|
163
|
+
reader.onerror = function() { callback(null, file.name, reader.error); };
|
|
164
|
+
reader.readAsText(file);
|
|
165
|
+
input.remove();
|
|
166
|
+
});
|
|
167
|
+
document.body.appendChild(input);
|
|
168
|
+
input.click();
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Prompt user to pick a local JSON file via file dialog (browser only).
|
|
173
|
+
*
|
|
174
|
+
* @param {Function} callback - Called with (parsedData, filename, error)
|
|
175
|
+
* @category File I/O
|
|
176
|
+
*/
|
|
177
|
+
bw.loadLocalJSON = function(callback) {
|
|
178
|
+
bw.loadLocalFile(callback, { parser: 'JSON', accept: '.json' });
|
|
179
|
+
};
|
|
180
|
+
}
|
package/src/bitwrench-lean.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* bitwrench-lean.js — Entry point for the lean build.
|
|
3
3
|
*
|
|
4
4
|
* This is identical to bitwrench.js but the Rollup config redirects
|
|
5
|
-
* the bitwrench-
|
|
5
|
+
* the bitwrench-bccl.js import to an empty stub, so no
|
|
6
6
|
* BCCL component code is included in the output.
|
|
7
7
|
*
|
|
8
8
|
* Includes: HTML/DOM generation, CSS generation, color utilities,
|
|
9
9
|
* state management, pub/sub, file I/O, random/lorem,
|
|
10
10
|
* cookies, URL params, logging, makeTable, makeDataTable.
|
|
11
|
-
* Excludes: All make* component helpers from bitwrench-
|
|
11
|
+
* Excludes: All make* component helpers from bitwrench-bccl.js
|
|
12
12
|
* (makeButton, makeCard, makeAlert, makeTabs, etc.)
|
|
13
13
|
*/
|
|
14
14
|
export { default } from './bitwrench.js';
|