RobinsAIWorld commited on
Commit
4e37599
·
verified ·
1 Parent(s): a3640f0

🐳 10/02 - 14:18 - 1. Can you also please add subtle but clear (ie. use the same colour, different shade of it) line numbers on the left hand side AND far right hand side, and X,Y character coordinate

Browse files
Files changed (3) hide show
  1. index.html +14 -2
  2. script.js +147 -9
  3. style.css +31 -0
index.html CHANGED
@@ -104,8 +104,15 @@
104
  <h2 class="text-lg font-semibold text-slate-200">Visual Editor</h2>
105
  <span class="text-xs text-slate-400 bg-slate-800 px-2 py-1 rounded">Single-click to edit • Tab to navigate • Shift+Enter for new field</span>
106
  </div>
107
- <div id="jsonEditor" class="json-editor border rounded-lg p-2 font-mono min-h-[600px] max-h-[700px] overflow-auto">
108
- <!-- JSON content will be rendered here -->
 
 
 
 
 
 
 
109
  </div>
110
  </div>
111
  </div>
@@ -178,6 +185,11 @@
178
  </div>
179
  </div>
180
  </div>
 
 
 
 
 
181
  </div>
182
 
183
  <script src="script.js"></script>
 
104
  <h2 class="text-lg font-semibold text-slate-200">Visual Editor</h2>
105
  <span class="text-xs text-slate-400 bg-slate-800 px-2 py-1 rounded">Single-click to edit • Tab to navigate • Shift+Enter for new field</span>
106
  </div>
107
+ <div class="relative json-editor border rounded-lg min-h-[600px] max-h-[700px] overflow-auto">
108
+ <!-- Left line numbers -->
109
+ <div id="lineNumbersLeft" class="absolute left-0 top-0 bottom-0 w-10 bg-slate-800 text-slate-500 text-xs font-mono text-right pr-2 select-none overflow-hidden z-10 pt-2"></div>
110
+ <!-- Right line numbers -->
111
+ <div id="lineNumbersRight" class="absolute right-0 top-0 bottom-0 w-10 bg-slate-800 text-slate-500 text-xs font-mono text-left pl-2 select-none overflow-hidden z-10 pt-2"></div>
112
+ <!-- JSON content -->
113
+ <div id="jsonEditor" class="pl-10 pr-10 py-2 font-mono">
114
+ <!-- JSON content will be rendered here -->
115
+ </div>
116
  </div>
117
  </div>
118
  </div>
 
185
  </div>
186
  </div>
187
  </div>
188
+
189
+ <!-- Coordinates display - fixed position -->
190
+ <div id="coordinates" class="fixed top-20 right-4 bg-slate-800 text-slate-300 px-3 py-2 rounded-lg text-xs font-mono shadow-lg z-50 opacity-80 hover:opacity-100 transition-opacity">
191
+ <span id="coordLine">Line: 0</span> | <span id="coordChar">Char: 0</span> | <span id="coordDepth">Depth: 0</span>
192
+ </div>
193
  </div>
194
 
195
  <script src="script.js"></script>
