Skip to content

Commit

Permalink
feat: json syntax highlighting
Browse files Browse the repository at this point in the history
  • Loading branch information
1weiho committed Feb 15, 2025
1 parent 449a41f commit 42f3e89
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 7 deletions.
49 changes: 49 additions & 0 deletions packages/next-sandbox/src/highlighted-json.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';

function syntaxHighlightReact(jsonStr: string) {
if (!jsonStr) return null;

const regex =
/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g;
let lastIndex = 0;
const tokens = [];
let match;
let key = 0;

while ((match = regex.exec(jsonStr)) !== null) {
if (match.index > lastIndex) {
tokens.push(jsonStr.slice(lastIndex, match.index));
}
const token = match[0];
let cls = 'number';
if (/^"/.test(token)) {
cls = /:$/.test(token) ? 'key' : 'string';
} else if (/true|false/.test(token)) {
cls = 'boolean';
} else if (/null/.test(token)) {
cls = 'null';
}
tokens.push(
<span key={key++} className={cls}>
{token}
</span>,
);
lastIndex = regex.lastIndex;
}

if (lastIndex < jsonStr.length) {
tokens.push(jsonStr.slice(lastIndex));
}

return tokens;
}

interface HighlightedJSONProps {
jsonStr: string;
}

export default function HighlightedJSON({ jsonStr }: HighlightedJSONProps) {
const tokens = syntaxHighlightReact(jsonStr);

return <pre className="log-content">{tokens}</pre>;
}
14 changes: 8 additions & 6 deletions packages/next-sandbox/src/log-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React from 'react';
import { useSandbox } from './sandbox-context';
import LoaderIcon from './icons/loader-icon';
import PlayIcon from './icons/play-icon';
import HighlightedJSON from './highlighted-json';

interface LogDrawerProps {
open: boolean;
Expand Down Expand Up @@ -56,12 +57,13 @@ export default function LogDrawer({
) : (
[...currentRecords].reverse().map((record, idx) => (
<div key={idx} className="log-item">
<pre
data-error-message={record.status === 'error'}
className="log-content"
>
{record.output}
</pre>
{record.status === 'success' ? (
<HighlightedJSON jsonStr={record.output} />
) : (
<pre className="log-content log-error">
{record.output}
</pre>
)}
<div className="log-meta">
<p className="log-time">
{new Date(record.timestamp).toLocaleTimeString() ??
Expand Down
18 changes: 17 additions & 1 deletion packages/next-sandbox/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,23 @@
overflow-x: auto;
}

.log-content[data-error-message='true'] {
.log-content .string {
color: var(--color-success);
}
.log-content .number {
color: darkorange;
}
.log-content .boolean {
color: var(--color-blue);
}
.log-content .null {
color: rgb(195, 120, 215);
}
.log-content .key {
color: var(--color-danger);
}

.log-error {
color: var(--color-danger);
}

Expand Down

0 comments on commit 42f3e89

Please sign in to comment.