Spaces:
Running
Running
🐳 10/02 - 13:30 - ** NONONO its turned ugly now--make it go back to the visualization it was before!! the very nice subtle highlightsa and NO textboxes until ONE is clicked on
Browse files- index.html +8 -275
index.html
CHANGED
|
@@ -20,58 +20,7 @@
|
|
| 20 |
}
|
| 21 |
}
|
| 22 |
</script>
|
| 23 |
-
|
| 24 |
-
.json-editor {
|
| 25 |
-
min-height: 500px;
|
| 26 |
-
max-height: 70vh;
|
| 27 |
-
overflow-y: auto;
|
| 28 |
-
background-color: #0f172a;
|
| 29 |
-
background-image:
|
| 30 |
-
linear-gradient(rgba(30, 41, 59, 0.5) 1px, transparent 1px),
|
| 31 |
-
linear-gradient(90deg, rgba(30, 41, 59, 0.5) 1px, transparent 1px);
|
| 32 |
-
background-size: 20px 20px;
|
| 33 |
-
padding: 20px;
|
| 34 |
-
border-radius: 8px;
|
| 35 |
-
position: relative;
|
| 36 |
-
}
|
| 37 |
-
.json-item {
|
| 38 |
-
transition: all 0.2s ease;
|
| 39 |
-
margin: 4px 0;
|
| 40 |
-
border-radius: 6px;
|
| 41 |
-
background-color: rgba(30, 41, 59, 0.6);
|
| 42 |
-
position: relative;
|
| 43 |
-
display: flex;
|
| 44 |
-
align-items: center;
|
| 45 |
-
}
|
| 46 |
-
.json-item-content {
|
| 47 |
-
padding: 6px 12px;
|
| 48 |
-
margin-left: 0;
|
| 49 |
-
display: inline-flex;
|
| 50 |
-
align-items: center;
|
| 51 |
-
min-width: 200px;
|
| 52 |
-
gap: 8px;
|
| 53 |
-
}
|
| 54 |
-
.json-item:hover .json-item-content {
|
| 55 |
-
background-color: rgba(51, 65, 85, 0.5);
|
| 56 |
-
}
|
| 57 |
-
.json-item.active .json-item-content {
|
| 58 |
-
background-color: rgba(51, 65, 85, 0.7);
|
| 59 |
-
}
|
| 60 |
-
.json-item.editing .json-item-content {
|
| 61 |
-
background-color: rgba(100, 116, 139, 0.5);
|
| 62 |
-
}
|
| 63 |
-
.json-key {
|
| 64 |
-
font-weight: 500;
|
| 65 |
-
color: #93c5fd;
|
| 66 |
-
margin-right: 4px;
|
| 67 |
-
}
|
| 68 |
-
.json-value {
|
| 69 |
-
color: #6ee7b7;
|
| 70 |
-
}
|
| 71 |
-
.json-bracket {
|
| 72 |
-
font-weight: bold;
|
| 73 |
-
font-family: monospace;
|
| 74 |
-
}
|
| 75 |
.btn {
|
| 76 |
transition: all 0.2s ease;
|
| 77 |
}
|
|
@@ -520,77 +469,7 @@
|
|
| 520 |
renderElement(jsonEditor, jsonData, 0, 'root');
|
| 521 |
}
|
| 522 |
|
| 523 |
-
|
| 524 |
-
function renderElement(container, data, depth, key = null, parentKey = null) {
|
| 525 |
-
const layerClass = `lcars-layer-${Math.min(depth, 7)}`;
|
| 526 |
-
const wrapper = document.createElement('div');
|
| 527 |
-
wrapper.className = `json-item relative ${layerClass}`;
|
| 528 |
-
wrapper.dataset.key = key;
|
| 529 |
-
wrapper.dataset.parent = parentKey;
|
| 530 |
-
wrapper.dataset.depth = depth;
|
| 531 |
-
wrapper.dataset.layer = Math.min(depth, 7);
|
| 532 |
-
|
| 533 |
-
// Add LCARS-style layer indicator
|
| 534 |
-
const layerIndicator = document.createElement('div');
|
| 535 |
-
layerIndicator.className = 'layer-indicator';
|
| 536 |
-
layerIndicator.style.left = `${depth * 24}px`;
|
| 537 |
-
wrapper.appendChild(layerIndicator);
|
| 538 |
-
|
| 539 |
-
// Add LCARS connector line
|
| 540 |
-
const connector = document.createElement('div');
|
| 541 |
-
connector.className = 'lcars-connector';
|
| 542 |
-
connector.style.left = `${depth * 24 + 4}px`;
|
| 543 |
-
wrapper.appendChild(connector);
|
| 544 |
-
|
| 545 |
-
// Create element content
|
| 546 |
-
const content = document.createElement('div');
|
| 547 |
-
content.className = 'json-item-content flex items-start py-1';
|
| 548 |
-
content.style.marginLeft = `${depth * 24 + 12}px`;
|
| 549 |
-
|
| 550 |
-
// Key - make it editable with single click
|
| 551 |
-
if (key !== null && key !== 'root') {
|
| 552 |
-
const keyInput = document.createElement('input');
|
| 553 |
-
keyInput.type = 'text';
|
| 554 |
-
keyInput.className = 'editable-field key-input';
|
| 555 |
-
keyInput.value = key;
|
| 556 |
-
keyInput.dataset.fieldType = 'key';
|
| 557 |
-
keyInput.dataset.parentKey = parentKey;
|
| 558 |
-
keyInput.dataset.depth = depth;
|
| 559 |
-
content.appendChild(keyInput);
|
| 560 |
-
|
| 561 |
-
const colon = document.createElement('span');
|
| 562 |
-
colon.className = 'json-bracket';
|
| 563 |
-
colon.textContent = ':';
|
| 564 |
-
content.appendChild(colon);
|
| 565 |
-
|
| 566 |
-
// Track editable field
|
| 567 |
-
editableFields.push({
|
| 568 |
-
element: keyInput,
|
| 569 |
-
type: 'key',
|
| 570 |
-
key: key,
|
| 571 |
-
parentKey: parentKey,
|
| 572 |
-
depth: depth
|
| 573 |
-
});
|
| 574 |
-
|
| 575 |
-
// Single-click to edit
|
| 576 |
-
keyInput.addEventListener('click', (e) => {
|
| 577 |
-
e.stopPropagation();
|
| 578 |
-
activateField(keyInput);
|
| 579 |
-
});
|
| 580 |
-
|
| 581 |
-
keyInput.addEventListener('focus', () => {
|
| 582 |
-
wrapper.classList.add('active');
|
| 583 |
-
wrapper.classList.add('editing');
|
| 584 |
-
});
|
| 585 |
-
|
| 586 |
-
keyInput.addEventListener('blur', () => {
|
| 587 |
-
wrapper.classList.remove('active');
|
| 588 |
-
wrapper.classList.remove('editing');
|
| 589 |
-
updateKey(keyInput);
|
| 590 |
-
});
|
| 591 |
-
|
| 592 |
-
keyInput.addEventListener('keydown', handleKeyNavigation);
|
| 593 |
-
}
|
| 594 |
|
| 595 |
// Value or children
|
| 596 |
if (typeof data === 'object' && data !== null) {
|
|
@@ -669,102 +548,14 @@
|
|
| 669 |
closingWrapper.appendChild(closingContent);
|
| 670 |
container.appendChild(closingWrapper);
|
| 671 |
}
|
| 672 |
-
|
| 673 |
-
// Primitive value - make it editable
|
| 674 |
-
const valueInput = document.createElement('input');
|
| 675 |
-
valueInput.type = 'text';
|
| 676 |
-
valueInput.className = 'editable-field value-input';
|
| 677 |
-
valueInput.dataset.fieldType = 'value';
|
| 678 |
-
valueInput.dataset.key = key;
|
| 679 |
-
valueInput.dataset.parentKey = parentKey;
|
| 680 |
-
valueInput.dataset.depth = depth;
|
| 681 |
-
|
| 682 |
-
if (typeof data === 'string') {
|
| 683 |
-
valueInput.value = data;
|
| 684 |
-
} else if (typeof data === 'boolean') {
|
| 685 |
-
valueInput.value = data.toString();
|
| 686 |
-
} else if (data === null) {
|
| 687 |
-
valueInput.value = 'null';
|
| 688 |
-
} else {
|
| 689 |
-
valueInput.value = data.toString();
|
| 690 |
-
}
|
| 691 |
-
|
| 692 |
-
content.appendChild(valueInput);
|
| 693 |
-
wrapper.appendChild(content);
|
| 694 |
-
container.appendChild(wrapper);
|
| 695 |
-
|
| 696 |
-
// Track editable field
|
| 697 |
-
editableFields.push({
|
| 698 |
-
element: valueInput,
|
| 699 |
-
type: 'value',
|
| 700 |
-
key: key,
|
| 701 |
-
parentKey: parentKey,
|
| 702 |
-
depth: depth
|
| 703 |
-
});
|
| 704 |
-
|
| 705 |
-
// Single-click to edit
|
| 706 |
-
valueInput.addEventListener('click', (e) => {
|
| 707 |
-
e.stopPropagation();
|
| 708 |
-
activateField(valueInput);
|
| 709 |
-
});
|
| 710 |
-
|
| 711 |
-
valueInput.addEventListener('focus', () => {
|
| 712 |
-
wrapper.classList.add('active');
|
| 713 |
-
wrapper.classList.add('editing');
|
| 714 |
-
});
|
| 715 |
-
|
| 716 |
-
valueInput.addEventListener('blur', () => {
|
| 717 |
-
wrapper.classList.remove('active');
|
| 718 |
-
wrapper.classList.remove('editing');
|
| 719 |
-
updateValue(valueInput);
|
| 720 |
-
});
|
| 721 |
-
|
| 722 |
-
valueInput.addEventListener('keydown', handleKeyNavigation);
|
| 723 |
-
}
|
| 724 |
}
|
| 725 |
|
| 726 |
-
// Activate a field and set it as current
|
| 727 |
-
function activateField(input) {
|
| 728 |
-
currentFieldIndex = editableFields.findIndex(f => f.element === input);
|
| 729 |
-
if (currentFieldIndex !== -1) {
|
| 730 |
-
editableFields.forEach(f => f.element.parentElement.classList.remove('active'));
|
| 731 |
-
input.parentElement.classList.add('active');
|
| 732 |
-
}
|
| 733 |
-
}
|
| 734 |
|
| 735 |
-
// Handle keyboard navigation
|
| 736 |
-
function handleKeyNavigation(e) {
|
| 737 |
-
const input = e.target;
|
| 738 |
-
const fieldData = editableFields.find(f => f.element === input);
|
| 739 |
-
|
| 740 |
-
if (e.key === 'Tab') {
|
| 741 |
-
e.preventDefault();
|
| 742 |
-
if (e.shiftKey) {
|
| 743 |
-
navigateToField(-1);
|
| 744 |
-
} else {
|
| 745 |
-
navigateToField(1);
|
| 746 |
-
}
|
| 747 |
-
} else if (e.key === 'Enter' && e.shiftKey) {
|
| 748 |
-
e.preventDefault();
|
| 749 |
-
// Insert new field after current one
|
| 750 |
-
insertNewField(fieldData);
|
| 751 |
-
} else if (e.key === 'Enter') {
|
| 752 |
-
e.preventDefault();
|
| 753 |
-
// Navigate to next field
|
| 754 |
-
navigateToField(1);
|
| 755 |
-
}
|
| 756 |
-
}
|
| 757 |
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
if (newIndex >= 0 && newIndex < editableFields.length) {
|
| 762 |
-
const nextField = editableFields[newIndex];
|
| 763 |
-
nextField.element.focus();
|
| 764 |
-
nextField.element.select();
|
| 765 |
-
currentFieldIndex = newIndex;
|
| 766 |
-
}
|
| 767 |
-
}
|
| 768 |
|
| 769 |
// Insert a new field after the current one
|
| 770 |
function insertNewField(currentFieldData) {
|
|
@@ -841,67 +632,9 @@
|
|
| 841 |
showSavedIndicator();
|
| 842 |
}
|
| 843 |
|
| 844 |
-
// Update a key from input
|
| 845 |
-
function updateKey(input) {
|
| 846 |
-
const newKey = input.value.trim();
|
| 847 |
-
const oldKey = input.dataset.originalKey || input.value;
|
| 848 |
-
const parentKey = input.dataset.parentKey;
|
| 849 |
-
|
| 850 |
-
// Only save if value actually changed
|
| 851 |
-
if (newKey === oldKey) return;
|
| 852 |
-
|
| 853 |
-
input.dataset.originalKey = newKey;
|
| 854 |
-
|
| 855 |
-
if (parentKey === 'root') {
|
| 856 |
-
// Root level
|
| 857 |
-
const value = jsonData[oldKey];
|
| 858 |
-
delete jsonData[oldKey];
|
| 859 |
-
jsonData[newKey] = value;
|
| 860 |
-
} else {
|
| 861 |
-
// Nested level
|
| 862 |
-
const parent = findElementByKey(jsonData, parentKey);
|
| 863 |
-
if (parent && typeof parent === 'object' && !Array.isArray(parent)) {
|
| 864 |
-
const value = parent[oldKey];
|
| 865 |
-
delete parent[oldKey];
|
| 866 |
-
parent[newKey] = value;
|
| 867 |
-
}
|
| 868 |
-
}
|
| 869 |
-
|
| 870 |
-
// Update tracking
|
| 871 |
-
const fieldData = editableFields.find(f => f.element === input);
|
| 872 |
-
if (fieldData) {
|
| 873 |
-
fieldData.key = newKey;
|
| 874 |
-
}
|
| 875 |
-
|
| 876 |
-
updateOutput();
|
| 877 |
-
saveToHistory();
|
| 878 |
-
showSavedIndicator();
|
| 879 |
-
}
|
| 880 |
|
| 881 |
-
|
| 882 |
-
|
| 883 |
-
const rawValue = input.value.trim();
|
| 884 |
-
const parsedValue = parseValue(rawValue);
|
| 885 |
-
const key = input.dataset.key;
|
| 886 |
-
const parentKey = input.dataset.parentKey;
|
| 887 |
-
|
| 888 |
-
// Only save if value actually changed
|
| 889 |
-
const currentValue = getNestedValue(jsonData, parentKey, key);
|
| 890 |
-
if (JSON.stringify(currentValue) === JSON.stringify(parsedValue)) return;
|
| 891 |
-
|
| 892 |
-
if (parentKey === 'root') {
|
| 893 |
-
jsonData[key] = parsedValue;
|
| 894 |
-
} else {
|
| 895 |
-
const parent = findElementByKey(jsonData, parentKey);
|
| 896 |
-
if (parent && typeof parent === 'object') {
|
| 897 |
-
parent[key] = parsedValue;
|
| 898 |
-
}
|
| 899 |
-
}
|
| 900 |
-
|
| 901 |
-
updateOutput();
|
| 902 |
-
saveToHistory();
|
| 903 |
-
showSavedIndicator();
|
| 904 |
-
}
|
| 905 |
|
| 906 |
// Get nested value
|
| 907 |
function getNestedValue(obj, parentKey, key) {
|
|
|
|
| 20 |
}
|
| 21 |
}
|
| 22 |
</script>
|
| 23 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
.btn {
|
| 25 |
transition: all 0.2s ease;
|
| 26 |
}
|
|
|
|
| 469 |
renderElement(jsonEditor, jsonData, 0, 'root');
|
| 470 |
}
|
| 471 |
|
| 472 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 473 |
|
| 474 |
// Value or children
|
| 475 |
if (typeof data === 'object' && data !== null) {
|
|
|
|
| 548 |
closingWrapper.appendChild(closingContent);
|
| 549 |
container.appendChild(closingWrapper);
|
| 550 |
}
|
| 551 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 552 |
}
|
| 553 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 554 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 555 |
|
| 556 |
+
|
| 557 |
+
|
| 558 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 559 |
|
| 560 |
// Insert a new field after the current one
|
| 561 |
function insertNewField(currentFieldData) {
|
|
|
|
| 632 |
showSavedIndicator();
|
| 633 |
}
|
| 634 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 635 |
|
| 636 |
+
|
| 637 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 638 |
|
| 639 |
// Get nested value
|
| 640 |
function getNestedValue(obj, parentKey, key) {
|