script.js CHANGED
@@ -28,6 +28,11 @@ document.addEventListener('DOMContentLoaded', function() {
28
  const downloadBtn = document.getElementById('downloadBtn');
29
  const applyCodeBtn = document.getElementById('applyCodeBtn');
30
  const validationStatus = document.getElementById('validationStatus');
 
 
 
 
 
31
 
32
  // Create saved indicator
33
  const savedIndicator = document.createElement('div');
@@ -54,6 +59,10 @@ document.addEventListener('DOMContentLoaded', function() {
54
  let editableFields = [];
55
  let currentFieldIndex = -1;
56
 
 
 
 
 
57
  // Drag and drop state
58
  let draggedElement = null;
59
  let draggedData = null;
@@ -251,9 +260,34 @@ document.addEventListener('DOMContentLoaded', function() {
251
  // Render the JSON editor
252
  function renderEditor() {
253
  jsonEditor.innerHTML = '';
 
 
254
  editableFields = [];
255
  currentFieldIndex = -1;
 
 
256
  renderElement(jsonEditor, jsonData, 0, 'root');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  }
258
 
259
  // Render a single JSON element (COMPLETE VERSION)
@@ -270,6 +304,9 @@ document.addEventListener('DOMContentLoaded', function() {
270
  wrapper.dataset.depth = depth;
271
  wrapper.dataset.layer = Math.min(depth, 7);
272
  wrapper.dataset.type = Array.isArray(data) ? 'array' : 'object';
 
 
 
273
 
274
  // Make primitive items draggable (not opening/closing brackets)
275
  if (key !== 'root' && key !== 'closing') {
@@ -405,6 +442,9 @@ document.addEventListener('DOMContentLoaded', function() {
405
  wrapper.dataset.depth = depth;
406
  wrapper.dataset.layer = Math.min(depth, 7);
407
  wrapper.dataset.type = 'primitive';
 
 
 
408
 
409
  // Make draggable
410
  if (key !== 'root') {
@@ -741,6 +781,12 @@ document.addEventListener('DOMContentLoaded', function() {
741
  }
742
  }
743
 
 
 
 
 
 
 
744
  // Apply changes from code editor to visual editor
745
  function applyCodeChanges(showMsg = true) {
746
  if (!jsonOutput.value.trim()) return false;
@@ -968,17 +1014,36 @@ document.addEventListener('DOMContentLoaded', function() {
968
 
969
  // Handle the drop operation
970
  function handleDrop(draggedData, dropTarget, dropPosition) {
971
- const { key: draggedKey, parentKey: draggedParentKey } = draggedData;
972
  const dropKey = dropTarget.dataset.key;
973
  const dropParentKey = dropTarget.dataset.parent;
974
  const dropDepth = parseInt(dropTarget.dataset.depth);
975
  const dropType = dropTarget.dataset.type;
976
 
977
- // Get the value being moved
978
- let movedValue = getValue(draggedParentKey, draggedKey);
 
 
979
 
980
- // Remove from original location
981
- removeFromParent(draggedParentKey, draggedKey);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
982
 
983
  // Determine new parent and position
984
  let newParentKey = dropParentKey;
@@ -986,17 +1051,14 @@ document.addEventListener('DOMContentLoaded', function() {
986
  let insertKey = dropKey;
987
 
988
  if (dropPosition === 'above') {
989
- // Insert before the drop target
990
  insertPosition = 'before';
991
  newParentKey = dropParentKey;
992
  insertKey = dropKey;
993
  } else if (dropPosition === 'below') {
994
- // Insert after the drop target
995
  insertPosition = 'after';
996
  newParentKey = dropParentKey;
997
  insertKey = dropKey;
998
  } else if (dropPosition === 'inside') {
999
- // Insert inside the drop target (as a child)
1000
  newParentKey = dropKey;
1001
  insertPosition = 'append';
1002
  insertKey = null;
@@ -1009,8 +1071,84 @@ document.addEventListener('DOMContentLoaded', function() {
1009
  }
1010
  }
1011
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1012
  // Insert at new location
1013
- insertToParent(newParentKey, movedValue, insertPosition, insertKey);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1014
 
1015
  // Re-render and sync
1016
  renderEditor();
 
28
  const downloadBtn = document.getElementById('downloadBtn');
29
  const applyCodeBtn = document.getElementById('applyCodeBtn');
30
  const validationStatus = document.getElementById('validationStatus');
31
+ const lineNumbersLeft = document.getElementById('lineNumbersLeft');
32
+ const lineNumbersRight = document.getElementById('lineNumbersRight');
33
+ const coordLine = document.getElementById('coordLine');
34
+ const coordChar = document.getElementById('coordChar');
35
+ const coordDepth = document.getElementById('coordDepth');
36
 
37
  // Create saved indicator
38
  const savedIndicator = document.createElement('div');
 
59
  let editableFields = [];
60
  let currentFieldIndex = -1;
61
 
62
+ // Line number tracking
63
+ let currentLine = 0;
64
+ let lineElements = [];
65
+
66
  // Drag and drop state
67
  let draggedElement = null;
68
  let draggedData = null;
 
260
  // Render the JSON editor
261
  function renderEditor() {
262
  jsonEditor.innerHTML = '';
263
+ lineNumbersLeft.innerHTML = '';
264
+ lineNumbersRight.innerHTML = '';
265
  editableFields = [];
266
  currentFieldIndex = -1;
267
+ currentLine = 0;
268
+ lineElements = [];
269
  renderElement(jsonEditor, jsonData, 0, 'root');
270
+ updateLineNumbers();
271
+ }
272
+
273
+ // Update line numbers on both sides
274
+ function updateLineNumbers() {
275
+ const items = jsonEditor.querySelectorAll('.json-item');
276
+ const totalLines = items.length;
277
+
278
+ lineNumbersLeft.innerHTML = '';
279
+ lineNumbersRight.innerHTML = '';
280
+
281
+ for (let i = 0; i < totalLines; i++) {
282
+ const lineNum = document.createElement('div');
283
+ lineNum.className = 'h-5 leading-5';
284
+ lineNum.textContent = (i + 1).toString();
285
+
286
+ const lineNumRight = lineNum.cloneNode(true);
287
+
288
+ lineNumbersLeft.appendChild(lineNum);
289
+ lineNumbersRight.appendChild(lineNumRight);
290
+ }
291
  }
292
 
293
  // Render a single JSON element (COMPLETE VERSION)
 
304
  wrapper.dataset.depth = depth;
305
  wrapper.dataset.layer = Math.min(depth, 7);
306
  wrapper.dataset.type = Array.isArray(data) ? 'array' : 'object';
307
+ wrapper.dataset.line = currentLine;
308
+ lineElements.push(wrapper);
309
+ currentLine++;
310
 
311
  // Make primitive items draggable (not opening/closing brackets)
312
  if (key !== 'root' && key !== 'closing') {
 
442
  wrapper.dataset.depth = depth;
443
  wrapper.dataset.layer = Math.min(depth, 7);
444
  wrapper.dataset.type = 'primitive';
445
+ wrapper.dataset.line = currentLine;
446
+ lineElements.push(wrapper);
447
+ currentLine++;
448
 
449
  // Make draggable
450
  if (key !== 'root') {
 
781
  }
782
  }
783
 
784
+ // Sync line numbers scroll with editor
785
+ jsonEditor.parentElement.addEventListener('scroll', function() {
786
+ lineNumbersLeft.scrollTop = this.scrollTop;
787
+ lineNumbersRight.scrollTop = this.scrollTop;
788
+ });
789
+
790
  // Apply changes from code editor to visual editor
791
  function applyCodeChanges(showMsg = true) {
792
  if (!jsonOutput.value.trim()) return false;
 
1014
 
1015
  // Handle the drop operation
1016
  function handleDrop(draggedData, dropTarget, dropPosition) {
1017
+ const { key: draggedKey, parentKey: draggedParentKey, depth: draggedDepth } = draggedData;
1018
  const dropKey = dropTarget.dataset.key;
1019
  const dropParentKey = dropTarget.dataset.parent;
1020
  const dropDepth = parseInt(dropTarget.dataset.depth);
1021
  const dropType = dropTarget.dataset.type;
1022
 
1023
+ // Don't allow dropping on self
1024
+ if (draggedKey === dropKey && draggedParentKey === dropParentKey) {
1025
+ return;
1026
+ }
1027
 
1028
+ // Get the value being moved BEFORE removing
1029
+ const draggedParent = findElementByKey(jsonData, draggedParentKey);
1030
+ let movedValue = null;
1031
+
1032
+ if (draggedParent && typeof draggedParent === 'object') {
1033
+ if (Array.isArray(draggedParent)) {
1034
+ const index = parseInt(draggedKey);
1035
+ if (!isNaN(index) && index >= 0 && index < draggedParent.length) {
1036
+ movedValue = draggedParent[index];
1037
+ }
1038
+ } else {
1039
+ movedValue = draggedParent[draggedKey];
1040
+ }
1041
+ }
1042
+
1043
+ if (movedValue === null) {
1044
+ showNotification('Could not move item - value not found', true);
1045
+ return;
1046
+ }
1047
 
1048
  // Determine new parent and position
1049
  let newParentKey = dropParentKey;
 
1051
  let insertKey = dropKey;
1052
 
1053
  if (dropPosition === 'above') {
 
1054
  insertPosition = 'before';
1055
  newParentKey = dropParentKey;
1056
  insertKey = dropKey;
1057
  } else if (dropPosition === 'below') {
 
1058
  insertPosition = 'after';
1059
  newParentKey = dropParentKey;
1060
  insertKey = dropKey;
1061
  } else if (dropPosition === 'inside') {
 
1062
  newParentKey = dropKey;
1063
  insertPosition = 'append';
1064
  insertKey = null;
 
1071
  }
1072
  }
1073
 
1074
+ // Get the new parent object
1075
+ const newParent = findElementByKey(jsonData, newParentKey);
1076
+
1077
+ if (!newParent || typeof newParent !== 'object') {
1078
+ showNotification('Cannot move here - invalid parent', true);
1079
+ return;
1080
+ }
1081
+
1082
+ // Remove from original location
1083
+ if (draggedParent === 'root') {
1084
+ if (Array.isArray(jsonData)) {
1085
+ const index = parseInt(draggedKey);
1086
+ if (!isNaN(index) && index >= 0 && index < jsonData.length) {
1087
+ jsonData.splice(index, 1);
1088
+ }
1089
+ } else {
1090
+ delete jsonData[draggedKey];
1091
+ }
1092
+ } else {
1093
+ if (draggedParent && Array.isArray(draggedParent)) {
1094
+ const index = parseInt(draggedKey);
1095
+ if (!isNaN(index) && index >= 0 && index < draggedParent.length) {
1096
+ draggedParent.splice(index, 1);
1097
+ }
1098
+ } else if (draggedParent) {
1099
+ delete draggedParent[draggedKey];
1100
+ }
1101
+ }
1102
+
1103
  // Insert at new location
1104
+ if (Array.isArray(newParent)) {
1105
+ if (insertPosition === 'append') {
1106
+ newParent.push(movedValue);
1107
+ } else if (insertPosition === 'before' || insertPosition === 'after') {
1108
+ const index = parseInt(insertKey);
1109
+ if (!isNaN(index) && index >= 0) {
1110
+ const insertIndex = insertPosition === 'after' ? index + 1 : index;
1111
+ newParent.splice(insertIndex, 0, movedValue);
1112
+ } else {
1113
+ newParent.push(movedValue);
1114
+ }
1115
+ }
1116
+ } else {
1117
+ // Object - need a key for the new value
1118
+ let newKey = 'newField';
1119
+ let counter = 1;
1120
+ while (newParent[newKey] !== undefined) {
1121
+ newKey = `newField${counter++}`;
1122
+ }
1123
+
1124
+ if (insertPosition === 'append') {
1125
+ newParent[newKey] = movedValue;
1126
+ } else if (insertPosition === 'before' || insertPosition === 'after') {
1127
+ const keys = Object.keys(newParent);
1128
+ const index = keys.indexOf(insertKey);
1129
+
1130
+ if (index === -1) {
1131
+ // Key not found, just append
1132
+ newParent[newKey] = movedValue;
1133
+ } else {
1134
+ // Rebuild object with new item in correct position
1135
+ const newObj = {};
1136
+ keys.forEach((k, i) => {
1137
+ if (insertPosition === 'before' && i === index) {
1138
+ newObj[newKey] = movedValue;
1139
+ }
1140
+ newObj[k] = newParent[k];
1141
+ if (insertPosition === 'after' && i === index) {
1142
+ newObj[newKey] = movedValue;
1143
+ }
1144
+ });
1145
+
1146
+ // Clear old object and assign new one
1147
+ Object.keys(newParent).forEach(k => delete newParent[k]);
1148
+ Object.assign(newParent, newObj);
1149
+ }
1150
+ }
1151
+ }
1152
 
1153
  // Re-render and sync
1154
  renderEditor();
style.css CHANGED
@@ -394,4 +394,35 @@ p {
394
  .drag-handle:active {
395
  cursor: grabbing;
396
  color: rgba(255, 255, 255, 0.6);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  }
 
394
  .drag-handle:active {
395
  cursor: grabbing;
396
  color: rgba(255, 255, 255, 0.6);
397
+ }
398
+
399
+ /* Line numbers */
400
+ #lineNumbersLeft,
401
+ #lineNumbersRight {
402
+ border-top: 4px solid #334155;
403
+ border-bottom: 4px solid #334155;
404
+ }
405
+
406
+ #lineNumbersLeft {
407
+ border-right: 1px solid #475569;
408
+ }
409
+
410
+ #lineNumbersRight {
411
+ border-left: 1px solid #475569;
412
+ }
413
+
414
+ /* Coordinates display */
415
+ #coordinates {
416
+ border: 1px solid #475569;
417
+ min-width: 150px;
418
+ text-align: center;
419
+ }
420
+
421
+ #coordinates span {
422
+ color: #94a3b8;
423
+ }
424
+
425
+ #coordinates span::before {
426
+ color: #60a5fa;
427
+ margin-right: 2px;
428
  }