Skip to content

Commit e8eefaf

Browse files
Sander-ToonenSander Toonen
andauthored
Redesign playground, prettify JSON result (#28)
* Rearrange playground layout * Prettify json --------- Co-authored-by: Sander Toonen <s.toonen@pro-fa.com>
1 parent c60071b commit e8eefaf

3 files changed

Lines changed: 202 additions & 78 deletions

File tree

samples/language-service-sample/app.js

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,12 @@ function loadExampleFromUrl() {
161161
return false;
162162
}
163163

164-
// Split pane resizing
164+
// Vertical resizing for bottom split (context/results)
165165
(function() {
166-
const resizer = document.getElementById('resizer');
167-
const leftPane = document.getElementById('leftPane');
168-
const rightPane = document.getElementById('rightPane');
169-
const mainContent = document.getElementById('mainContent');
166+
const resizer = document.getElementById('verticalResizer');
167+
const contextPane = document.getElementById('contextPane');
168+
const resultsPane = document.getElementById('resultsPane');
169+
const bottomArea = document.getElementById('bottomArea');
170170
let isResizing = false;
171171

172172
resizer.addEventListener('mousedown', (e) => {
@@ -180,15 +180,15 @@ function loadExampleFromUrl() {
180180
if (!isResizing) return;
181181
e.preventDefault();
182182

183-
const containerRect = mainContent.getBoundingClientRect();
183+
const containerRect = bottomArea.getBoundingClientRect();
184184
const containerWidth = containerRect.width;
185185
const resizerWidth = 6;
186186

187187
let newLeftWidth = e.clientX - containerRect.left;
188-
newLeftWidth = Math.max(containerWidth * 0.15, Math.min(containerWidth * 0.85, newLeftWidth));
188+
newLeftWidth = Math.max(containerWidth * 0.2, Math.min(containerWidth * 0.8, newLeftWidth));
189189

190-
leftPane.style.width = newLeftWidth + 'px';
191-
rightPane.style.width = (containerWidth - newLeftWidth - resizerWidth) + 'px';
190+
const percentage = (newLeftWidth / containerWidth) * 100;
191+
contextPane.style.width = percentage + '%';
192192
});
193193

194194
document.addEventListener('mouseup', () => {
@@ -417,6 +417,24 @@ require(['vs/editor/editor.main'], function () {
417417
highlightDecorations = expressionEditor.deltaDecorations(highlightDecorations, decorations);
418418
}
419419

420+
// Syntax highlight JSON
421+
function syntaxHighlightJson(json) {
422+
if (typeof json !== 'string') {
423+
json = JSON.stringify(json, null, 2);
424+
}
425+
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
426+
return json.replace(/("+[^"]*"+)(:)?/g, function(match, p1, p2) {
427+
let cls = 'json-key';
428+
if (!p2) {
429+
cls = 'json-string';
430+
}
431+
return '<span class="' + cls + '">' + p1 + '</span>' + (p2 || '');
432+
})
433+
.replace(/: (true|false)/g, ': <span class="json-boolean">$1</span>')
434+
.replace(/: (null)/g, ': <span class="json-null">$1</span>')
435+
.replace(/: (-?\d+\.?\d*)/g, ': <span class="json-number">$1</span>');
436+
}
437+
420438
// Result display functions
421439
function showResult(result) {
422440
const resultSuccess = document.getElementById('resultSuccess');
@@ -432,21 +450,42 @@ require(['vs/editor/editor.main'], function () {
432450
// Format the result
433451
let displayValue;
434452
let typeInfo;
453+
let isJson = false;
454+
435455
if (result === null) {
436-
displayValue = 'null';
456+
displayValue = '<span class="json-null">null</span>';
437457
typeInfo = 'Type: null';
458+
isJson = true;
438459
} else if (result === undefined) {
439-
displayValue = 'undefined';
460+
displayValue = '<span class="json-null">undefined</span>';
440461
typeInfo = 'Type: undefined';
462+
isJson = true;
441463
} else if (typeof result === 'object') {
442-
displayValue = JSON.stringify(result, null, 2);
464+
displayValue = syntaxHighlightJson(result);
443465
typeInfo = Array.isArray(result) ? `Type: array (${result.length} items)` : 'Type: object';
466+
isJson = true;
467+
} else if (typeof result === 'boolean') {
468+
displayValue = `<span class="json-boolean">${result}</span>`;
469+
typeInfo = 'Type: boolean';
470+
isJson = true;
471+
} else if (typeof result === 'number') {
472+
displayValue = `<span class="json-number">${result}</span>`;
473+
typeInfo = 'Type: number';
474+
isJson = true;
475+
} else if (typeof result === 'string') {
476+
displayValue = `<span class="json-string">"${result}"</span>`;
477+
typeInfo = 'Type: string';
478+
isJson = true;
444479
} else {
445480
displayValue = String(result);
446481
typeInfo = `Type: ${typeof result}`;
447482
}
448483

449-
resultValue.textContent = displayValue;
484+
if (isJson) {
485+
resultValue.innerHTML = displayValue;
486+
} else {
487+
resultValue.textContent = displayValue;
488+
}
450489
resultType.textContent = typeInfo;
451490
}
452491

@@ -456,15 +495,17 @@ require(['vs/editor/editor.main'], function () {
456495
const resultEmpty = document.getElementById('resultEmpty');
457496
const errorMessage = document.getElementById('errorMessage');
458497
const errorDetails = document.getElementById('errorDetails');
459-
const footer = document.getElementById('footer');
498+
const resultsPane = document.getElementById('resultsPane');
460499

461500
resultSuccess.classList.add('hidden');
462501
resultError.classList.remove('hidden');
463502
resultEmpty.classList.add('hidden');
464503

465504
// Shake animation
466-
footer.classList.add('error-shake');
467-
setTimeout(() => footer.classList.remove('error-shake'), 300);
505+
if (resultsPane) {
506+
resultsPane.classList.add('error-shake');
507+
setTimeout(() => resultsPane.classList.remove('error-shake'), 300);
508+
}
468509

469510
errorMessage.textContent = error.message;
470511

samples/language-service-sample/index.html

Lines changed: 60 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -66,73 +66,77 @@ <h1 class="text-xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 dark
6666
</div>
6767
</aside>
6868

69-
<!-- Expression Editor Pane -->
70-
<div id="leftPane" class="pane bg-white dark:bg-[#1e1e1e]" style="width: calc(60% - var(--sidebar-width));">
71-
<div class="pane-header h-10 bg-gray-100 dark:bg-[#252526] border-b border-gray-200 dark:border-[#3c3c3c] flex items-center px-4">
72-
<span class="text-sm font-medium text-gray-600 dark:text-[#cccccc]">Expression</span>
73-
<span class="ml-2 text-xs text-gray-400 dark:text-[#808080]">Enter your expr-eval expression</span>
74-
</div>
75-
<div class="editor-wrapper">
76-
<div id="expressionEditor"></div>
69+
<!-- Main Work Area -->
70+
<div id="workArea" class="flex-1 flex flex-col">
71+
<!-- Expression Editor -->
72+
<div id="expressionPane" class="pane bg-white dark:bg-[#1e1e1e]">
73+
<div class="pane-header h-10 bg-gray-100 dark:bg-[#252526] border-b border-gray-200 dark:border-[#3c3c3c] flex items-center px-4">
74+
<span class="text-sm font-medium text-gray-600 dark:text-[#cccccc]">Expression</span>
75+
<span class="ml-2 text-xs text-gray-400 dark:text-[#808080]">Enter your expr-eval expression</span>
76+
</div>
77+
<div class="editor-wrapper">
78+
<div id="expressionEditor"></div>
79+
</div>
7780
</div>
78-
</div>
7981

80-
<!-- Resizer -->
81-
<div id="resizer" class="bg-gray-200 dark:bg-[#3c3c3c]"></div>
82+
<!-- Bottom Split Area -->
83+
<div id="bottomArea" class="flex-1 flex">
84+
<!-- Context Editor Pane -->
85+
<div id="contextPane" class="pane bg-white dark:bg-[#1e1e1e] border-t border-gray-200 dark:border-[#3c3c3c]">
86+
<div class="pane-header h-10 bg-gray-100 dark:bg-[#252526] border-b border-gray-200 dark:border-[#3c3c3c] flex items-center px-4">
87+
<span class="text-sm font-medium text-gray-600 dark:text-[#cccccc]">Context (JSON)</span>
88+
<span class="ml-2 text-xs text-gray-400 dark:text-[#808080]">Variables available in expressions</span>
89+
</div>
90+
<div class="editor-wrapper">
91+
<div id="contextEditor"></div>
92+
</div>
93+
</div>
8294

83-
<!-- Context Editor Pane -->
84-
<div id="rightPane" class="pane bg-white dark:bg-[#1e1e1e]" style="width: calc(40% - 6px);">
85-
<div class="pane-header h-10 bg-gray-100 dark:bg-[#252526] border-b border-gray-200 dark:border-[#3c3c3c] flex items-center px-4">
86-
<span class="text-sm font-medium text-gray-600 dark:text-[#cccccc]">Context (JSON)</span>
87-
<span class="ml-2 text-xs text-gray-400 dark:text-[#808080]">Variables available in expressions</span>
88-
</div>
89-
<div class="editor-wrapper">
90-
<div id="contextEditor"></div>
91-
</div>
92-
</div>
93-
</main>
95+
<!-- Vertical Resizer -->
96+
<div id="verticalResizer" class="bg-gray-200 dark:bg-[#3c3c3c]"></div>
9497

95-
<!-- Footer / Results -->
96-
<footer id="footer" class="bg-white dark:bg-[#1e1e1e] border-t border-gray-200 dark:border-[#3c3c3c]">
97-
<div class="h-full flex flex-col">
98-
<div class="h-8 bg-gray-100 dark:bg-[#252526] flex items-center px-4 border-b border-gray-200 dark:border-[#3c3c3c] flex-shrink-0">
99-
<span class="text-sm font-medium text-gray-600 dark:text-[#cccccc]">Result</span>
100-
</div>
101-
<div id="resultContainer" class="flex-1 p-4 overflow-auto">
102-
<div id="resultSuccess" class="hidden">
103-
<div class="flex items-start gap-3">
104-
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-green-100 dark:bg-[#2d4a3e] flex items-center justify-center">
105-
<svg class="w-5 h-5 text-green-600 dark:text-[#4ec9b0]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
106-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
107-
</svg>
108-
</div>
109-
<div class="flex-1">
110-
<p class="text-sm text-gray-500 dark:text-[#808080]">Evaluation Result</p>
111-
<p id="resultValue" class="text-2xl font-mono font-bold text-gray-900 dark:text-[#d4d4d4] mt-1"></p>
112-
<p id="resultType" class="text-xs text-gray-400 dark:text-[#808080] mt-1"></p>
113-
</div>
98+
<!-- Results Pane -->
99+
<div id="resultsPane" class="pane bg-white dark:bg-[#1e1e1e] border-t border-gray-200 dark:border-[#3c3c3c]">
100+
<div class="pane-header h-10 bg-gray-100 dark:bg-[#252526] border-b border-gray-200 dark:border-[#3c3c3c] flex items-center px-4">
101+
<span class="text-sm font-medium text-gray-600 dark:text-[#cccccc]">Result</span>
114102
</div>
115-
</div>
116-
<div id="resultError" class="hidden">
117-
<div class="flex items-start gap-3">
118-
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-100 dark:bg-[#4a2d2d] flex items-center justify-center">
119-
<svg class="w-5 h-5 text-red-600 dark:text-[#f48771]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
120-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
121-
</svg>
103+
<div id="resultContainer" class="flex-1 p-4 overflow-auto">
104+
<div id="resultSuccess" class="hidden">
105+
<div class="flex items-start gap-3">
106+
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-green-100 dark:bg-[#2d4a3e] flex items-center justify-center">
107+
<svg class="w-5 h-5 text-green-600 dark:text-[#4ec9b0]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
108+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
109+
</svg>
110+
</div>
111+
<div class="flex-1">
112+
<p class="text-sm text-gray-500 dark:text-[#808080]">Evaluation Result</p>
113+
<p id="resultValue" class="text-2xl font-mono font-bold text-gray-900 dark:text-[#d4d4d4] mt-1"></p>
114+
<p id="resultType" class="text-xs text-gray-400 dark:text-[#808080] mt-1"></p>
115+
</div>
116+
</div>
117+
</div>
118+
<div id="resultError" class="hidden">
119+
<div class="flex items-start gap-3">
120+
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-100 dark:bg-[#4a2d2d] flex items-center justify-center">
121+
<svg class="w-5 h-5 text-red-600 dark:text-[#f48771]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
122+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
123+
</svg>
124+
</div>
125+
<div class="flex-1">
126+
<p class="text-sm font-medium text-red-600 dark:text-[#f48771]">Error</p>
127+
<p id="errorMessage" class="text-base font-mono text-red-700 dark:text-[#f48771] mt-1 bg-red-50 dark:bg-[#3a2222] p-3 rounded-lg border border-red-200 dark:border-[#5a3c3c]"></p>
128+
<div id="errorDetails" class="mt-3 text-sm text-gray-600 dark:text-[#cccccc] space-y-2"></div>
129+
</div>
130+
</div>
122131
</div>
123-
<div class="flex-1">
124-
<p class="text-sm font-medium text-red-600 dark:text-[#f48771]">Error</p>
125-
<p id="errorMessage" class="text-base font-mono text-red-700 dark:text-[#f48771] mt-1 bg-red-50 dark:bg-[#3a2222] p-3 rounded-lg border border-red-200 dark:border-[#5a3c3c]"></p>
126-
<div id="errorDetails" class="mt-3 text-sm text-gray-600 dark:text-[#cccccc] space-y-2"></div>
132+
<div id="resultEmpty" class="flex items-center justify-center h-full text-gray-400 dark:text-[#808080]">
133+
<p>Enter an expression to see results</p>
127134
</div>
128135
</div>
129136
</div>
130-
<div id="resultEmpty" class="flex items-center justify-center h-full text-gray-400 dark:text-[#808080]">
131-
<p>Enter an expression to see results</p>
132-
</div>
133137
</div>
134138
</div>
135-
</footer>
139+
</main>
136140
</div>
137141

138142
<!-- Save Toast Notification -->

samples/language-service-sample/styles.css

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,35 @@ html, body {
2222
display: flex;
2323
}
2424

25+
/* Work area layout */
26+
#workArea {
27+
flex: 1;
28+
display: flex;
29+
flex-direction: column;
30+
min-width: 0;
31+
}
32+
33+
#expressionPane {
34+
height: 300px;
35+
flex-shrink: 0;
36+
}
37+
38+
#bottomArea {
39+
flex: 1;
40+
min-height: 0;
41+
display: flex;
42+
}
43+
44+
#contextPane {
45+
width: 50%;
46+
flex-shrink: 0;
47+
}
48+
49+
#resultsPane {
50+
flex: 1;
51+
min-width: 0;
52+
}
53+
2554
/* Split pane styles */
2655
.pane {
2756
display: flex;
@@ -47,18 +76,19 @@ html, body {
4776
bottom: 0;
4877
}
4978

50-
#resizer {
79+
/* Vertical resizer */
80+
#verticalResizer {
5181
width: 6px;
5282
cursor: col-resize;
5383
flex-shrink: 0;
5484
position: relative;
5585
}
5686

57-
#resizer:hover {
87+
#verticalResizer:hover {
5888
background-color: #6366f1 !important;
5989
}
6090

61-
#resizer::after {
91+
#verticalResizer::after {
6292
content: '';
6393
position: absolute;
6494
top: 50%;
@@ -70,9 +100,58 @@ html, body {
70100
border-radius: 2px;
71101
}
72102

73-
#footer {
74-
flex-shrink: 0;
75-
height: 300px;
103+
#resultContainer {
104+
flex: 1;
105+
overflow-y: auto;
106+
}
107+
108+
/* JSON syntax highlighting */
109+
#resultValue {
110+
white-space: pre-wrap;
111+
word-break: break-word;
112+
}
113+
114+
.json-key {
115+
color: #0451a5;
116+
font-weight: 500;
117+
}
118+
119+
.json-string {
120+
color: #a31515;
121+
}
122+
123+
.json-number {
124+
color: #098658;
125+
}
126+
127+
.json-boolean {
128+
color: #0000ff;
129+
font-weight: 600;
130+
}
131+
132+
.json-null {
133+
color: #0000ff;
134+
font-weight: 600;
135+
}
136+
137+
.dark .json-key {
138+
color: #9cdcfe;
139+
}
140+
141+
.dark .json-string {
142+
color: #ce9178;
143+
}
144+
145+
.dark .json-number {
146+
color: #b5cea8;
147+
}
148+
149+
.dark .json-boolean {
150+
color: #569cd6;
151+
}
152+
153+
.dark .json-null {
154+
color: #569cd6;
76155
}
77156

78157
/* Light theme token colors */

0 commit comments

Comments
 (0)