anentrypoint-design 0.0.251 → 0.0.253
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/dist/247420.css +58 -0
- package/dist/247420.js +15 -14
- package/package.json +1 -1
- package/src/components/editor-primitives.js +113 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.253",
|
|
4
4
|
"description": "247420 design system SDK — webjsx + modified ripple-ui, single-file ESM bundle for reproducible use of the AnEntrypoint design.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/247420.js",
|
|
@@ -615,18 +615,123 @@ export function Pager({ page = 1, pageCount = 1, onPage, total, itemLabel = 'ite
|
|
|
615
615
|
}
|
|
616
616
|
|
|
617
617
|
// ---------------------------------------------------------------------------
|
|
618
|
-
// JsonViewer —
|
|
619
|
-
//
|
|
620
|
-
//
|
|
621
|
-
//
|
|
618
|
+
// JsonViewer — monospace data preview (max-height + scroll), generalizing
|
|
619
|
+
// gmsniff's gm-json. Accepts a pre-stringified string OR any value
|
|
620
|
+
// (objects/arrays get JSON.stringify(v, null, 2); null/undefined render the
|
|
621
|
+
// empty-state text rather than the literal string "undefined"/"null").
|
|
622
|
+
//
|
|
623
|
+
// mode selects rendering; 'plain' is the historical contract (children[0] is
|
|
624
|
+
// the raw text string, verbatim for string input) and stays the default so
|
|
625
|
+
// every existing consumer is untouched:
|
|
626
|
+
// 'plain' — flat <pre>, raw text.
|
|
627
|
+
// 'highlight' — flat <pre>, text tokenized into ds-ep-json-* spans
|
|
628
|
+
// (key/string/number/boolean/null). A string that does not
|
|
629
|
+
// parse as JSON falls back to plain text — arbitrary prose is
|
|
630
|
+
// never falsely tokenized.
|
|
631
|
+
// 'tree' — collapsible <details> tree per nested object/array, open
|
|
632
|
+
// above treeDepth (default 2), each summary carrying a
|
|
633
|
+
// child-count tag. Scalars/unparseable input fall back to
|
|
634
|
+
// 'highlight'/plain respectively.
|
|
635
|
+
// copyable=true wraps the viewer with a copy-to-clipboard button (transient
|
|
636
|
+
// copied/failed feedback, no dependencies).
|
|
622
637
|
// ---------------------------------------------------------------------------
|
|
623
|
-
|
|
624
|
-
|
|
638
|
+
const JSON_NUM_CHARS = '0123456789eE+.-';
|
|
639
|
+
|
|
640
|
+
// Linear single-pass scan — no regex, no backtracking, safe on truncated
|
|
641
|
+
// input (an unterminated string just consumes to end-of-text).
|
|
642
|
+
function tokenizeJson(text) {
|
|
643
|
+
const toks = [];
|
|
644
|
+
let i = 0, plain = '';
|
|
645
|
+
const flush = () => { if (plain) { toks.push(['', plain]); plain = ''; } };
|
|
646
|
+
while (i < text.length) {
|
|
647
|
+
const c = text[i];
|
|
648
|
+
if (c === '"') {
|
|
649
|
+
const start = i;
|
|
650
|
+
i++;
|
|
651
|
+
while (i < text.length && text[i] !== '"') { if (text[i] === '\\') i++; i++; }
|
|
652
|
+
i = Math.min(i + 1, text.length);
|
|
653
|
+
let j = i;
|
|
654
|
+
while (j < text.length && (text[j] === ' ' || text[j] === '\t' || text[j] === '\n' || text[j] === '\r')) j++;
|
|
655
|
+
flush();
|
|
656
|
+
toks.push([text[j] === ':' ? 'k' : 's', text.slice(start, i)]);
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
if (c === '-' || (c >= '0' && c <= '9')) {
|
|
660
|
+
const start = i;
|
|
661
|
+
i++;
|
|
662
|
+
while (i < text.length && JSON_NUM_CHARS.includes(text[i])) i++;
|
|
663
|
+
flush();
|
|
664
|
+
toks.push(['n', text.slice(start, i)]);
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
if (text.startsWith('true', i)) { flush(); toks.push(['b', 'true']); i += 4; continue; }
|
|
668
|
+
if (text.startsWith('false', i)) { flush(); toks.push(['b', 'false']); i += 5; continue; }
|
|
669
|
+
if (text.startsWith('null', i)) { flush(); toks.push(['z', 'null']); i += 4; continue; }
|
|
670
|
+
plain += c; i++;
|
|
671
|
+
}
|
|
672
|
+
flush();
|
|
673
|
+
return toks;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function highlightJsonSpans(text) {
|
|
677
|
+
return tokenizeJson(text).map(([t, s]) => t ? h('span', { class: 'ds-ep-json-' + t }, s) : s);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function jsonTreeNode(key, val, depth, treeDepth) {
|
|
681
|
+
const keyParts = key != null ? [h('span', { class: 'ds-ep-json-k' }, JSON.stringify(key)), ': '] : [];
|
|
682
|
+
if (val !== null && typeof val === 'object') {
|
|
683
|
+
const isArr = Array.isArray(val);
|
|
684
|
+
const entries = isArr ? val.map((v) => [null, v]) : Object.entries(val);
|
|
685
|
+
if (!entries.length) return h('div', { class: 'ds-ep-json-leaf' }, ...keyParts, isArr ? '[]' : '{}');
|
|
686
|
+
const tag = isArr ? '[' + entries.length + ']' : '{' + entries.length + '}';
|
|
687
|
+
return h('details', { class: 'ds-ep-json-node', open: depth < treeDepth ? true : null },
|
|
688
|
+
h('summary', { class: 'ds-ep-json-sum' }, ...keyParts, h('span', { class: 'ds-ep-json-tag' }, tag)),
|
|
689
|
+
h('div', { class: 'ds-ep-json-kids' }, ...entries.map(([k, v]) => jsonTreeNode(k, v, depth + 1, treeDepth))));
|
|
690
|
+
}
|
|
691
|
+
const t = typeof val === 'string' ? 's' : typeof val === 'number' ? 'n' : typeof val === 'boolean' ? 'b' : 'z';
|
|
692
|
+
return h('div', { class: 'ds-ep-json-leaf' }, ...keyParts, h('span', { class: 'ds-ep-json-' + t }, JSON.stringify(val) ?? String(val)));
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function jsonCopyButton(text) {
|
|
696
|
+
return h('button', {
|
|
697
|
+
type: 'button', class: 'ds-ep-json-copy', title: 'copy JSON', 'aria-label': 'copy JSON',
|
|
698
|
+
onclick: (e) => {
|
|
699
|
+
const btn = e.currentTarget;
|
|
700
|
+
const show = (label, ok) => {
|
|
701
|
+
btn.textContent = label;
|
|
702
|
+
btn.classList.toggle('copied', ok);
|
|
703
|
+
setTimeout(() => { btn.textContent = 'copy'; btn.classList.remove('copied'); }, 1200);
|
|
704
|
+
};
|
|
705
|
+
try {
|
|
706
|
+
navigator.clipboard.writeText(text).then(() => show('copied', true), () => show('failed', false));
|
|
707
|
+
} catch {
|
|
708
|
+
show('failed', false);
|
|
709
|
+
}
|
|
710
|
+
},
|
|
711
|
+
}, 'copy');
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
export function JsonViewer({ value, emptyText = 'no data', maxHeight, mode = 'plain', copyable = false, treeDepth = 2 } = {}) {
|
|
715
|
+
let text, parsed;
|
|
716
|
+
let knownJson = false;
|
|
625
717
|
if (value == null) text = null;
|
|
626
718
|
else if (typeof value === 'string') text = value;
|
|
627
|
-
else { try { text = JSON.stringify(value, null, 2); } catch { text = String(value); } }
|
|
719
|
+
else { try { text = JSON.stringify(value, null, 2); knownJson = text != null; parsed = value; } catch { text = String(value); } }
|
|
628
720
|
if (!text) return h('div', { class: 'ds-ep-json ds-ep-json-empty' }, emptyText);
|
|
629
|
-
|
|
721
|
+
const style = maxHeight ? ('max-height:' + maxHeight) : null;
|
|
722
|
+
if (!knownJson && (mode === 'highlight' || mode === 'tree')) {
|
|
723
|
+
try { parsed = JSON.parse(text); knownJson = true; } catch { /* not JSON — render plain */ }
|
|
724
|
+
}
|
|
725
|
+
let body;
|
|
726
|
+
if (mode === 'tree' && knownJson && parsed !== null && typeof parsed === 'object') {
|
|
727
|
+
body = h('div', { class: 'ds-ep-json ds-ep-json-tree', style }, jsonTreeNode(null, parsed, 0, treeDepth));
|
|
728
|
+
} else if ((mode === 'highlight' || mode === 'tree') && knownJson) {
|
|
729
|
+
body = h('pre', { class: 'ds-ep-json ds-ep-json-hl', style }, ...highlightJsonSpans(text));
|
|
730
|
+
} else {
|
|
731
|
+
body = h('pre', { class: 'ds-ep-json', style }, text);
|
|
732
|
+
}
|
|
733
|
+
if (!copyable) return body;
|
|
734
|
+
return h('div', { class: 'ds-ep-json-wrap' }, jsonCopyButton(text), body);
|
|
630
735
|
}
|
|
631
736
|
|
|
632
737
|
export function IconButtonGroup({ items = [], value, onChange, dense = false } = {}) {
|